Сводная информация о главе 16. Привязка данных
Примечание.
Эта книга была опубликована весной 2016 года и с тех пор не обновлялась. Многое в этой книге остается ценным, но некоторые материалы устарели, а некоторые разделы перестали быть полностью верными или полными.
Программистам часто приходится писать обработчики событий, которые отслеживают изменение свойства одного объекта и используют его для изменения значения свойства в другом объекте. Этот процесс можно автоматизировать с помощью технологии привязки данных. Привязки данных обычно определяются в XAML и становятся частью определения пользовательского интерфейса.
Очень часто эти привязки данных соединяют объекты пользовательского интерфейса с базовыми данными. Это метод, который рассматривается более в главе 18. MVVM. Но привязки данных могут также соединять два и более элементов пользовательского интерфейса. Этот способ демонстрируется в первых нескольких примерах привязки данных в этой главе.
Основы привязки данных
В привязке данных участвуют несколько свойств, методов и классов:
- класс
Binding
наследуется отBindingBase
и инкапсулирует множество характеристик привязки данных; - свойство
BindingContext
определено в классеBindableObject
; - метод
SetBinding
также определен в классеBindableObject
; - класс
BindableObjectExtensions
определяет три дополнительных методаSetBinding
.
Следующие два класса поддерживают расширения разметки 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
.