Опубликован: 05.08.2010 | Уровень: специалист | Доступ: свободно
Лекция 1:

Основы привязки данных в WPF

Подключение документа через ресурс

Теперь ту же задачу подключения документа к контейнеру <RichTextBox> быстренько повторим с использованием механизма ресурсов, чтобы вспомнить молодость. При этом нам не придется писать ни строчки процедурного кода, все выполним в разметке. Чтобы не вмешиваться в процедурный код, просто удалим регистрацию обработчика события Loaded из разметки элемента RichTextBox. Пусть обработчик существует как обычный незадействованный метод класса Window1 и никуда не встревает со своими услугами. А компилятор тоже будет молчать.

Было
<RichTextBox Name="documentReader" Loaded="documentReader_Loaded"
                                 Margin="0,5,0,0"
                                 ScrollViewer.VerticalScrollBarVisibility="Auto">
                    </RichTextBox>
Стало
<RichTextBox Name="documentReader" 
                                 Margin="0,5,0,0"
                                 ScrollViewer.VerticalScrollBarVisibility="Auto">
                    </RichTextBox>

Имя удалять не будем, потому что на него ссылается код обработчика. Если хотим убрать и имя, тогда нужно очистить или вообще удалить обработчик documentReader_Loaded() из класса Window1 (кому как, а лично мне это делать жалко, код-то вымучен бессонными ночами и недопитыми литрами. Хотя с допитыми может быть получилось бы лучше).

  • В панели Solution Explorer выделите корневой узел текущего проекта и через меню оболочки командой Project/Add Resource Dictionary добавьте новый файл с именем по умолчанию Dictionary1.xaml


  • Выделите в Solution Explore новый файл Dictionary1.xaml и проверьте через панель Properties его свойства, чтобы они были такими

  • Build Action = Page
  • Copy to Output Directory=Do not copy
  • Скопируйте из файла Instruct.xaml в новый файл Dictionary1.xaml все содержимое с разметкой документа <FlowDocument> и обязательно добавьте в его открывающем дескрипторе ключ ресурса x:Key="instruct". При этом можете удалить, а можете и оставить, индивидуальное подключение пространств имен XAML, которое в дескрипторе ресурса уже присутствует (можете удалить любое из них или ничего не удалять)

Файл Dictionary1.xaml должен стать таким (текст неважен, важна разметка)

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    >
    <FlowDocument x:Key="instruct" FontSize="11" Background="White"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        >
        <List MarkerStyle="Decimal">
            <ListItem>
                <Paragraph>
                    Первый флажок двунаправленно привязан булевым свойством 
                    IsChecked к вложенному логическому свойству Selector.IsSelected, 
                    которое инициализируется один раз и больше не меняется. 
                    Поэтому флажок и элемент списка меняются независимо.
                    Режим поиска родителя определен значением Mode=Self - 
                    искать источник в том же элементе.
                </Paragraph>
            </ListItem>
            <ListItem>
                <Paragraph>
                    Второй флажок своим свойством IsChecked привязан двунаправленной 
                    привязкой к свойству IsSelected своего ближайшего контейнера,
                    которым является элемент списка ListBoxItem, 
                    поэтому и меняется синхронно с ним, 
                    обеспечивая нормальную логику работы списка.
                    Режим поиска родителя определен значением Mode=FindAncestor - 
                    искать источник в ближайшем родителе.
                </Paragraph>
            </ListItem>
            <ListItem>
                <Paragraph>
                    В третьем элементе списка флажок и элемент списка ListBoxItem 
                    никак не связаны, поэтому изменяются автономно как в первом элементе, 
                    где связь хоть и установлена, но источник Selector.IsSelected 
                    остается постоянным
                </Paragraph>
            </ListItem>
            <ListItem>
                <Paragraph>
                    Четвертый элемент списка задается явно и флажок своим свойством 
                    IsChecked привязан двунаправленной привязкой к свойству 
                    IsSelected этого контейнера. Поэтому флажок и элемент списка 
                    работают синхронно, как и в случае со вторым списком.
                </Paragraph>
            </ListItem>
        </List>
    </FlowDocument>
</ResourceDictionary>
  • Дополните разметку элемента RichTextBox во вкладке Page11 следующей конструкцией подключения ресурса по синтаксису элемента свойства

<RichTextBox Name="documentReader"
                                 Margin="0,5,0,0"
                                 ScrollViewer.VerticalScrollBarVisibility="Auto">
                        <RichTextBox.Resources>
                            <ResourceDictionary>
                                <ResourceDictionary.MergedDictionaries>
                                    <ResourceDictionary Source="Dictionary1.xaml" />
                                </ResourceDictionary.MergedDictionaries>
                            </ResourceDictionary>
                        </RichTextBox.Resources>
	
                        <RichTextBox.Document>
                            <StaticResource ResourceKey="instruct" />                        
                        </RichTextBox.Document>
                   </RichTextBox>
  • Запустите приложение - вкладка Page11 должна выглядеть точно так, как и в предыдущем случае подключения документа через процедурный код

Обращает на себя внимание применение элементов ListBox (ListBoxItem и List (ListItem). Оба называются списками. Но первый из них относится к элементам управления, поскольку служит для отображения информации и обеспечивает взаимодействие с пользователем. А второй - просто к элементам (хотя это и более общая категория), поскольку незримо участвует только в верстке документа. Да ладно, в конце-концов, - это касается только терминологии. Главное - не как называть, а правильно понимать и применять.

Вкладка Page12. Привязка RelativeSource в процедурном коде

Возьмем за основу привязку RelativeSource в разметке, а потом перенесем ее в процедурный код.

  • Добавьте в контейнер TabControl файла Window1.xaml новую вкладку с именем Page12, которую заполните следующей начальной разметкой, использующей привязку RelativeSource

<!-- Привязка RelativeSource в коде --<
            <TabItem Header="Page12"<
                <!-- Привязка Button.Content к StackPanel уровней 1 и 2 --<
                <StackPanel Tag="Эта информация находится в StackPanel уровня 2"<
                    <StackPanel Tag="Эта информация находится в StackPanel уровня 1"<
                        <Button Margin="5"
                            Content="{Binding Path=Tag,
                                              RelativeSource={RelativeSource 
                                                  Mode=FindAncestor,
                                                  AncestorType=StackPanel, 
                                                  AncestorLevel=1},
                                              Mode=OneWay}" /<
                       <Button Margin="5"
                            Content="{Binding Path=Tag,
                                              RelativeSource={RelativeSource 
                                                  Mode=FindAncestor,
                                                  AncestorType=StackPanel,
                                                  AncestorLevel=2},
                                              Mode=OneWay}" /<
                    </StackPanel<
                    <!-- Привязка TextBlock.Text к Window.Title --<
                    <Label<
                        Следующая информация находится в Window:
                    </Label<
                    <TextBlock Margin="5,0"<
                        <TextBlock.Text<
                            <Binding Path="Title" Mode="OneWay"<
                                <Binding.RelativeSource<
                                    <RelativeSource Mode="FindAncestor"
                                                    AncestorType="Window" /<
                                </Binding.RelativeSource<
                            </Binding<
                        </TextBlock.Text<
                    </TextBlock<
                </StackPanel<
            </TabItem<
  • Запустите приложение - вывод вкладки Page12 будет таким


  • Удалите привязку RelativeSource из элементов вкладки Page12 и присвойте привязываемым источникам имена для возможности адресации из процедурного кода

Окончательный вид вкладки Page12 будет таким

<!-- Привязка RelativeSource в коде -->
            <TabItem Header="Page12" Selector.IsSelected="True">
                <!-- Привязка Button.Content к StackPanel уровней 1 и 2 -->
                <StackPanel Tag="Эта информация находится в StackPanel уровня 2">
                    <StackPanel Tag="Эта информация находится в StackPanel уровня 1">
                        <Button Margin="5" Name="button12level1" />
                        <Button Margin="5" Name="button12level2" />
                    </StackPanel>
                    <!-- Привязка TextBlock.Text к Window.Title -->
                    <Label>
                        Следующая информация находится в Window:
                    </Label>
                    <TextBlock Margin="5,0" Name="textBlock12" />
                </StackPanel>
            </TabItem>
  • Добавьте в конструктор класса Window1 файла Window1.xaml.cs следующий процедурный код привязки RelativeSource

public Window1()
        {
            InitializeComponent();
    
            //********************************************************
            // Привязка RelativeSource для элементов вкладки Page12
            //********************************************************
            //
            // button12level1
            //
            Binding binding = new Binding();
            binding.Path = new PropertyPath("Tag");
            binding.Mode = BindingMode.OneWay;
            binding.RelativeSource = new RelativeSource(
                RelativeSourceMode.FindAncestor, typeof(StackPanel), 1);
            BindingOperations.SetBinding(button12level1, Button.ContentProperty, binding);
            //
            // button12level2
            //
            binding = new Binding("Tag");
            binding.Mode = BindingMode.OneWay;
            binding.RelativeSource = new RelativeSource(
                RelativeSourceMode.FindAncestor, typeof(StackPanel), 2);
            BindingOperations.SetBinding(button12level2, Button.ContentProperty, binding);
            //
            // textBlock12
            //
            binding = new Binding();
            binding.Path = new PropertyPath("Title");
            binding.Mode = BindingMode.OneWay;
            binding.RelativeSource = new RelativeSource(
                RelativeSourceMode.FindAncestor, typeof(Window), 1);
            BindingOperations.SetBinding(textBlock12, TextBlock.TextProperty, binding);
        }
  • Запустите приложение - вывод вкладки Page12 остался в точности таким, как и с привязкой в разметк

Пока об основах привязки поговорили достаточно. Но это еще далеко не все. В следующих разделах мы рассмотрим вопросы более серьезного применения привязки: привязки WPF к объектам ADO.NET, привязки в шаблонах и т.д.


Алексей Бабушкин
Алексей Бабушкин

При выполнении в лабораторной работе упражнения №1 , а именно при выполнении нижеследующего кода:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using Microsoft.Xna.Framework.Graphics;

   

namespace Application1

{

    public partial class MainForm : Form

    {

        // Объявим поле графического устройства для видимости в методах

        GraphicsDevice device;

   

        public MainForm()

        {

            InitializeComponent();

   

            // Подпишемся на событие Load формы

            this.Load += new EventHandler(MainForm_Load);

   

            // Попишемся на событие FormClosed формы

            this.FormClosed += new FormClosedEventHandler(MainForm_FormClosed);

        }

   

        void MainForm_FormClosed(object sender, FormClosedEventArgs e)

        {

            //  Удаляем (освобождаем) устройство

            device.Dispose();

            // На всякий случай присваиваем ссылке на устройство значение null

            device = null;       

        }

   

        void MainForm_Load(object sender, EventArgs e)

        {

            // Создаем объект представления для настройки графического устройства

            PresentationParameters presentParams = new PresentationParameters();

            // Настраиваем объект представления через его свойства

            presentParams.IsFullScreen = false; // Включаем оконный режим

            presentParams.BackBufferCount = 1;  // Включаем задний буфер

                                                // для двойной буферизации

            // Переключение переднего и заднего буферов

            // должно осуществляться с максимальной эффективностью

            presentParams.SwapEffect = SwapEffect.Discard;

            // Устанавливаем размеры заднего буфера по клиентской области окна формы

            presentParams.BackBufferWidth = this.ClientSize.Width;

            presentParams.BackBufferHeight = this.ClientSize.Height;

   

            // Создадим графическое устройство с заданными настройками

            device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware,

                this.Handle, presentParams);

        }

   

        protected override void OnPaint(PaintEventArgs e)

        {

            device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);

   

            base.OnPaint(e);

        }

    }

}

Выбрасывается исключение:

Невозможно загрузить файл или сборку "Microsoft.Xna.Framework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d" или один из зависимых от них компонентов. Не удается найти указанный файл.

Делаю все пунктуально. В чем может быть проблема?

Юрий Макушин
Юрий Макушин
Россия, Москва, РЭА им. Плеханова, 2004