什么是数据绑定?
数据绑定是在应用 UI 与其显示的数据之间建立连接的过程。 如果绑定具有正确的设置,并且数据提供适当的通知,则在数据更改其值时,绑定到该数据的元素会自动反映更改。 数据绑定还意味着,如果元素中数据的外部表示形式发生更改,则基础数据可以自动进行更新以反映更改。 例如,如果用户编辑 TextBox
元素中的值,则基础数据值会自动更新以反映该更改。
数据绑定基本概念
不论要绑定什么元素,也不论数据源是什么性质,每个绑定都始终遵循下图所示的模型。
通常情况下,每个绑定具有四个组件:
- 绑定目标对象。
- 目标属性。
- 绑定源。
- 指向绑定源中要使用的值的路径。
例如,如果要将
TextBox
的内容绑定到Employee.Name
属性,则目标对象是 TextBox,目标属性是 Text属性,要使用的值是 Name,源对象是 Employee 对象。
数据流的方向
可以通过设置 Binding.Mode
来控制数据流。
- OneWay
通过 OneWay绑定,对源属性的更改会自动更新目标属性,但对目标属性的更改不会传播回源属性。 如果绑定的控件为隐式只读,则此类型的绑定适用。 例如,可能会绑定到股票行情自动收录器这样的源,也可能目标属性没有用于进行更改的控件接口(例如表的数据绑定背景色)。 如果无需监视目标属性的更改,则使用 OneWay 绑定模式可避免 TwoWay 绑定模式的系统开销。
- TwoWay
通过 TwoWay 绑定,更改源属性或目标属性时会自动更新另一方。 此类型的绑定适用于可编辑窗体或其他完全交互式 UI 方案。 大多数属性默认为 OneWay 绑定,但某些依赖属性通常 (用户可编辑控件的属性,例如 TextBox.Text 和 CheckBox.IsChecked) 默认为 TwoWay 绑定。 用于确定依赖属性绑定在默认情况下是单向还是双向的编程方法是:使用 DependencyProperty.GetMetadata 获取属性元数据,然后检查 FrameworkPropertyMetadata.BindsTwoWayByDefault 属性的布尔值。
- OneWayToSource
OneWayToSource 绑定与 OneWay 绑定相反;当目标属性更改时,它会更新源属性。 一个示例方案是只需要从 UI 重新计算源值的情况。
- OneTime
OneTime 绑定未在图中显示,该绑定会使源属性初始化目标属性,但不传播后续更改。 如果数据上下文发生更改,或者数据上下文中的对象发生更改,则更改不会在目标属性中反映。 如果适合使用当前状态的快照或数据实际为静态数据,则此类型的绑定适合。 如果你想使用源属性中的某个值来初始化目标属性,且提前不知道数据上下文,则此类型的绑定也有用。 此模式实质上是 OneWay 绑定的一种简化形式,它在源值不更改的情况下提供更好的性能。
若要检测源更改(适用于 OneWay 和 TwoWay 绑定),则源必须实现合适的属性更改通知机制,例如 INotifyPropertyChanged。
触发源更新的因素
TwoWay
或 OneWayToSource
绑定侦听目标属性中的更改,并将更改传播回源(称为更新源)。 例如,可以编辑文本框的文本以更改基础源值。
但是,在编辑文本时或完成文本编辑后控件失去焦点时,源值是否会更新?
Binding.UpdateSourceTrigger
属性确定触发源更新的因素。 下图中右箭头的点说明了 Binding.UpdateSourceTrigger
属性的角色。
如果 UpdateSourceTrigger 值为 UpdateSourceTrigger.PropertyChanged,则目标属性更改后,TwoWay 或 OneWayToSource 绑定的右箭头指向的值会立即更新。 但是,如果 UpdateSourceTrigger 值为 LostFocus,则仅当目标属性失去焦点时才会使用新值更新该值。
与 Mode 属性类似,不同的依赖属性具有不同的默认 UpdateSourceTrigger 值。 大多数依赖属性的默认值为 PropertyChanged,而 TextBox.Text 属性的默认值为 LostFocus。 PropertyChanged 表示源更新通常在每次目标属性更改时发生。 即时更改适用于 CheckBox 和其他简单控件。 但对于文本字段,每次击键后都进行更新会降低性能,用户也没有机会在提交新值之前使用 Backspace 键修改键入错误。
创建绑定
使用 Binding
对象建立绑定,且每个绑定通常具有四个组件:绑定目标、目标属性、绑定源以及指向要使用的源值的路径。
示例,绑定源对象是一个名为 MyData 的类,该类在 SDKSample 命名空间中定义。 出于演示目的,MyData 具有名为 ColorName 的字符串属性,其值设置为“Red”。 因此,此示例生成一个具有红色背景的按钮。
<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:SDKSample">
<DockPanel.Resources>
<c:MyData x:Key="myDataSource"/>
</DockPanel.Resources>
<DockPanel.DataContext>
<Binding Source="{StaticResource myDataSource}"/>
</DockPanel.DataContext>
<Button Background="{Binding Path=ColorName}"
Width="150" Height="30">
I am bound to be RED!
</Button>
</DockPanel>
ColorName 属性的类型为字符串而 Background 属性的类型为 Brush。,此绑定使用默认类型转换。通过设置 DockPanel.DataContext 属性指定绑定源。 然后,Button 从其父元素 DockPanel 继承 DataContext 值。
可通过多种方法指定绑定源对象。 将多个属性绑定到同一个源时,可以使用父元素上的 DataContext 属性。 不过,有时在个别绑定声明中指定绑定源可能更为合适。 对于前面的示例,不使用 DataContext 属性,而是通过在按钮的绑定声明中直接设置 Binding.Source 属性来指定绑定源。
<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:SDKSample">
<DockPanel.Resources>
<c:MyData x:Key="myDataSource"/>
</DockPanel.Resources>
<Button Background="{Binding Source={StaticResource myDataSource}, Path=ColorName}"
Width="150" Height="30">
I am bound to be RED!
</Button>
</DockPanel>
除直接在元素中设置 DataContext 属性、从上级元素(例如第一个示例中的按钮)继承 DataContext 值以及通过在绑定上设置 Binding.Source 属性(例如最后一个示例中的按钮)来显式指定绑定源外,你还可以使用 Binding.ElementName 属性或 Binding.RelativeSource 属性指定绑定源。 当绑定到应用中的其他元素时(例如,使用滑块调整按钮的宽度时),ElementName 属性非常有用。 在 ControlTemplate 或 Style 中指定绑定时,可以使用 RelativeSource 属性。
如果绑定源是一个对象,则使用 Binding.Path 属性指定要用于绑定的值。 如果要绑定到 XML 数据,则使用 Binding.XPath 属性指定值。 在某些情况下,使用 Path 属性(即使数据为 XML)可能更为合适。 例如,如果要访问返回的 XmlNode(作为 XPath 查询的结果)的 Name 属性,则除 XPath 属性外,还应使用 Path 属性。
但在要绑定到整个对象的方案中,要使用的值会与绑定源对象相同。 在这些情况下,可以不指定 Path。
<ListBox ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="true"/>
以上示例使用空绑定语法:{Binding}。 在此示例中,ListBox 从父 DockPanel 元素继承 DataContext(此示例中未显示)。 未指定路径时,默认为绑定到整个对象。 换句话说,此示例中的路径已省略,因为要将 ItemsSource 属性绑定到整个对象。
除了绑定到集合以外,在希望绑定到整个对象,而不是仅绑定到对象的单个属性时,也可以使用此方案。 例如,如果源对象的类型为 String,则可能仅希望绑定到字符串本身。 另一种常见情况是希望将一个元素绑定到一个具有多个属性的对象。