Привязка данных или data binding является одним из ключевых аспектов при создании приложений на платформе .NET Multi-platform App UI. Привязка позволяет связать два свойства разных объектов таким образом, что изменения свойства одного объекта автоматически приводили к изменению свойства другого объекта.
Привязка данных состоит из двух компонентов:
источник привязки (source) - кто привязывается
цель привязки (target) - к кому идет привязка
Привязка осуществляется от свойства источника к свойству цели. И когда происходит изменение источника, механизм привязки автоматически обновляет также и цель.
Объект-цель привязки должен представлять объект BindableObject, а свойство, к которому осуществляется привязка, должно быть свойством BindableProperty. Поскольку большинство визуальных элементов в .NET MAUI и C# наследуются от класса BindableObject, то в качестве цели привязки будут, как правило, выступать визуальные элементы.
А вот источником привязки может выступать любой объект языка C#. Однако, надо понимать, что цель привязки должна автоматически изменяться при изменении источника, поэтому нам нужно извещать систему о изменении свойств источника привязки. В .NET MAUI, да и вообще на платформе .NET, в качестве подобного механизма извещения выступает интерфейс INotifyPropertyChanged. То есть нужно реализовать данный интерфейс в объекте-источнике.
Объект BindableObject как раз реализует INotifyPropertyChanged
. Поэтому если источником привязки является стандартный визуальный элемент
из .NET MAUI, то автоматически будет изменяться и цель привязки. Но если в качестве источника выступает не BindableObject, а какой-нибудь объект простого
класса C#, то, как писалось выше, этот класс должен реализовать INotifyPropertyChanged.
Есть два способа установить привязку - с помощью свойства BindingContext и с помощью объекта Binding. Рассмотрим на примерах, как устанавливается привязка в коде C# и в коде XAML.
Для установки привязки у объекта-цели устанавливается свойство BindingContext. В качестве значения оно принимает источник привязки.
Для привязки в коде после установки свойства BindingContext у объекта-цели привязки необходимо также вызвать метод SetBinding(), который свяжет свойство объекта-цели со свойством объекта-источника.
namespace HelloApp { class StartPage : ContentPage { public StartPage() { Label label = new Label(); Entry entry = new Entry(); // Устанавливаем привязку // источник привязки - entry, цель привязки - label label.BindingContext = entry; // Связываем свойства источника и цели label.SetBinding(Label.TextProperty, "Text"); StackLayout stackLayout = new StackLayout { Children = { label, entry }, Padding = 20 }; Content = stackLayout; } } }
Здесь объект-цель привязки - label
, а источник привязки - entry
. То есть привязка идет от label к entry. Выражение
label.SetBinding(Label.TextProperty, "Text")
устанавливает привязку свойства TextProperty
элемента label к свойству Text
источника привязки - элемента Entry.
Причем, что важно у объекта цели Label привязка устанавливается именно у свойства BindableProperty
, которым является TextProperty
,
а не просто для свойства Text
.
В итоге при вводе данных в текстовое поле будет изменяться значение свойства Text у объекта entry. А это изменение автоматически скажется на объекте label и изменит его свойство TextProperty.
И таким образом, нам не надо вручную обрабатывать изменения текста в текстовом поле Entry, все за нас делает механизм привязки данных.
Аналогично можно установить привязку и в 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" x:Class="HelloApp.MainPage"> <StackLayout Padding="20"> <Label BindingContext="{x:Reference entryBox}" Text="{Binding Text}" /> <Entry x:Name="entryBox" /> </StackLayout> </ContentPage>
В XAML действует тот же принцип. Для установки привязки для объекта цели задается свойство BindingContext. Но теперь его значение
имеет другую форму: {x:Reference entryBox}
- расширению разметки x:Reference
передается имя элемента источника.
То есть в данном случае источником выступает объект с именем entryBox.
Далее устанавливается сама привязка к свойству:
Text="{Binding Text}"
Здесь также устанавливается привязка к свойству Text. Выражение привязки заключается в фигурные скобки и состоит из слова Binding, после которого указывается свойство источника-привязки. То есть свойство Text объекта label привязано к свойству Text объекта entryBox.
Другой способ привязки представляет объект Binding. Например:
class StartPage : ContentPage { public StartPage() { Label label = new Label(); Entry entry = new Entry(); // определяем объект привязки: Source - источник, Path - его свойство Binding binding = new Binding { Source = entry, Path = "Text" }; // установка привязки для свойства TextProperty label.SetBinding(Label.TextProperty, binding); StackLayout stackLayout = new StackLayout() { Children = { entry, label}, Padding = 20 }; Content = stackLayout; } }
Здесь у объекта Binding настраиваются ряд свойств. В частности, свойство Source
указывает на источник привязки, а свойство
Path
- на свойство источника. В данном случае источником служит объект Entry, а свойством - его свойство Text.
Далее в метод SetBinding()
в качестве второго параметра передается объект Binding, инкапсулирующий все опции привязки.
В итоге мы получаем то же самое, что и в прошлой теме: свойство Text объекта Label привязано к свойству Text объекта Entry.
Тоже самое можно прописать и в 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" x:Class="HelloApp.MainPage"> <StackLayout Padding="20"> <Label x:Name="label" Text="{Binding Source={x:Reference entry}, Path=Text}" /> <Entry x:Name="entry" /> </StackLayout> </ContentPage>
Таким образом, нам необязательно использовать свойство BindingContext для установки привязки к источнику. Более того использование объекта Binding имеет свои преимущества. В частности, с помощью BindingContext мы можем установить только один источник привязки для какого-либо элемента. Но что если мы хотим, чтобы одно свойство у Label было привязано к одному элемену, другое свойство - к другому элементу? В этом случае можно использовать только вышеописанный способ.
Также мы можем одновременно устанавливать и BindingContext, и использовать объект Binding для определения опций привязки, и в этом случае объект Binding будет переопределять BindingContext, если, к примеру, в BindingContext указан один источник привязки, а в объекте Binding другой.