Привязка в WPF. Привязка к произвольным объектам (интерфейс INotifyPropertyChanged)

Для того, чтобы привязка данных работала в WPF главным условием является то, что цель привязки обязательно должна наследоваться от DependencyObject (иначе мы не сможем считывать и записывать значение в свойство зависимостей), а целевым свойством должно выступать свойство зависимостей (DependencyProperty). При этом, в качестве источника привязки может выступать кто угодно, но для того, чтобы источник привязки мог сообщать об изменении своих свойств, он должен реализовать интерфейс INotifyPropertyChanged.

Пример приложения

Создадим новое приложение WPF и добавим в него следующий класс:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Объекты этого класса будут выступать в качестве источника данных. Теперь изменим разметку XAML главного окна приложения следующим образом:

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        xmlns:components="clr-namespace:WpfApp1.Components"
        Title="MainWindow" Height="450" Width="435">

    <StackPanel>
        <TextBox Margin="10" Text="{Binding Person.Name, RelativeSource={RelativeSource AncestorType=Window}}"/>
        <TextBox Margin="10" Text="{Binding Person.Age, RelativeSource={RelativeSource AncestorType=Window}}"/>
        <Button Content="Новый пользователь" Click="Button_Click"/>
    </StackPanel>
</Window>

Здесь мы добавили два текстовых поля для вывода информации о пользователе и кнопку, клик по которой будет менять свойства объекта Person. Здесь мы установили относительную привязку к объекту в окне Window, поэтому изменим файл отдельного кода MainWindow.xaml.cs следующим образом:

using System.Windows;


namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public Person Person { get; set; } = new() { Name = "Вася Пупкин", Age = 25};

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Person.Name = "Петя Петров";
            Person.Age = 40;
        }
    }
}

Запустим приложение и проверим результат:

NotifyPropertyChanged

Сколько бы раз мы не кликали по кнопке — текстовые поля не поменяют свое значение, хотя сам источник привязки — меняется. Всё дело в том, что наш объект ни каким образом не сообщает о своем изменении. Чтобы он смог это сделать необходимо реализовать интерфейс INotifyPropertyChanged.

Интерфейс INotifyPropertyChanged

Интерфейс INotifyPropertyChanged предоставляет нам свойство

public event PropertyChangedEventHandler? PropertyChanged;

которое должно срабатывать каждый раз при изменении привязанных свойств источника. Чтобы это сделать — перепишем код класса Person следующим образом:

public class Person: INotifyPropertyChanged
{
    private string _name;
    public string Name { 
        get => _name;
        set {
            if (_name != value) 
            {
                _name = value;
                OnPropertyChanged();
            }
        }
    }

    private int _age;
    public int Age { 
        get => _age;
        set {
            if (_age != value) 
            {
                _age = value;
                OnPropertyChanged();
            }
        } 
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    public void OnPropertyChanged([CallerMemberName] string prop = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }
}

Здесь мы добавили в класс метод OnPropertyChanged(), который и генерирует событие интерфейса. Обратите внимание на атрибут [CallerMemberName] у параметра этого метода. Атрибут CallerMemberName позволяет не указывать вручную имя измененного свойства. Теперь наши объекты типа Person смогут сообщать об изменении своих свойств. Проверим результат работы:

Интерфейс INotifyPropertyChanged

Как видите, теперь интерфейс реагирует на изменение свойств объекта Person.

Автоматизация рутинных задач для привязки данных

Может возникнуть вполне резонный вопрос: если источников привязки в приложении десять и у каждого объекта будет по несколько свойств, то получается, что для каждого привязываемого свойства мы должны будем организовывать работу как показано выше? При использовании только стандартных средств .NET/C# — да, нам придется каждый раз реализовывать интерфейс INotifyPropertyChanged. Однако, этот рутинный процесс можно автоматизировать с помощью пакета CommunityToolkit.Mvvm

Благодаря использованию этого пакета, мы можем автоматизировать выполнении рутинных задач по организации привязки данных в наших приложениях WPF. В качестве примера, продемонстрирую то, как бы выглядел наш класс Person при использовании CommunityToolkit.Mvvm

using CommunityToolkit.Mvvm.ComponentModel;

namespace WpfApp1
{
    public partial class Person: ObservableObject
    {
        [ObservableProperty]
        private string _name;

        [ObservableProperty]
        private int _age;
    }
}

При этом, код XAML менять не нужно — обратите внимание на то, что наш класс стал частичным и наследуется от ObservableObject. Это позволяет CommunityToolkit создать необходимые свойства объекта и использовать их в разметке XAML.

Итого

В WPF мы можем привязываться к произвольным объектам. При этом, классы таких объектов-источников должны реализовать интерфейс INotifyPropertyChanged, чтобы иметь возможность сообщать об изменении своих свойств. В этой части мы рассмотрели то, как реализуется интерфейс INotifyPropertyChanged,  а также то, как можно автоматизировать рутинные задачи по привязке данных с использованием пакета CommunityToolkit.Mvvm

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии