小疯最近的项目组工做开发中须要用到WPF,因此最近在学习WPF,小疯对于WPF中的绑定比较迷糊,认为这里须要多注意。而后小疯在这里找到了一篇文章,转过来分享一下:html
数据绑定就是将各类数据与用户展示控件进行关联的过程。WPF的数据绑定机制能够以最少的代码方便地处理这样的关联。数据库
在实现这样的用户交互的时候:windows
在没有任何数据绑定机制实现的时候无非采用ide
this.nameTextBox.Text = person.Name;工具
this.ageTextBox.Text = person.Age.ToString();学习
这样的赋值的方法,而一旦数据改变了,将界面的值写回给person对象。开发工具
1.1对象改变this
咱们如今须要当对象发生改变的时候,UI的现实如何同步跟着改变呢?spa
实现起来仍是比较复杂的,看以下代码就明白了。orm
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;//INotifyPropertyChanged
namespace SimpleDataBinding
{
class Person:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string PropName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(PropName));
}
}
public Person()
{
_Age = 0;
_name = "Null";
}
private string _name;
public string Name
{
get
{ return _name; }
set
{
if (value == _name)
{ return; }
_name = value;//注意:不能用this.Name来赋值,若是这样造成循环调用,栈溢出
Notify("Name");
}
}
private int _Age;
public int Age
{
get
{ return _Age; }
set
{
if (value == _Age) return;
_Age = value;
Notify("Age");
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace SimpleDataBinding
{
/// <summary>
/// Window1.xaml 的交互逻辑
/// </summary>
public partial class WithoutDataBinding : Window
{
private Person _person;
public WithoutDataBinding()
{
InitializeComponent();
//能够采用以下对象初始化,但本示例为了使用使用属性改变UI改变就先不赋值。
//_person = new Person
//{
// Name = "zhangying",
// Age = 28
//};
_person = new Person();
_person.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "Name":
this.txt_name.Text = _person.Name;
break;
case "Age":
this.txt_age.Text = _person.Age.ToString();
break;
}
};
}
private void button2_Click(object sender, RoutedEventArgs e)
{
_person.Name = "zhangying";
_person.Age = 28;
}
}
}
1.2 UI改变
当UI的输入值发生变化的时候,如何同步对象的值呢?这须要给他们设置事件代码:
private void txt_name_TextChanged(object sender, TextChangedEventArgs e)
{
_person.Name = this.txt_name.Text;
}
private void txt_age_TextChanged(object sender, TextChangedEventArgs e)
{
int age = 0;
if(int.TryParse(this.txt_age.Text,out age)
{
_person.Age = age ;
}
}
上面的示例是使用人工的手段将对象属性与UI显示进行同步,而WPF数据绑定则是注册两个属性与数据绑定引擎,来处理同步,合适的数据转换,以下图:
2.1 Bindings
1.使用元素属性绑定:
<TextBox>
<TextBox.Text>
<Binding Path="Age" />
</TextBox.Text>
</TextBox>
2. 简化属性绑定
<TextBox Text="{Binding Path=Age}" />
更简化方式:
<TextBox Text="{Binding Age}" />
看一个更多特性的绑定方式(属性元素的方式):
<TextBox>
<TextBox.Foreground>
<Binding Path="Age" Mode="OneWay" Source="{StaticResource Tom}"
Converter="{StaticResource ageConverter}" />
</TextBox.Foreground>
</TextBox>
上面的绑定能够采用以下简洁方式:
Foreground="{Binding Path=Age, Mode=OneWay, Source={StaticResource Tom},
Converter={StaticResource ageConverter}}" />
注意上面的引号的用法和不用引号的用法,使用属性元素的方式必须用引号。
下面图表列出绑定到一个对象时候可能用到的属性:
这些属性后面将一一介绍到。
2.2 隐式数据源Implicit Data Source
在WPF中,每一个FrameworkElement 和FrameworkContentElement对象都有一个DataContex属性,该属性是Object类型,因此你能够给它指定任何类型的值。当咱们定义个对象做为绑定对象,则绑定引擎在逻辑树上寻找该数据库绑定源,以下图所示:
2.3 数据岛Data Islands
看如下代码示例就明白了:
private void btn_Click(object sender, RoutedEventArgs e)
{
Person person = (Person)this.FindResource("zy");
MessageBox.Show(string.Format("I am {0},Age is {1}",person.Name,person.Age.ToString()));
}
2.4 显示数据源Explicit Data Source
显示数据源设置对于多个数据源是很重要的,咱们能够经过绑定属性的Source来实现。
注意上面两个文本框设置了不一样的数据源的属性。
2.5 绑定到其余控件binding to other controls
以下代码所示:
<Grid>
<Slider Name="slider1" />
<TextBlock Text="{Binding Path=Value,ElementName=slider1}"/>
</Grid>
2.6 值转换 Value Conversation
上面的代码咱们发现绑定是将一个数值类型绑定到一个字符串,类型不符合,这到底是如何作到的呢?原来这是一个号称Value Converter类的功劳,它实现了IValueConverter接口,该接口有两个方法:Convert和ConvertBack。(该接口在System.Windows.Data命名空间,PresentationFramework.dll程序集中),实现 IValueConverter 接口时,最好用ValueConversionAttribute 属性来修饰此实现,以便向开发工具指示转换所涉及的数据类型,以下面的示例所示:(如下示例摘录自MSDN)
看下面的使用方法:
使用方法,先指定数据源
2.7 Editable Value Conversion
2.8 Validation
一个验证规则就是在一条目标数据更新来源数据的时候的一段数据验证代码,这段代码通常是继承ValidationRule类并覆盖Validate方法,一个内置的规则称为ExceptionValidationRule
以下代码咱们能够看到使用最基本的验证的方式:
当咱们运行程序的时候
红色边框显示输入框数据格式错误。可是咱们发现这样仍然不够友好,由于没有消息提示到底为何错误。
为了显示错误,须要使用绑定的事件来侦听ValidationError,代码示例很容易说明这点:
这样还不行,还必须以下图设置:
运行结果你们确定很明白了,固然这也许还不是咱们须要的,咱们能够更多地控制行为,这就要求咱们自定义验证规则。下面咱们以一个实例来说解自定义验证规则。
首先咱们经过继承ValidationRule类而后覆写validate方法,仍是让代码说明一切吧:
在实际项目中,可能你们对错误提示采用对话框不是很喜欢,最好的方式是WindowsForm的验证方式Tooltip,但有一点须要注意ValidateRule没有相似ValidateSucess事件让咱们清除错误的时候产生的Tooltip,以致于整个Tooltip的显示很奇怪。
2.9绑定路径句法Binding Path Syntax
如下列出MSDN上的描述:
使用 Path 属性能够指定您要绑定到的源值:
· 在最简单的状况下,Path 属性值是要用于绑定的源对象的属性名,如 Path=PropertyName。
· 在 C# 中能够经过相似语法指定属性的子属性。 例如,子句 Path=ShoppingCart.Order 设置与对象或属性 ShoppingCart 的 Order 子属性的绑定。
· 若要绑定到附加属性,应在附加属性周围放置圆括号。 例如,若要绑定到附加属性 DockPanel..::.Dock,则语法是 Path=(DockPanel.Dock)。
· 能够在要应用索引器的属性名后面的方括号内指定属性的索引器。 例如,子句 Path=ShoppingCart[0] 将绑定设置为与属性的内部索引处理文本字符串“0”的方式对应的索引。 还支持嵌套的索引器。
· 能够在 Path 子句中混合索引器和子属性;例如,Path=ShoppingCart.ShippingInfo[MailingAddress,Street].
· 在索引器内部,您能够有多个由逗号 (,) 分隔的索引器参数。 可使用圆括号指定每一个参数的类型。 例如,您能够有 Path="[(sys:Int32)42,(sys:Int32)24]",其中 sys 映射到 System 命名空间。
· 若是源为集合视图,则能够用斜杠 (/) 指定当前项。 例如,子句 Path=/ 用于设置到视图中当前项的绑定。 若是源为集合,则此语法指定默认集合视图的当前项。
· 能够结合使用属性名和斜杠来遍历做为集合的属性。 例如,Path=/Offices/ManagerName 指定源集合的当前项,该源集合包含也做为集合的 Offices 属性。 其当前项是一个包含 ManagerName 属性的对象。
· 也可使用句点 (.) 路径绑定到当前源。例如,Text=”{Binding}” 等效于 Text=”{Binding Path=.}”。
转义机制
· 在索引器 ([ ]) 内部,插入符号 (^) 用于对下一个字符进行转义。
· 若是您在 XAML 中设置 Path,则还须要使用 XAML 实体对 XAML 分析程序专用的某些字符进行转义:
· 使用 & 对字符“&”进行转义。
· 使用 > 对结束标记“>”进行转义。
· 此外,若是您使用标记扩展语法描述属性中的整个绑定,则须要使用反斜杠 \ 对 WPF 标记扩展分析程序专用的字符进行转义:
· 反斜杠 \ 自己是转义字符。
· 等号 (=) 将属性名与属性值隔开。
· 逗号 (,) 用于分隔属性。
· 右大括号 (}) 是标记扩展的结尾。
Binding the ToolTip property to the validation error message
<TextBox
Name="ageTextBox" ...
ToolTip="{Binding
ElementName=ageTextBox,
Path=(Validation.Errors)[0].ErrorContent}">
<TextBox.Text>
<Binding Path="Age">
<!-- No need for NotifyOnValidationError="true" -->
<Binding.ValidationRules>
<local:NumberRangeRule Min="0" Max="128" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
2.10相对数据源Relative Source
仍是代码来讲明更易理解
<TextBox ...
ToolTip="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}">
在上面实例中咱们采用 ElementName=txtDate这样的方式指定的,这里使用self来表示引用该标签所在的元素生成的对象,固然咱们还可使用相似于parent指向上一级,关于Relative resource在后续章节会继续讨论。
2.11更新源数据触发器Update Source Trigger
在前面咱们基本上是在目标控件的失去焦点的时候发生相关验证并更新数据源的,而实际上更新数据源的机制有多种类型,以下:
namespace System.Windows.Data
{
public enum UpdateSourceTrigger
{
Default = 0, // updates "naturally" based on the target control
PropertyChanged = 1, // updates the source immediately
LostFocus = 2, // updates the source when focus changes
Explicit = 3, // must call BindingExpression.UpdateSource()
}
}
而在XAML里面必须以下声明:
<Binding Path="Age" UpdateSourceTrigger="PropertyChanged">