数据绑定是在UI和业务逻辑间建立一个连接的过程,如果绑定有被正确的设置以及数据提供了合适的通知机制,则当数据改变时,被绑定这个数据的元素会自动的反应这个变化。
2. Basic Data Binding Concepts
无论你用何种绑定target和绑定source,所有的绑定都遵循下面的绑定关系:
这个特性说明了以下几点
(1): 每个绑定都有四个要素, binding target object、binding target property、binding source、以及绑定间的关系。例如我们将一个Employee对象的Name属性作为source绑定到一个TextBox的Text属性上。
(2): target property 必须是Dependency property, 而且只有在Dependency object内部才能定义Dependency property。
(3): binding source没有被限定为只能是CLR object,例如一个包含xml data的xml,Ado.Net的数据都可以当作绑定源。
必须注意的是,当实现一个绑定的时候,我们是绑定一个binding target到一个binding source。而不是反过来。
Direction of the Data Flow:
下图说明了几种绑定过程中数据流的方向:
(1): OneWay, 当source改变时,target property会自动的改变。
(2): TwoWay, 当任意一个property改变时,另一个会自动的改变。
(3): OneWayToSource, 当target property改变时,source会自动的改变。这适合于仅要根据UI的变化来评价source value的变化。
(4): OneTime, 和OneWay的绑定方向一样,但是只绑定一次。它适合于source不改变或者以后的改变不需要反映到target上的情况,可以提高性能。
需要注意的是为了监测到source 的变化(在OneWay和TwoWay的绑定中),source需要实现必要的通知机制,例如实现INotifyPropertyChanged接口。
What causes the source updates?
在TwoWay和OneWayToSource的绑定中,target的改变是怎样通知source的呢?这是通过设置UpdateSourceTrigger属性达到的。
它有三个枚举值,LostFocus、PropertyChanged和Explicit。不同的控件默认的该值是不同的,例如CheckBox默认的是PropertyChanged,而TextBox为了提高性能,则是LostFocus。而Explicit则是需要显式的调用了,例如:
<TextBox Name="itemNameTextBox" Text="{Binding Path=ItemName, UpdateSourceTrigger=Explicit}" />例如下面的例子,binding source object是一个MyData类,定义在SDKSample名空间中,MyData类有一个string类型的属性ColorName,被设置成"Red":
这样则只有当程序中调用UpdateSource()方法是,source才更新:
// itemNameTextBox is an instance of a TextBox
BindingExpression be = itemNameTextBox.GetBindingExpression(TextBox.TextProperty); be.UpdateSource();
3. Creating a Binding
<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>
注意在这个例子中,binding source object的指定是通过DockPanel.DataContext属性指定的,因为Button在DockPanel的内部,所以Button会继承DockPanel的DataContext属性的。
这种在父Element中设置DataContext适用于有多个绑定属性时,而只有一个source object,方便。当然我们可以更方便的使用下面的方式:
<DockPanel.Resources>
<c:MyData x:Key="myDataSource"/>
</DockPanel.Resources>
<Button Width="150" Height="30"
Background="{Binding Source={StaticResource myDataSource},
Path=ColorName}">I am bound to be RED!</Button>
Binding and BindingExpression类:
一个Binding类实例提供了binding source的信息,一个BindingExpression类实例则负责把这个binding source和一个target连接起来。因为可以把一个source绑定到多个不同的target,所以这么多BindingExpression实例就共享一个binding类的实例,如下例:
MyData myDataObject = new MyData(System.DateTime.Now.ToShortDateString());
Binding myBinding = new Binding("MyDataProperty"); // 参数是Binding的path。
myBinding.Source = myDataObject;
下面是两个BindingExpression都用这个binding:
myText.SetBinding(TextBlock.TextProperty, myBinding);
myCheck.SetBinding(CheckBox.ContentProperty, myBinding);
我们可以通过BindingOperations类在运行时获得这些信息:
Binding b1 = BindingOperations.GetBinding(myText, TextBlock.TextProperty);
BindingExpression be1 = BindingOperations.GetBindingExpression(myText, TextBlock.TextProperty);
BindingExpression be2 = BindingOperations.GetBindingExpression(myCheck, CheckBox.ContentProperty);
4. Data Conversion
具体参见Data Conversion
5. Binding to collections
除了绑定到单个object之外,很常见的是绑定到一个集合,都可以用下图描述:
注意缺省状况下是OneWay绑定。
同理,为了实现UI的自动更新,这个collection必须实现INotifyCollectionChanged接口,而collection里的每一个对象也要实现INotifyPropertyChanged接口。WPF提供的ObservableCollection<T>类已经实现了这个接口, 我们可以用它,也可以用List<T>和Collection<T>等。Sample