Контекст данных и интерфейс INotifyPropertyChanged

Последнее обновление: 13.04.2017

Свойство DataContext

Одним из способов задания привязки к объекту является использование контекста данных или свойства DataContext. С его помощью мы можем задать элементу какой-то общий контекст, а все его вложенные элементы будут привязаны к отдельным свойствам этого контекста. Посмотрим на примере с объектом Phone:

public class Phone
{
	public string Title { get; set; }
	public string Company { get; set; }
	public int Price { get; set; }
}

Свяжем свойства объекта Phone элементами на странице через DataContext:

<Page
    x:Class="BindingApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:BindingApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Page.DataContext>
        <local:Phone Title="Lumia 950" Company="Microsoft" Price="40000" />
    </Page.DataContext>
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBlock Text="Модель" />
        <TextBlock Text="{Binding Title}" Grid.Row="1" />
        <TextBlock Text="Производитель" Grid.Column="1"/>
        <TextBlock Text="{Binding Company}" Grid.Column="1" Grid.Row="1" />
        <TextBlock Text="Цена" Grid.Column="2" />
        <TextBlock Text="{Binding Price}" Grid.Column="2" Grid.Row="1" />
    </Grid>
</Page>
DataContext в Universal Windows Platform

Таким образом мы задаем свойству DataContext некоторый ресурс, и затем осуществляется привязка к этому ресурсу.

Каждый объект класса FrameworkElement, а это практически все стандартные элементы управления, может определять свой собственный контекст данных. Например:

<Page
    x:Class="BindingApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:BindingApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Page.DataContext>
        <local:Phone Title="iPhone 7" Company="Apple" Price="49000" />
    </Page.DataContext>
    <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="{Binding Title}" />
        <StackPanel>
            <StackPanel.DataContext>
                <local:Phone Title="Huawei P10" Company="Huawei" Price="30000" />
            </StackPanel.DataContext>
            <TextBlock Text="{Binding Title}" />
        </StackPanel>
    </StackPanel>
</Page>

Здесь определены два элемента StackPanel, каждый из которых использует разные контексты данных: один использует контекст страницы, а другой определяет свой собственный.

Свойство DataContext в UWP

Определение контекста в коде C#:

using Windows.UI.Xaml.Controls;
namespace BindingApp
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();

            this.DataContext = new Phone { Title = "Samsung Galaxy S8", Company = "Samsung", Price = 50000 };
        }
    }
}

В этом случае все выражения Binding в XAML автоматически будут привязываться к свойствам этого объекта:

<Page
    x:Class="BindingApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:BindingApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="{Binding Title}" />
    </StackPanel>
</Page>

Интерфейс INotifyPropertyChanged

В примерах выше привязка осуществлялась к объекту класса Phone. Но использование этого класса имело один минус. Даже если мы изменим значения его свойств, то содержимое привязанных текстовых блоков это никак не повлияет. Например, добавим на страницу пару кнопок для изменения состояния объекта Phone:

<Page
    x:Class="BindingApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:BindingApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Page.DataContext>
        <local:Phone Title="iPhone 7" Company="Apple" Price="49000" />
    </Page.DataContext>
    <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="{Binding Title}" />
        <TextBlock Text="{Binding Price}" />
        <StackPanel Orientation="Horizontal">
            <Button Margin="10" Click="Increase">+</Button>
            <Button Click="Decrease">-</Button>
        </StackPanel>
    </StackPanel>
</Page>

А в файле кода C# для этих кнопок определим обработчики Increase и Decrease, в которых будет меняться свойство Price:

private void Increase(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    Phone phone = this.DataContext as Phone;
    phone.Price += 1000;
}

private void Decrease(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    Phone phone = this.DataContext as Phone;
    phone.Price -= 1000;
}

В данном случае нажатия на кнопку изменят ресурс, но текстовые блоки, привязанные к этому ресурсу, не изменятся. Чтобы объект мог полноценно реализовать механизм привязки, нам надо реализовать в его классе интерфейс INotifyPropertyChanged. И для этого изменим класс Phone следующим образом:

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace BindingApp
{
    public class Phone : INotifyPropertyChanged
    {
        private string title;
        private string company;
        private int price;

        public string Title
        {
            get { return title; }
            set
            {
                if (title != value)
                {
                    title = value;
                    OnPropertyChanged("Title");
                }
            }
        }
        public string Company
        {
            get { return company; }
            set
            {
                if (company != value)
                {
                    company = value;
                    OnPropertyChanged("Company");
                }
            }
        }
        public int Price
        {
            get { return price; }
            set
            {
                if (price != value)
                {
                    price = value;
                    OnPropertyChanged("Price");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged([CallerMemberName]string prop = "")
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }
}

Теперь при изменении значения свойства в объекте Phone срабатывает метод OnPropertyChanged, через который данный объект будет извещать систему об изменении через событие PropertyChanged. А система в свою очередь обновляет все привязанные объекты, в частности, элементы TextBlock.

Интерфейс INotifyPropertyChanged в Universal Windows Platform
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850