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

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

Вкладка Page7. Присвоение свойству DataContext логической ссылки на ресурс

Вернемся к примеру вкладки Page2 и немного изменим его.

  • В панели Solution Explorer для папки Images вызовите контекстное меню и скопируйте в нее командой Add/Existing Item из прилагаемого каталога Source файл wood.jpg
  • Выделите файл wood.jpg и в панели Properties проверьте его настройки в оболочке. Они должны быть такими
    • Build Action=None
    • Copy to Output Directory=Copy if newer
  • В файле Window1.xaml.cs в класс Pictures добавьте новый код
class Pictures
    {
        // Поле
        public static ImageBrush picture = new ImageBrush();
        public static ImageBrush picture1 = new ImageBrush();
    
        static Pictures()
        {
            picture.ImageSource = new BitmapImage(
                new Uri(@"Images\flower2.jpg", UriKind.Relative));
            picture.Stretch = Stretch.UniformToFill;
            picture.Opacity = 1.0D;
    
            picture1.ImageSource = new BitmapImage(
                new Uri(@"Images\wood.jpg", UriKind.Relative));
            picture1.Stretch = Stretch.Uniform;
            picture1.Opacity = 1.0D;
        }
    
        // Свойство
        public static ImageBrush Picture { get { return picture; } }
        public static ImageBrush Picture1 { get { return picture1; } }
    }
  • В коллекцию ресурсов разметки окна Window1 добавьте новый ресурс
<Window.Resources> 
        <SolidColorBrush x:Key="ControlColorBrush" 
            Color="{x:Static SystemColors.ControlColor}" /> 
        <local:Pictures x:Key="wood" /> 
    </Window.Resources>

Вспомним, что при разработке вкладки Page2 мы уже добавили в заголовок дескриптора Window запись

xmlns:local="clr-namespace:ElementWithObject"

импортирования пространства имен приложения для доступа к свойству класса Pictures из разметки и теперь просто используем готовый псевдоним local.

  • В контейнер TabControl добавьте новую вкладку Page7 со следующей разметкой

<!-- Page7. Присвоение свойству DataContext 
                        логической ссылки на ресурс -->
            <TabItem Header="Page7"
                DataContext=" {StaticResource ResourceKey=wood}">
                <Button Background="{Binding Path=Picture1, Mode=OneWay}" />
            </TabItem>

Синтаксический анализатор ищет ресурс вначале в элементе его использования со свойством DataContext, а затем вверх по логическому дереву, поэтому мы расположили определение ресурса в словаре окна. То же самое присходит и с DataContext. Когда в выражении привязки атрибут Source пропущен, то WPF ищет определение источника в атрибуте DataContext вначале в элементе с выражением привязки и далее в родительских элементах, поэтому мы расположили DataContext в элементе <TabItem>.

  • Запустите приложение - результат вкладки Page7 выглядет так


Вкладка Page8. Управление свойством DataContext в процедурном коде

Обычно DataContext задается в коде. В частности, эта методика применяется к объектам ADO.NET, которые необходимо инициировать в коде и нельзя заполнять декларативно. Пока что без объектов ADO.NET рассмотрим этот способ. Естественно, что элемент, в котором будет задаваться свойство DataContext, должен иметь имя для возможности обращения из процедурного кода.

  • Добавьте в контейнер TabControl файла Window1.xaml новую вкладку с именем Page8

<!-- Page8. Управление свойством DataContext в процедурном коде -->
            <TabItem Header="Page8">
                <StackPanel Name="stackPanel8">
                    <TextBlock FontSize="16" TextDecorations="Underline"
                        TextAlignment="Center" Margin="0,5,0,15" Foreground="Red"
                        Text="Управление DataContext в коде" 
                        />
                    <StackPanel Orientation="Horizontal" Margin="5">
                        <Label Width="40"
                            Content="{Binding Path=Value, Mode=OneWay}" 
                            />
                        <Slider Name="slider8" Width="230" 
                            Minimum="11" Maximum="54"
                            TickFrequency="2" TickPlacement="BottomRight"
                            IsSnapToTickEnabled="True"
                            />
                    </StackPanel>
                    <Button 
                        Margin="5"
                        Content="Подключить источник в DataContext"
                        Click="button8_Click">
                    </Button>
                    <TextBlock Margin="0,10" 
                        Foreground="Blue"
                        FontSize="{Binding Path=Value, Mode=OneWay}"
                        Text="Simple Text"
                        />
                </StackPanel>
            </TabItem>
  • Расширьте класс Window1 следующим процедурным кодом

public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }
    
        bool bindingFlag = false;
        private void button8_Click(object sender, RoutedEventArgs e)
        {
            Button button = (Button)sender;
            if (bindingFlag == false)
            {
                stackPanel8.DataContext = slider8;
                button.Content = "Отключить источник в DataContext";
            }
            else
            {
                stackPanel8.DataContext = null;
                button.Content = "Подключить источник в DataContext";
            }
    
            bindingFlag = !bindingFlag;
        }
    }
  • Запустите приложение и испытайте работу вкладки Page8, внешний вид которой будет таким


Вкладка Page9. Создание и привязка пользовательского объекта

Мы можем создать пользовательский элемент управления и привязать его в коде или разметке. Приведем пример из Петцольда (стр. 633) и создадим простой пользовательский объект со свойством зависимости.

  • Добавьте в пространство имен ElementWithObject текущего проекта в файле Window1.xaml.cs новый класс SimpleElement

// Простой пользовательский элемент управления
    class SimpleElement : FrameworkElement
    {
        // Статическое поле зависимости, базовое для свойства
        public static DependencyProperty NumberProperty;
    
        // Инициализация поля в статическом конструкторе
        static SimpleElement()
        {
            NumberProperty = DependencyProperty.Register(
                "Number",
                typeof(double),
                typeof(SimpleElement),
                new FrameworkPropertyMetadata(0.0,
                    FrameworkPropertyMetadataOptions.AffectsRender));
        }
    
        // Свойство зависимости для доступа к полю
        public double Number
        {
            get { return (double)this.GetValue(NumberProperty); }
            set { this.SetValue(NumberProperty, value); }
        }
    
        // Жесткое кодирование размера области вывода
        protected override Size MeasureOverride(Size availableSize)
        {
            return new Size(200, 20);
        } 
    
        // Вывод значения свойства Number
        protected override void OnRender(DrawingContext drawingContext)
        {
            drawingContext.DrawRectangle(Brushes.Yellow, 
                new Pen(Brushes.Red, 1.0), 
                new Rect(new Size(200, 20)));
            drawingContext.DrawText(
                new FormattedText(
                    Convert.ToInt32(Number).ToString(),
                    System.Globalization.CultureInfo.CurrentCulture,
                    FlowDirection.LeftToRight,
                    new Typeface("Arial"),
                    16.0D,
                    SystemColors.WindowTextBrush),
                new Point(0, 0));
        }
    }

При регистрации поля зависимости используется перечисление FrameworkPropertyMetadataOptions, которое определяет типы поведения свойства зависимости.

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

<!-- Привязка пользовательского элемента управления -->
            <TabItem Header="Page9">
                <StackPanel>
                    <ScrollBar Orientation="Horizontal"
                        Margin="24"
                        Maximum="100"
                        LargeChange="10"
                        SmallChange="1"
                        Value="{Binding ElementName=simple9, Path=Number,
                                        Mode=OneWayToSource}" />
                   <local:SimpleElement x:Name="simple9"
                        HorizontalAlignment="Center" />
                   <ScrollBar Name="scroll9"
                        Orientation="Horizontal"
                        Margin="24"
                        Maximum="100"
                        LargeChange="10"
                        SmallChange="1"
                        Value="{Binding ElementName=simple9, Path=Number,
                                        Mode=TwoWay}" />
                   <local:SimpleElement
                        HorizontalAlignment="Center"
                        Number="{Binding ElementName=scroll9, Path=Value,
                                         Mode=OneWay}" />
                </StackPanel>
            </TabItem>

Здесь мы тоже просто используем ранее определенный псевдоним local импортирования пространства имен приложения для доступа к классу SimpleElement из разметки.

Разметка вводит в контейнер <StackPanel> две пары элементов: "библиотечный элемент прокрутки" - "пользовательский элемент". В секции определения первого пользовательского элемента вводится его имя в пространство имен XAML: x:Name="simple9", которое в дальнейшем используется в выражениях привязки другими элементами как имя объекта-источника. Применение обычного атрибута Name="simple9" будет считаться ошибкой.

Первый объект ScrollBar определяет одностороннюю привязку типа OneWayToSource со свойством Number пользовательского элемента и успешно обновляет это свойство при перемещении бегунка. Второй элемент ScrollBar определяет двухстороннюю привязку к свойству Number и успешно обновляет его в представлении двух пользовательских элементов, а также обновляется сам при изменении этого свойства. Однако перемещение второго бегунка хоть и изменяет свойство Number пользовательского элемента, но не влияет на состояние первого бегунка, поскольку определенная в первом ScrollBar привязка OneWayToSource обеспечивает передачу информации только в одну сторону.

  • Запустите приложение и убедитесь в сказанном (слава Петцольду!)


Привязка к источнику с помощью свойства RelativeSource

Класс System.Windows.Data. RelativeSource в выражении привязки целевого элемента указывает источник, которым является родительский объект, находящийся на расстоянии неопределенного (или определенного) числа шагов вверх по дереву элементов в направлении к корню. Этот объект также можно использовать и для привязки элемента к самому себе.

ип такой привязки задается свойством Mode из перечисления RelativeSourceMode, возможные значения которого следующие (Ancestor - предок)

Возможные значения перечисления RelativeSourceMode
Значение Описание
FindAncestor Ссылается на источник с данными в визуальном дереве, который находится на неопределенное число шагов выше целевого элемента, содержащего выражение привязки. При использовании этого режима необходимо задавать дополнительные уточнения в свойствах класса RelativeSource, таких как AncestorType и AncestorLevel. Свойство AncestorType указывает тип источника привязки. Свойство AncestorLevel определяет, с какого вышестоящего уровня от приемника начинать поиск источника. Значение AncestorLevel=1 указывает ближайший к приемнику уровень начального поиска исходного объекта привязки
PreviousData Когда ссылки на источники данных перечислены в списке, этот режим позволяет сменить текущий источник на предыдущий в списке
Self Действует внутри элемента, содержащего выражение привязки, и позволяет сменить одно привязанное свойство на другое в том же элементе, т.е. привязать объект к самому себе
TemplatedParent Если целевой элемент с выражением привязки находится внутри шаблона, такой режим привязывает его к данным шаблона

На первый взгляд привязка со свойством RelativeSource кажется излишним усложнением. Ведь к источнику можно привязаться обычными средствами с помощью ElementName или Source. Однако такое возможно не всегда, в частности, при использовании шаблонов может выручить только привязка со свойством RelativeSource. Механизм шаблонов в этой теме не рассматривается, а вот применение данной привязки в традиционных задачах мы продемонстрируем на нескольких примерах.

Исполнение привязки со свойством RelativeSource, как и обычной, тоже возможно в двух синтаксисах: по синтаксису элементов свойств и по синтаксису расширения разметки.

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

При выполнении в лабораторной работе упражнения №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