WPF数据绑定为应用程序提供了一种表示数据和与数据交互的简单而又一致的方法。元素能够以公共语言运行库 (CLR) 对象和 XML 的形式绑定到各种数据源中的数据。
一、数据绑定的基本概念:
数据绑定涉及到两个方面:一个是绑定源,再一个是绑定目标。绑定源即控件绑定所使用的源数据,绑定目标即数据显示的控件。
1、对于绑定源,在WPF可以是以下四种:
- CLR对象:可以绑定到CLR类的公开的属性、子属性、索引器上
- ADO.Net对象:例如DataTable、DataView等
- XML文件:使用XPath进行解析
- DependencyObject:绑定到其依赖项属性上,即控件绑定控件
2、对于绑定目标,必须是WPF中的DependencyObject,将数据绑定到其依赖项属性上。
二、绑定的基本方式
根据数据流的方向,WPF中的数据绑定分为以下四种:
-
OneWay 绑定导致对源属性的更改会自动更新目标属性,但是对目标属性的更改不会传播回源属性。此绑定类型适用于绑定的控件为隐式只读控件的情况。例如,您可能绑定到如股票行情自动收录器这样的源,或许目标属性没有用于进行更改的控件接口(如表的数据绑定背景色)。如果无需监视目标属性的更改,则使用 OneWay 绑定模式可避免 TwoWay 绑定模式的系统开销。
-
TwoWay 绑定导致对源属性的更改会自动更新目标属性,而对目标属性的更改也会自动更新源属性。此绑定类型适用于可编辑窗体或其他完全交互式 UI 方案。大多数属性都默认为 OneWay 绑定,但是一些依赖项属性(通常为用户可编辑的控件的属性,如 TextBox 的 Text 属性和 CheckBox 的 IsChecked 属性)默认为 TwoWay 绑定。确定依赖项属性绑定在默认情况下是单向还是双向的编程方法是:使用 GetMetadata 获取属性的属性元数据,然后检查 BindsTwoWayByDefault 属性的布尔值。
-
OneWayToSource 与 OneWay 绑定相反;它在目标属性更改时更新源属性。一个示例方案是您只需要从 UI 重新计算源值的情况。
-
OneTime绑定 ,该绑定会导致源属性初始化目标属性,但不传播后续更改。这意味着,如果数据上下文发生了更改,或者数据上下文中的对象发生了更改,则更改会反映在目标属性中。如果您使用的数据的当前状态的快照适于使用,或者这些数据是真正静态的,则适合使用此绑定类型。如果要使用源属性中的某个值初始化目标属性,并且事先不知道数据上下文,则也可以使用此绑定类型。此绑定类型实质上是 OneWay 绑定的简化形式,在源值不更改的情况下可以提供更好的性能。
每个依赖项属性的默认值都不同。一般情况下,用户可编辑控件属性(例如文本框和复选框的属性)默认为双向绑定,而多数其他属性默认为单向绑定。确定依赖项属性绑定在默认情况下是单向还是双向的编程方法是:使用 GetMetadata 来获取属性的属性元数据,然后检查 BindsTwoWayByDefault 属性的布尔值。
三、实现数据源更改影响目标更改
如果要实现数据源更改时,改变目标的值(即上图中的OneWay方式及TwoWay方式的由绑定源到绑定目标方向的数据绑定),需使数据源对象实现System.ComponentModel命名空间的INotifyPropertyChanged接口。INotifyPropertyChanged接口中定义了一个PropertyChanged事件,在某属性值发生变化时引发此事件,即可通知绑定目标更改其显示的值。例如:
1: using System.ComponentModel;
2:
3: namespace BasicWPFDataBinding
4: {
5: public class MyData : INotifyPropertyChanged
6: {
7: #region INotifyPropertyChanged Members
8: public event PropertyChangedEventHandler PropertyChanged;
9: #endregion
10:
11: public MyData()
12: {
13: Name = "Tom";
14: }
15:
16: private string _Name;
17: public string Name
18: {
19: set
20: {
21: _Name = value;
22:
23: if (PropertyChanged != null)
24: {
25: // 引发PropertyChanged事件,
26: // PropertyChangedEventArgs构造方法中的参数字符串表示属性名
27: PropertyChanged(this,new PropertyChangedEventArgs("Name"));
28: }
29: }
30: get
31: {
32: return _Name;
33: }
34: }
35: }
36: }
四、实现绑定目标的值更改影响绑定源的值
若实现实现绑定目标的值更改影响绑定源的值(即上图中TwoWay的由绑定目标到绑定源方向,及OneWayToSource),可以设置相应控件绑定时的UpdateSourceTrigger的值,其值有三种:
- PropertyChanged:当绑定目标属性更改时,立即更新绑定源。
- LostFocus:当绑定目标元素失去焦点时,更新绑定源。
- Explicit:仅在调用 UpdateSource 方法时更新绑定源。
多数依赖项属性的UpdateSourceTrigger 值的默认值为 PropertyChanged,而 Text 属性的默认值为 LostFocus。
例如:
1: <TextBox Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
五、示例:
下面的示例使用第3节中的MyData类作为数据源,演示基于4种绑定方式的执行效果:
XAML代码如下:
1: <Window x:Class="BasicWPFDataBinding.WinBasicBinding"
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:c="clr-namespace:BasicWPFDataBinding"
5: Title="WinBasicBinding" Height="360" Width="360">
6: <Grid>
7: <Grid.ColumnDefinitions>
8: <ColumnDefinition/>
9: <ColumnDefinition/>
10: Grid.ColumnDefinitions>
11: <Grid.RowDefinitions>
12: <RowDefinition/>
13: <RowDefinition/>
14: Grid.RowDefinitions>
15: <StackPanel Grid.Row="0" Grid.Column="0" x:Name="panelOneTime">
16: <StackPanel.Resources>
17: <c:MyData x:Key="myDataSourceA" />
18: StackPanel.Resources>
19: <StackPanel.DataContext>
20: <Binding Source="{StaticResource myDataSourceA}" />
21: StackPanel.DataContext>
22: <TextBlock Text="OneTime Binding" />
23: <TextBox Margin="5" Text="{Binding Path=Name, Mode=OneTime}" />
24: <Button Margin="5" Content="Change Name"
25: x:Name="btnOneTimeBindingChange" Click="btnOneTimeBindingChange_Click" />
26: <Button Margin="5" Content="Get Name"
27: x:Name="btnOnTimeBindingGet" Click="btnOnTimeBindingGet_Click" />
28: StackPanel>
29: <StackPanel Grid.Row="0" Grid.Column="1" x:Name="panelOneWay">
30: <StackPanel.Resources>
31: <c:MyData x:Key="myDataSourceB" />
32: StackPanel.Resources>
33: <StackPanel.DataContext>
34: <Binding Source="{StaticResource myDataSourceB}" />
35: StackPanel.DataContext>
36: <TextBlock Text="OneWay Binding" />
37: <TextBox Margin="5"
38: Text="{Binding Path=Name, Mode=OneWay}" />
39: <Button Margin="5" Content="Change Name"
40: x:Name="btnOneWayeBindingChange" Click="btnOneWayeBindingChange_Click" />
41: <Button Margin="5" Content="Get Name"
42: x:Name="btnOneWayBindingGet" Click="btnOneWayBindingGet_Click" />
43: StackPanel>
44: <StackPanel Grid.Row="1" Grid.Column="0" x:Name="panelTwoWay">
45: <StackPanel.Resources>
46: <c:MyData x:Key="myDataSourceC" />
47: StackPanel.Resources>
48: <StackPanel.DataContext>
49: <Binding Source="{StaticResource myDataSourceC}" />
50: StackPanel.DataContext>
51: <TextBlock Text="TwoWay Binding" />
52: <TextBox Margin="5"
53: Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
54: <Button Margin="5" Content="Change Name"
55: x:Name="btnTwoWayBindingChange" Click="btnTwoWayBindingChange_Click" />
56: <Button Margin="5" Content="Get Name"
57: x:Name="btnTwoWayBindingGet" Click="btnTwoWayBindingGet_Click" />
58: <TextBlock Margin="5"
59: Text="{Binding Path=Name, Mode=OneWay}" />
60: StackPanel>
61: <StackPanel Grid.Row="1" Grid.Column="1" x:Name="panelOneWayToSource">
62: <StackPanel.Resources>
63: <c:MyData x:Key="myDataSourceD" />
64: StackPanel.Resources>
65: <StackPanel.DataContext>
66: <Binding Source="{StaticResource myDataSourceD}" />
67: StackPanel.DataContext>
68: <TextBlock Text="OneWayToSource Binding" />
69: <TextBox Margin="5"
70: Text="{Binding Path=Name, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" />
71: <Button Margin="5" Content="Change Name"
72: x:Name="btnOneWayToSourceBindingChange" Click="btnOneWayToSourceBindingChange_Click" />
73: <Button Margin="5" Content="Get Name"
74: x:Name="btnOneWayToSourceBindingGet" Click="btnOneWayToSourceBindingGet_Click" />
75: <TextBlock Margin="5"
76: Text="{Binding Path=Name, Mode=OneWay}" />
77: StackPanel>
78: Grid>
79: Window>
代码文件如下:
1: using System.Windows;
2:
3: namespace BasicWPFDataBinding
4: {
5: /// <summary>
6: /// Interaction logic for WinBasicBinding.xaml
7: /// summary>
8: public partial class WinBasicBinding : Window
9: {
10: public WinBasicBinding()
11: {
12: InitializeComponent();
13: }
14:
15: #region OneTime绑定
16: private void btnOneTimeBindingChange_Click(object sender, RoutedEventArgs e)
17: {
18: MyData source = (MyData)(panelOneTime.DataContext);
19: source.Name = "Jerry";
20:
21: MessageBox.Show(
22: "myData.Name has been changed to Jerry",
23: "System Information",
24: MessageBoxButton.OK,
25: MessageBoxImage.Information);
26: }
27:
28: private void btnOnTimeBindingGet_Click(object sender, RoutedEventArgs e)
29: {
30: MyData source = (MyData)(panelOneTime.DataContext);
31:
32: string name = source.Name;
33:
34: MessageBox.Show(
35: string.Format("myData.Name value is {0}.", name),
36: "System Information",
37: MessageBoxButton.OK,
38: MessageBoxImage.Information);
39: }
40: #endregion
41:
42: #region OneWay绑定
43: private void btnOneWayeBindingChange_Click(object sender, RoutedEventArgs e)
44: {
45: MyData source = (MyData)(panelOneWay.DataContext);
46: source.Name = "Jerry";
47:
48: MessageBox.Show(
49: "myData.Name has been changed to Jerry",
50: "System Information",
51: MessageBoxButton.OK,
52: MessageBoxImage.Information);
53: }
54:
55: private void btnOneWayBindingGet_Click(object sender, RoutedEventArgs e)
56: {
57: MyData source = (MyData)(panelOneWay.DataContext);
58:
59: string name = source.Name;
60:
61: MessageBox.Show(
62: string.Format("myData.Name value is {0}.", name),
63: "System Information",
64: MessageBoxButton.OK,
65: MessageBoxImage.Information);
66: }
67: #endregion
68:
69: #region TwoWay绑定
70: private void btnTwoWayBindingChange_Click(object sender, RoutedEventArgs e)
71: {
72: MyData source = (MyData)(panelTwoWay.DataContext);
73: source.Name = "Jerry";
74:
75: MessageBox.Show(
76: "myData.Name has been changed to Jerry",
77: "System Information",
78: MessageBoxButton.OK,
79: MessageBoxImage.Information);
80: }
81:
82: private void btnTwoWayBindingGet_Click(object sender, RoutedEventArgs e)
83: {
84: MyData source = (MyData)(panelTwoWay.DataContext);
85:
86: string name = source.Name;
87:
88: MessageBox.Show(
89: string.Format("myData.Name value is {0}.", name),
90: "System Information",
91: MessageBoxButton.OK,
92: MessageBoxImage.Information);
93: }
94: #endregion
95:
96: #region OneWayToSource绑定
97: private void btnOneWayToSourceBindingChange_Click(object sender, RoutedEventArgs e)
98: {
99: MyData source = (MyData)(panelOneWayToSource.DataContext);
100: source.Name = "Jerry";
101:
102: MessageBox.Show(
103: "myData.Name has been changed to Jerry",
104: "System Information",
105: MessageBoxButton.OK,
106: MessageBoxImage.Information);
107: }
108:
109: private void btnOneWayToSourceBindingGet_Click(object sender, RoutedEventArgs e)
110: {
111: MyData source = (MyData)(panelOneWayToSource.DataContext);
112:
113: string name = source.Name;
114:
115: MessageBox.Show(
116: string.Format("myData.Name value is {0}.", name),
117: "System Information",
118: MessageBoxButton.OK,
119: MessageBoxImage.Information);
120: }
121: #endregion
122: }
123: }
程序执行,我们可以得到如下的结论:
- 对于OneTime绑定:在界面中显示的为数据源的初始值,更改数据源的值的时候,不会更改界面的数据显示;更改界面的数据也不会影响到数据源的数据。
- 对于OneWay绑定:在界面中显示的数据可以随数据源的值的变化而变化,但更改界面的数据不会影响到数据源。
- 对于TwoWay绑定:界面中显示的数据及数据源的数据可以双向显示及更新。
- 对于OneWayToSource绑定:初始时界面的数据为空;更改界面的数据可以影响数据源的值,但是更改数据源的值不会体现在界面上。