Сводная информация о главе 16. Привязка данных

Примечание.

Эта книга была опубликована весной 2016 года и с тех пор не обновлялась. Многое в этой книге остается ценным, но некоторые материалы устарели, а некоторые разделы перестали быть полностью верными или полными.

Программистам часто приходится писать обработчики событий, которые отслеживают изменение свойства одного объекта и используют его для изменения значения свойства в другом объекте. Этот процесс можно автоматизировать с помощью технологии привязки данных. Привязки данных обычно определяются в XAML и становятся частью определения пользовательского интерфейса.

Очень часто эти привязки данных соединяют объекты пользовательского интерфейса с базовыми данными. Это метод, который рассматривается более в главе 18. MVVM. Но привязки данных могут также соединять два и более элементов пользовательского интерфейса. Этот способ демонстрируется в первых нескольких примерах привязки данных в этой главе.

Основы привязки данных

В привязке данных участвуют несколько свойств, методов и классов:

Следующие два класса поддерживают расширения разметки XAML для привязок:

  • BindingExtension поддерживает расширение разметки Binding.
  • ReferenceExtension поддерживает расширение разметки x:Reference.

В привязке данных участвуют два интерфейса:

  • INotifyPropertyChanged в пространстве имен System.ComponentModel используется для реализации уведомлений об изменении свойства;
  • IValueConverter используется для определения небольших классов, которые в привязках данных преобразуют значения из одного типа в другой.

Привязка данных соединяет два свойства одного объекта или (чаще) двух разных объектов. Эти два свойства называются исходным и целевым. Как правило, изменение исходного свойства приводит к изменению целевого свойства, но иногда направление изменяется. Но, в любом случае:

  • целевое свойство должно подкрепляться BindableProperty;
  • исходное свойство обычно является членом класса, который реализует INotifyPropertyChanged.

Класс, который реализует INotifyPropertyChanged, запускает событие PropertyChanged при изменении значения свойства. BindableObject реализует INotifyPropertyChanged и автоматически вызывает событие PropertyChanged, когда подкрепленное BindableProperty свойство изменяет значения. Но можно написать собственные классы с реализацией INotifyPropertyChanged без наследования от BindableObject.

Код и XAML

В примере OpacityBindingCode показано, как задать привязку данных в коде.

  • Исходным является свойство Value объекта Slider.
  • Целевым является свойство Opacity объекта Label.

Эти два объекта связываются путем присвоения BindingContext из объекта Label в объект Slider. Эти два свойства связываются путем вызова метода расширения SetBinding для Label со ссылкой на привязываемое свойство OpacityProperty и свойство Value объекта Slider, выраженное строковым значением.

Теперь изменение Slider приводит к тому, что Label исчезает и появляется снова.

OPacityBindingXaml — это такая же программа, но в ней привязка данных настроена в XAML. BindingContext в Label содержит расширение разметки x:Reference, которое ссылается на Slider, а свойство Opacity в Label содержит расширение разметки Binding, у которого свойство Path ссылается на свойство Value объекта Slider.

Источник и BindingContext

В примере BindingSourceCode показан другой подход, реализуемый в коде. Для создания объекта Binding свойству Source присваивается объект Slider, а свойству Path задается значение "Value". Затем для объекта Label вызывается метод SetBinding из BindableObject.

Конструктор Binding также можно использовать для определения объекта Binding.

В примере BindingSourceXaml показан аналогичный подход с использованием XAML. Свойству Opacity объекта Label присвоено расширение разметки Binding, где Path получает значение свойства Value, и Source содержит внедренное расширение разметки x:Reference.

Итак, у нас есть несколько способов указать исходный объект для привязки:

  • через свойство BindingContext в целевом объекте;
  • через свойство Source в самом объекте Binding.

Если указаны оба варианта, второй имеет более высокий приоритет. Преимуществом BindingContext является то, что оно распространяется через визуальное дерево. Это очень удобно, если несколько целевых свойств привязаны к одному и тому же исходному объекту.

Программа WebViewDemo демонстрирует этот способ на примере элемента WebView. Два элемента Button для перемещения вперед и назад наследуют BindingContext от своего родительского элемента, который ссылается на WebView. Затем свойства IsEnabled двух кнопок получают простые расширения разметки Binding, нацеленные на свойства IsEnabled кнопки, как определяется свойствами CanGoBack и CanGoForward только для чтения в объекте WebView.

Режим привязки

Присвойте свойству Mode объекта Binding значение одного из элементов перечисления BindingMode:

  • OneWay означает, что изменение исходного свойства влияет на целевое;
  • OneWayToSource означает, что изменение целевого свойства влияет на исходное;
  • TwoWay означает, что исходное и целевое свойства влияют друг на друга;
  • Default означает, что используется DefaultBindingMode, указанное при создании целевого BindableProperty. Если не указано ни одно значение, по умолчанию используется OneWay для обычных привязываемых свойств и OneWayToSource для привязываемых свойств только для чтения.

Примечание.

Перечисление BindingMode теперь также включает OnTime, при выборе которого привязка применяется только в случае изменения контекста привязки, а не изменения исходного свойства.

Свойства, которые могут стать целевыми для привязки данных в сценариях MVVM, обычно имеют для DefaultBindingMode значение TwoWay. К ним относятся:

  • свойство Value объектов Slider и Stepper;
  • свойство IsToggled объекта Switch.
  • свойство Text объектов Entry, Editor и SearchBar;
  • свойство Date объекта DatePicker.
  • свойство Time объекта TimePicker.

В примере BindingModes демонстрируются четыре режима привязки данных, где целевым является свойство FontSize объекта Label, а исходным — свойство Value объекта Slider. Это позволяет каждому Slider управлять размером шрифта соответствующего Label. Однако элементы Slider не инициализируются, так как DefaultBindingMode для свойства FontSize имеет значение OneWay.

В примере ReverseBinding настраивается привязка свойства Value объекта Slider к свойству FontSize каждого объекта Label. Кажется, что должно быть наоборот, но так лучше работает инициализация элементов Slider, так как в свойстве Value объекта Slider параметр DefaultBindingMode имеет значение TwoWay.

Снимок экрана с тремя изображениями обратной привязки

Похожим способом привязки определяются в MVVM, и вы будете часто использовать этот тип привязки.

Форматирование строк

Если целевое свойство имеет тип string, вы можете применить свойство StringFormat, определенное в BindingBase, для преобразования исходного свойства в string. Задайте для свойства StringFormat строку форматирования .NET, которая будет использоваться совместно со статическим форматом String.Format для отображения объекта. При использовании этой строки форматирования в расширении разметки ее следует заключить в одинарные кавычки, чтобы фигурные скобки не были ошибочно приняты за внедренное расширение разметки.

В примере ShowViewValues показано использование StringFormat в XAML.

В примере WhatSizeBindings демонстрируется отображение размера страницы с привязками к свойствам Width и Height объекта ContentPage.

Почему "путь" так называется?

Свойство Path ("Путь") объекта Binding получило такое название, так как оно может содержать серию свойств и индексаторов, разделенных точками. В BindingPathDemos представлено несколько примеров.

Преобразователи значений привязки

Если исходное и целевое свойства привязки имеют разные типы, преобразование между этими типами можно выполнить с помощью преобразователя привязок. Этот класс реализует интерфейс IValueConverter и содержит два метода: Convert для преобразования исходного значения в целевое и ConvertBack для преобразования целевого значения в исходное.

Класс IntToBoolConverter из библиотеки Xamarin.FormsBook.Toolkit служит примером преобразования int в bool. Это демонстрируется в примере ButtonEnabler, который включает Button только в том случае, если в Entry введен хотя бы один символ.

Класс BoolToStringConverter преобразует bool в string и определяет два свойства, чтобы указать текст для возвращаемых значений false и true. BoolToColorConverter действует аналогично. В примере SwitchText показано использование этих двух преобразователей для отображения разных текстов разными цветами на основе значения параметра Switch.

Универсальный BoolToObjectConverter может заменить BoolToStringConverter и BoolToColorConverter, выполняя роль обобщенного преобразователя bool в объект любого типа.

Привязки и пользовательские представления

Вы можете упростить пользовательские элементы управления, используя привязки данных. В файле кода NewCheckBox.cs определяются свойства Text, TextColor, FontSize, FontAttributes и IsChecked, но он не содержит логику для визуальных элементов управления. Вместо этого в файле NewCheckBox.cs.xaml определена вся разметку для визуальных элементов управления через привязки данных в элементах Label, основанные на свойствах в файле кода программной части.

В примере NewCheckBoxDemo демонстрируется пользовательский элемент управления NewCheckBox.