WPF数据绑定为应用程序提供了一种简单而一致的方法来显示数据以及与数据交互。 元素能够以CLR对象和 XML 形式绑定到来自各种数据源的数据。
WPF 中的数据绑定功能与传统模型相比具有一些优势,包括本质上支持数据绑定的各种属性、灵活的数据 UI 表示形式,以及业务逻辑与 UI 的完全分离。
数据绑定是在应用程序 UI 与业务逻辑之间建立连接的过程。 如果绑定具有正确设置并且数据提供正确通知,则当数据更改其值时,绑定到数据的元素会自动反映更改。 数据绑定可能还意味着如果元素中数据的外部表现形式发生更改,则基础数据可以自动更新以反映更改。 例如,如果用户编辑 TextBox 元素中的值,则基础数据值会自动更新以反映该更改。
1. 数据绑定基础
1.1 数据绑定模型
完整的数据绑定模型由5部分组成:目标对象、目标属性、数据源对象、数据源属性和绑定对象。如下:
注意点:
- 目标属性必须是依赖属性。
- 绑定源 对象并不限于自定义 CLR 对象。 WPF 数据绑定支持 CLR 对象和 XML 形式的数据。 举例来说,绑定源可以是 UIElement、任何列表对象、与 ADO.NET 数据或 Web 服务关联的 CLR 对象,也可以是包含 XML 数据的 XmlNode。
1.2 数据绑定方向
数据绑定方向通过Binding 对象的 Mode 属性来控制。
Mode包括:
成员名称 | 说明 |
TwoWay | 导致对源属性或目标属性的更改可自动更新对方。 此绑定类型适用于可编辑窗体或其他完全交互式 UI 方案。 |
OneWay | 当绑定源(源)更改时,更新绑定目标(目标)属性。 如果要绑定的控件为隐式只读控件,则适用此绑定类型。 例如,可以绑定到如股市代号之类的源。 或者,可能目标属性没有用于进行更改(例如表的数据绑定背景色)的控件接口。 如果不需要监视目标属性的更改,则使用 OneWay 绑定模式可避免 TwoWay 绑定模式的系统开销。 |
OneTime | 当应用程序启动或数据上下文更改时,更新绑定目标。 此绑定类型适用于以下情况:使用当前状态的快照适合使用的或数据状态实际为静态的数据。 如果要从源属性初始化具有某个值的目标属性,并且事先不知道数据上下文,则也可以使用此绑定类型。 实质上,这是 OneWay 绑定的较简单的形式,它在不更改源值的情况下可提供更好的性能。 |
OneWayToSource | 当目标属性更改时更新源属性。与OneWay相反。 |
Default | 使用绑定目标的默认 Mode 值。 每个依赖项属性的默认值都不同。 一般情况下,用户可编辑控件属性(例如文本框和复选框的属性)默认为双向绑定,而多数其他属性默认为单向绑定。 确定依赖项属性绑定在默认情况下是单向还是双向的编程方法是:使用 GetMetadata 来获取属性的属性元数据,然后检查 BindsTwoWayByDefault 属性的布尔值。也可通过MSDN查看对应依赖属性的信息中设置为 true 的元数据属性。 |
数据绑定的触发条件通过Binding 对象的 UpdateSourceTrigger 属性来控制。
成员名称 | 说明 |
Default | 绑定目标属性的默认 UpdateSourceTrigger值。多数依赖项属性的默认值为PropertyChanged,而 Text属性的默认值为 LostFocus。 确定依赖项属性的默认 UpdateSourceTrigger值的编程方法是使用 GetMetadata来获取属性的属性元数据,然后检查 DefaultUpdateSourceTrigger属性的值。 |
PropertyChanged | 当绑定目标属性更改时,立即更新绑定源。 |
LostFocus | 当绑定目标元素失去焦点时,更新绑定源。 |
Explicit | 仅在调用 UpdateSource方法时更新绑定源。 |
1.4 数据绑定实例
现在有两个TextBox,当一个文本框内容修改的时候另外一个文本框同步修改。WinForm的思想应该是分别给两个文本框写文本改变事件。
将第一个文本框的Text属性绑定到第二个文本框的Text属性即可。
XAML中使用
- Text="{Binding Path=Text,ElementName=txtBox2,Mode=Default,UpdateSourceTrigger=PropertyChanged}"
程序代码中使用
- txtBox1.SetBinding(TextBox.TextProperty, new Binding("Text")
- {
- ElementName="txtBox2",
-
- UpdateSourceTrigger=UpdateSourceTrigger.PropertyChanged
- });
1.5小结
在依赖属性进价(一)中讲到的依赖属性元数据,这里的绑定方向和触发条件的Default就是在依赖属性元数据中的。
通过下面的编程方法来获得他们的Default值(以下为即时窗口调试输出结果):
TextBox.TextProperty.GetMetadata(txtBox1);
{System.Windows.FrameworkPropertyMetadata}
[System.Windows.FrameworkPropertyMetadata]: {System.Windows.FrameworkPropertyMetadata}
CoerceValueCallback: {Method = {System.Object CoerceText(System.Windows.DependencyObject, System.Object)}}
DefaultValue: ""
PropertyChangedCallback: {Method = {Void OnTextPropertyChanged(System.Windows.DependencyObject, System.Windows.DependencyPropertyChangedEventArgs)}}
(TextBox.TextProperty.GetMetadata(txtBox1) as System.Windows.FrameworkPropertyMetadata).BindsTwoWayByDefault
true
(TextBox.TextProperty.GetMetadata(txtBox1) as System.Windows.FrameworkPropertyMetadata).DefaultUpdateSourceTrigger
LostFocus
2. 绑定数据源的4种方式
定义资源
- <Window.Resources>
- <local:Student x:Key="student" id="01" name="小丽"/>
- </Window.Resources>
XAML中使用
- Text="{Binding Path=id,Source={StaticResource student}}"
该绑定方式多用于元素间的绑定,上面的数据绑定实例中用的就是ElementName绑定。
RelativeSourceMode枚举
成员名称 | 说明 |
PreviousData | 允许您绑定所显示数据项列表中以前的数据项(不是包含数据项的控件)。 |
TemplatedParent | 引用应用了模板(其中有数据绑定元素)的元素。这类似于设置TemplateBindingExtension,并仅当 Binding在模板中时适用。 |
Self | 引用您对其设置绑定的元素,并允许您将该元素的一个属性绑定到同一元素中的其他属性。 |
FindAncestor | 引用数据绑定元素父链中的上级。您可以使用它绑定到特定类型或其子类的上级。如果您要指定 AncestorType和/或 AncestorLevel,可以使用此模式。 |
需要指定绑定对象或其父级的DataContext,一般可在程序代码中指定。
此时,添加个button,让stu的id值加"01"的话,TextBox中的内容没有得到相应的更新。
虽然将数据绑定的方向规定为OneWay或者TwoWay,但是数据源没有一种机制通知目标,属性发送改变后目标是得不到相应的更新的。通常解决这个问题有两种方法。
- 将数据源属性实现为依赖属性。此法多用于元素之间,对于Person这种类型,此法不合适。
- 数据源实现INotifyPropertyChanged接口。
INotifyPropertyChanged 接口用于向客户端(通常是执行绑定的客户端)发出某一属性值已更改的通知。
- public class Student : INotifyPropertyChanged
- {
- private string _id;
-
- public string id
- {
- get { return _id; }
- set
- {
- _id = value;
- NotifyPropertyChanged("id");
- }
- }
- public string name { get; set; }
- public Sex sex{ get; set; }
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- private void NotifyPropertyChanged(string info)
- {
- if (PropertyChanged != null)
- {
- PropertyChanged(this, new PropertyChangedEventArgs(info));
- }
- }
- }
效果图
4. 数据转换器(ValueConverter)
用于不同数据类型间值的转换。譬如将枚举类型的Sex值转为中文显示。
转换器代码
- public class SexConverter : IValueConverter
- {
-
- public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
- {
- string sex = "其他";
- if(value is Sex)
- {
- sex = (Sex)value == Sex.Boy ? "男" : ((Sex)value == Sex.Girl ? "女" : "其他");
- }
- return sex;
- }
-
-
- public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
- {
- return value.ToString().Trim() == "男" ? Sex.Boy : (value.ToString().Trim() == "女" ? Sex.Girl : Sex.Other);
- }
- }
XMAL中代码
- <TextBox HorizontalAlignment="Left" Height="32" Margin="31,137,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="227"
- Text="{Binding stu.sex,Converter={StaticResource sexConverter}}"/>
效果
5.数据验证
接受用户输入的大多数应用程序都需要具有验证逻辑,以确保用户输入了需要的信息。 验证检查可以基于类型、范围、格式或其他应用程序特定的要求。
5.1 创建验证规则
ValidationRule 对象可检查属性的值是否有效。 WPF 具有以下两种类型的内置 ValidationRule 对象:
ExceptionValidationRule 检查在更新绑定源属性的过程中引发的异常。 在前面的示例中,StartPrice 为整数类型。 当用户输入的值无法转换为整数时,将引发异常,这会
导致将绑定标记为无效。 用于显式设置 ExceptionValidationRule 的可选语法是将 Binding 或 MultiBinding 对象上的 ValidatesOnExceptions 属性设置为 true。
DataErrorValidationRule 对象检查由实现 IDataErrorInfo 接口的对象所引发的错误。 有关使用此验证规则的示例,请参见 DataErrorValidationRule。 用于显式设置
DataErrorValidationRule 的可选语法是将 Binding 或 MultiBinding 对象上的 ValidatesOnDataErrors 属性设置为 true。
5.1.1 自定义验证规则
可以通过从 ValidationRule 类派生和实现 Validate 方法来创建自己的验证规则。
- class IdRule : ValidationRule
- {
- public override ValidationResult Validate(object value, CultureInfo cultureInfo)
- {
- if(string.IsNullOrWhiteSpace(value.ToString()))
- {
- return new ValidationResult(false, "代码不能为。");
- }
- else
- {
- Regex regex = new Regex(@"^\d+$");
- if(regex.IsMatch(value.ToString()))
- {
- return ValidationResult.ValidResult;
- }
- else
- {
- return new ValidationResult(false, "代码只能输入数字。");
- }
- }
- }
- }
XAML中使用
- <TextBox HorizontalAlignment="Left" Height="29" Margin="124,216,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="247">
- <TextBox.Text>
- <Binding Path="stu.id">
- <Binding.ValidationRules>
- <local:IdRule />
- </Binding.ValidationRules>
- </Binding>
- </TextBox.Text>
- </TextBox>
5.2验证提示方法
5.2.1使用附加属性Validation.ErrorTemplate
- <ControlTemplate x:Key="validationTemplate">
- <DockPanel>
- <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
- <AdornedElementPlaceholder/>
- </DockPanel>
- </ControlTemplate>
5.2.2使用ToolTip
- <Style x:Key="textStyleTextBox" TargetType="TextBox">
- <Style.Triggers>
- <Trigger Property="Validation.HasError" Value="true">
- <Setter Property="ToolTip"
- Value="{Binding RelativeSource={RelativeSource Self},
- Path=(Validation.Errors)[0].ErrorContent}"/>
- </Trigger>
- </Style.Triggers>
- </Style>
验证的
TextBox
的
XAML
代码
- <TextBox HorizontalAlignment="Left" Height="29" Margin="124,216,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="247"
- Validation.ErrorTemplate="{StaticResource validationTemplate}"
- Style="{StaticResource textStyleTextBox}">
- <TextBox.Text>
- <Binding Path="stu.id">
- <Binding.ValidationRules>
- <local:IdRule />
- </Binding.ValidationRules>
- </Binding>
- </TextBox.Text>
- </TextBox>
效果