Привязка к объектам. Интерфейс INotifyPropertyChanged

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

По умолчанию в качестве цели привязки обязательно должен выступать объект класса BindableObject, то источником привязки может быть любой объект любого класса. Однако если источником привязки является объект класса, который НЕ наследуется от BindableObject, то мы можем столкнуться с проблемой обновления данных при привязке. Например, пусть у нас в главном проекте есть класс Person:

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

Осущестим привязку к объекту класса Person:

namespace HelloApp;

class StartPage : ContentPage
{
    public StartPage()
    {
        Person tom = new Person { Name = "Tom", Age = 38 };
        Label nameHeaderLabel = new Label { Text = "Name" };
        Label ageHeaderLabel = new Label { Text = "Age" };
        Label nameValueLabel = new Label { FontAttributes = FontAttributes.Bold };
        Label ageValueLabel = new Label { FontAttributes = FontAttributes.Bold };


        Binding nameBinding = new Binding { Source = tom, Path = "Name" };
        Binding ageBinding = new Binding { Source = tom, Path = "Age" };

        nameValueLabel.SetBinding(Label.TextProperty, nameBinding);
        ageValueLabel.SetBinding(Label.TextProperty, ageBinding);

        Button btn = new Button { Text = "+", WidthRequest = 60, HorizontalOptions = LayoutOptions.Start };
        Label testLabel = new Label();
        // по нажатию увеличиваем возраст на 1
        btn.Clicked += (o, e) =>
        {
            tom.Age++;
            testLabel.Text = tom.Age.ToString();
        };

        StackLayout stackLayout = new StackLayout()
        {
            Children = { nameHeaderLabel, nameValueLabel, ageHeaderLabel, ageValueLabel, btn, testLabel },
            Padding = 20
        };
        Content = stackLayout;
    }
}

Здесь на странице элемент Label ageValueLabel привязан к свойству Age объекта Person. По нажатию на кнопку увеличиваем значение свойства Age у объекта tom и выводим это значение в другую метку - testLabel. В итоге при запуске приложения ageValueLabel отобразит значение свойства Age:

Интерфейс INotifyPropertyChanged в .NET MAUI и C#

Однако если мы нажмем на кнопку, то мы не увидим никаких изменений в тексте ageValueLabel. Хотя обработчик кнопки изменит значение свойства Age объекта Person, и метка testLabel отобразит это значение. То есть привязанные метки никак не могут узнать об изменениях в объекте Person.

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

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

namespace HelloApp;

public class Person : INotifyPropertyChanged
{
    string name = "";
    int age;

    public event PropertyChangedEventHandler PropertyChanged;

    public string Name
    {
        get => name;
        set
        {
            if (name != value)
            {
                name = value;
                OnPropertyChanged();
            }
        }
    }
    public int Age
    {
        get => age;
        set
        {
            if (age != value)
            {
                age = value;
                OnPropertyChanged();
            }
        }
    }
    public void OnPropertyChanged([CallerMemberName] string prop = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }
}

При этом в коде самой страницы ничего менять не надо. И если теперь мы нажмем на кнопку, то элемент Label мгновенно отреагирует на изменение свойства Age объекта Person.

Интерфейс INotifyPropertyChanged в приложении на .NET MAUI и C#

Аналогичный пример в XAML:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:HelloApp"
             x:Class="HelloApp.MainPage">
    <ContentPage.Resources>
        <ResourceDictionary>
            <local:Person x:Key="person" Name="Tom" Age="38" />
        </ResourceDictionary>
    </ContentPage.Resources>
    <StackLayout Padding="20" BindingContext="{StaticResource person}">
        <Label Text="Name" />
        <Label Text="{Binding Path=Name}" FontAttributes="Bold" />
        <Label Text="Age" />
        <Label Text="{Binding Path=Age}" FontAttributes="Bold"  />
        <Button Text="+" WidthRequest="60" HorizontalOptions="Start" Clicked="UpdateAge" />
    </StackLayout>
</ContentPage>

Здесь объект Person определяется как статический ресурс. Затем мы можем задать его как контекст для всего элемента StackLayout, а в отдельных элементах Label прописать привязку к свойствам этого объекта.

А в классе связанного кода пропишем обработчик кнопки, который будет менять значение свойства Age:

namespace HelloApp;

public partial class MainPage : ContentPage
{
	public MainPage()
	{
		InitializeComponent();
	}
    private void UpdateAge(object sender, EventArgs e)
    {
        var person = Resources["person"] as Person;
        person.Age ++;
    }
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850