注意
本书于 2016 年春季出版,之后再未更新。 书中有许多内容仍然有价值,但有些内容已过时,有些主题不再完全正确或完整。
程序员经常会编写事件处理程序以检测某对象属性的更改时间,并使用该事件处理程序更改另一个对象中的属性值。 可以使用数据绑定方法自动执行此流程。 数据绑定通常在 XAML 中定义,且属于用户界面定义。
通常,这些数据绑定会将用户界面对象连接到基础数据。 第 18 章 MVVM 中详细介绍了此技术。 但数据绑定还可以连接两个或多个用户界面元素。 本章中,大多数数据绑定早期示例均演示了此方法。
关于绑定的基础知识
数据绑定涉及多个属性、方法和类:
- Binding类派生自- BindingBase且可封装数据绑定的许多特性
- BindingContext属性由- BindableObject类定义
- SetBinding方法也由- BindableObject类定义
- BindableObjectExtensions类定义其他三个- SetBinding方法
以下两个类支持用于绑定的 XAML 标记扩展:
- BindingExtension支持- Binding标记扩展
- ReferenceExtension支持- x:Reference标记扩展
数据绑定涉及两个接口:
- System.ComponentModel命名空间中的- INotifyPropertyChanged用于在属性更改时进行通知
- IValueConverter用于定义数据绑定中将值从一种类型转换为另一种类型的小型类
数据绑定连接同一对象或两个不同对象(更常见)的两个属性。 这两个属性称为源和目标。 通常,源属性更改会导致目标属性发生更改,但有时情况相反。 不考虑:
- 目标属性必须由 BindableProperty支持
- 源属性通常是可实现 INotifyPropertyChanged的类的成员
属性更改值时,可实现 INotifyPropertyChanged 的类会触发 PropertyChanged 事件。 BindableObject 可实现 INotifyPropertyChanged 并在 BindableProperty 支持的属性更改值时自动触发 PropertyChanged 事件,但你可以自己编写可实现 INotifyPropertyChanged 的类,而无需从 BindableObject 派生。
代码和 XAML
OpacityBindingCode 示例演示如何在代码中设置数据绑定:
- 源是 Slider的Value属性
- 目标是 Label的Opacity属性
将 Label 对象的 BindingContext 设置为 Slider 对象即可连接两个对象。 调用 Label 上的 SetBinding 扩展方法(可引用以字符串形式表示的 Slider 的 OpacityProperty 可绑定属性和 Value 属性)即可连接这两个属性。
然后,操作 Slider 会使 Label 淡入和淡出。
OpacityBindingXaml 是与 XAML 中所设数据绑定相同的程序。 Label 的 BindingContext 设置为 x:Reference 标记扩展,其引用 Slider,而 Label 的 Opacity 属性设置为 Binding 标记扩展,其 Path 属性引用 Slider 的 Value 属性。
源和 BindingContext
BindingSourceCode 示例显示代码中的替代方法。 将 Source 属性设置为 Slider 对象并将 Path 属性设置为“Value”即可创建 Binding 对象。 然后在 Label 对象上调用 BindableObject 的 SetBinding 方法。
Binding 构造函数也可以用于定义 Binding 对象。
BindingSourceXaml 示例显示 XAML 中的类似方法。 Label 的 Opacity 属性设置为 Binding 标记扩展,其中 Path 设置为 Value 属性,Source 设置为嵌入式 x:Reference 标记扩展。
总而言之,以下两种方式可以引用绑定源对象:
- 通过目标的 BindingContext属性
- 通过 Binding对象自身的Source属性
如果同时指定两者,则第二种优先。 BindingContext 的优点在于它通过可视化树传播。 如果将多个目标属性绑定到同一源对象,这非常方便。
WebViewDemo 程序使用 WebView 元素演示此技术。 用于向后导航和向前导航的两个 Button 元素从引用 WebView 的父级继承 BindingContext。 然后,两个按钮的 IsEnabled 属性具有简单的 Binding 标记扩展,适用于基于 WebView 的 CanGoBack 和 CanGoForward 只读属性的设置的按钮 IsEnabled 属性。
绑定模式
将 Binding 的 Mode  属性设置为 BindingMode 枚举的成员:
- OneWay,使源属性的更改影响目标
- OneWayToSource,使目标属性的更改影响源
- TwoWay,使源和目标的更改彼此影响
- Default,使用创建目标- BindableProperty时指定的- DefaultBindingMode。 如果未指定,则常规可绑定属性默认为- OneWay,只读可绑定属性默认为- OneWayToSource。
注意
BindingMode 枚举现还包括 OnTime,用于仅在绑定上下文发生更改时而不是在源属性发生更改时应用绑定。
MVVM 场景中可能成为数据绑定目标的属性的 DefaultBindingMode 通常为 TwoWay。 这些功能是:
- Slider和- Stepper的- Value属性
- Switch的- IsToggled属性
- Entry、- Editor和- SearchBar的- Text属性
- DatePicker的- Date属性
- TimePicker的- Time属性
BindingModes 示例演示了数据绑定的四种绑定模式,其中目标是 Label 的 FontSize 属性,源是 Slider 的 Value 属性。 这使每个 Slider 可以控制相应的 Label 的字号。 但 Slider 元素未初始化,因为 FontSize 属性的 DefaultBindingMode 是 OneWay。
ReverseBinding 示例在 Slider(可引用每个 Label 的 FontSize 属性)的 Value 属性上设置绑定。 这似乎是向后的,但更适合初始化 Slider 元素,因为 Slider 的 Value 属性的 DefaultBindingMode 是 TwoWay。
这类似于在 MVVM 中定义绑定的方式,你将经常使用这种类型的绑定。
字符串格式设置
如果目标属性是 string 类型,可以使用 BindingBase 定义的 StringFormat 属性将源转换为 string。 将 StringFormat 属性设置为 .NET 格式设置字符串,该字符串将与静态 String.Format 格式一起用于显示对象。 在标记扩展中使用此格式设置字符串时,用单引号将它括起来,以免大括号被误以为是嵌入的标记扩展。
ShowViewValues 示例演示如何在 XAML 中使用 StringFormat。
WhatSizeBindings 示例演示如何绑定 ContentPage 的 Width 和 Height 属性以显示页面的大小。
为什么称其为“路径”?
Binding 的 Path 属性被称为“路径”是因为它可以是由句点分隔的一系列属性和索引器。 BindingPathDemos 示例显示了多个示例。
绑定值转换器
如果绑定的源属性和目标属性是不同类型,可以使用绑定转换器在类型之间进行转换。 这个类可实现 IValueConverter 接口,且包含两个方法:将源转换为目标的 Convert,以及将目标转换为源的 ConvertBack。
Xamarin.FormsBook.Toolkit 库中的 IntToBoolConverter 类是将 int 转换为 bool 的示例。 ButtonEnabler 示例对此进行了演示,该示例在 Entry 中至少已键入一个字符时才启用 Button。
BoolToStringConverter 类将 bool 转换为 string,并定义两个属性以指定应针对 false 和 true 值而返回的文本。
BoolToColorConverter 类似。 SwitchText 示例演示如何使用这两个转换器基于 Switch 设置以不同颜色显示不同文本。
通用的 BoolToObjectConverter 可替代 BoolToStringConverter 和 BoolToColorConverter,并且可用作任何类型的通用 bool-to-object 转换器。
绑定和自定义视图
可以使用数据绑定简化自定义控件。 NewCheckBox.cs 代码文件定义 Text、TextColor、FontSize、FontAttributes 和 IsChecked 属性,但对于控件的视觉对象而言完全没有逻辑。
而 NewCheckBox.cs.xaml 文件基于代码隐藏文件中定义的属性,在 Label 元素上通过数据绑定包含控件视觉对象的所有标记。
NewCheckBoxDemo 示例演示 NewCheckBox 自定义控件。
