Объект, который выступает в качестве привязки данных, то есть к которому идет привязка, в .NET MAUI должен представлять класс BindableObject. В реальности стандартные визуальные элементы в .NET MAUI, например, кнопки, метки, текстовые поля, контейнеры компоновки и прочие наследуются от этого класса. Поэтому мы можем привязывать свойства различных объектов к свойствам визуальных элементов.
Отличительной особенностью класса BindableObject является то, что он содержит специальные типы свойств BindableProperty. обычные свойства по сути представляют обертку над BindableProperty. В другой технологии на платформе .NET - WPF есть похожая концепция - свойства зависимости (dependency property), которые имеют похожее назначение.
Например, в .NET MAUI есть класс Label, который является потомком класса BindableObject и у которого есть свойство Text
. Через
это свойство мы можем установить текст метки. Но в реальности это свойство выглядит следующим образом:
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(Label), default(string), propertyChanged: OnTextPropertyChanged); public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } }
Как правило, для каждого свойства BindableProperty создается обертка-обычное свойство. И название BindableProperty обычно имеет название обычного свойства + суффикс
Property
. Например, Text и TextProperty или TextColor и TextColorProperty.
Поэтому, если, допустим, у нас есть элемент Label, и мы хотим присвоить ему некоторый текст, то мы можем сдеать это двумя способами:
Label label = new Label(); // 1 способ - обычное свойство label.Text = "Hello"; // 2 способ - через BindableProperty label.SetValue(Label.TextProperty, "Hello METANIT.COM");
Для установки значения свойства через BindableProperty у объекта BindableObject вызывается метод SetValue()
. В качестве первого
параметра в метод передается само свойство (то есть в данном случае Label.TextProperty). Второй параметр передает значение для этого свойства.
Аналогично для получения значения свойства мы также можем применять два способа:
// 1 способ - через обычное свойство string text = label.Text; // 2 способ - через BindableProperty text = (string)label.GetValue(Label.TextProperty);
Таким образом, определяются BindableObject и BindableProperty.
Допустим, мы хотим определить свое свойство BindableProperty в каком-то своем классе. Например, мы хотим расширить функционал класса Label, чтобы он включал некоторый тег, который присваивается метке. Для этого своздадим свой класс, производный от Label:
public class TagLabel : Label { public static readonly BindableProperty TagProperty = BindableProperty.Create("Tag", // название обычного свойства typeof(string), // возвращаемый тип typeof(TagLabel), // тип, котором объявляется свойство "0"// значение по умолчанию ); public string Tag { set { SetValue(TagProperty, value); } get { return (string)GetValue(TagProperty); } } }
Данный класс располагается в главном проекте решения.
Для определения свойства BindableProperty используется метод BindableProperty.Create(). Этот метод возвращает объект BindableProperty и принимает в данном случае четыре параметра по порядку:
Имя обычного свойства, которое будет оберткой. В данном случае свойство будет называться "Tag"
Возвращаемый тип свойства. В данном случае тип string
Название типа, в котором объявляется это свойство. Здесь тип TagLabel
Значение по умолчанию. Здесь строка "0"
Это не все возможные параметры. Другие перегруженные версии метода BindableProperty.Create() могут принимать еще шесть параметров по порядку:
defaultBindingMode
- режим привязки
validateValue
- метод, который проверяет новое значение на корректность
propertyChanged
- метод, который вызывается при изменении свойства
propertyChanging
- метод, который вызывается перед изменением свойства
coerceValue
- метод корректировки нового значения
defaultValueCreator
- метод-генератор значения по умоланию
После определения класса и свойства они могут участвовать в привязке данных. Так, пусть у нас будет следующий код страницы:
class StartPage : ContentPage { public StartPage() { TagLabel tagLabel = new TagLabel(); Entry entry = new Entry(); // Устанавливаем привязку // источник привязки - entry, цель привязки - tagLabel tagLabel.BindingContext = entry; // Связываем свойства источника и цели tagLabel.SetBinding(TagLabel.TagProperty, "Text"); tagLabel.SetBinding(TagLabel.TextProperty, "Text"); Label simpleLabel = new Label(); simpleLabel.BindingContext = tagLabel; // устанавливаем привязку к свойству Tag объекта tagLabel simpleLabel.SetBinding(Label.TextProperty, "Tag"); StackLayout stackLayout = new StackLayout() { Children = { tagLabel, entry, simpleLabel }, Padding = 20 }; Content = stackLayout; } }Xamarin
Итак, здесь объект нашего класса TagLabel привязан к объекту entry. Причем сразу два свойства - Text и Tag у объекта TagLabel привязаны к свойству Text объекта Entry.
Также здесь есть простой объект Label, свойство Text которого привязано к свойству Tag объекта TagLabel. Поэтому при вводе символов в текстовое поле Entry синхронно будет изменяться значение свойства Tag у tagLabel, а это в свою очередь вызовет изменение свойства Text у простого объекта Label.
Таким образом, определив свое свойство по типу BindableProperty впоследствии мы сможем осуществлять к нему привязку.
Определение аналогичной функциональности в 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"> <StackLayout Padding="20"> <local:TagLabel x:Name="tagLabel" BindingContext="{x:Reference entryBox}" Text="{Binding Text}" Tag="{Binding Text}" /> <Entry x:Name="entryBox" /> <Label x:Name="simpleLabel" BindingContext="{x:Reference tagLabel}" Text="{Binding Tag}" /> </StackLayout> </ContentPage>