Binding作为数据的桥梁,它的两端分别是Binding的源(Source)和目标(Target),用来把Source中的数据送到Target中,并把在Target中的改变返回到Source。一般情况,Binding的源是逻辑层的对象,目标是UI层的控件对象。
Binding的基本使用方法
如果想让作为数据源的对象在发生更改时自动显示到界面上,数据源的对象需要实现INotifyPropertyChanged接口。设置绑定时需要指定绑定到数据源的哪个属性,这个属性称为Binding的路径Path。通过绑定方法把Binding对象关联到目标上,同时指定把数据送达目标的哪个属性,目标的Binding属性必须为依赖属性。示例代码:
Binding binding = new Binding(); binding.Source = DateTime.Now; binding.Path = new PropertyPath("Year"); BindingOperations.SetBinding(txtBlock, TextBlock.TextProperty, binding); //txtBlock.SetBinding(TextBlock.TextDecorationsProperty, binding);
设置绑定时也可以通过FrameworkElement的SetBinding方法实现。
Binding的数据源
如果想让数据源对象更改时自动通知Binding更新UI,则需要源对象实现INotifyPropertyChanged接口。除了普通对象作为数据源外,还可以把其他控件、集合、XML数据等作为数据源。
通过标记扩展把一个控件的属性关联到另一个控件的属性上,示例代码:
<TextBox Text="{Binding Path=Value,ElementName=slider}"></TextBox> <Slider x:Name="slider"></Slider>
控制Binding数据流向的属性是Mode,类型为BindingMode枚举,TwoWay:双向流动,OneWay:由源流向目标,OneTime:只读取一次,OneWayToSource:从目标流向源,Default:根据目标的实际情况来确定,若目标是可编辑的则为TwoWay,若目标为只读的,则为OneWay。可以通过更改Binding的UpdateSourceTrigger属性来指定Target更新Source的时机。还可以为目标元素添加监听Binding的附加事件SourceUpdated和TargetUpdated来监听源和目标的更新。
当为Binding指定数据源的Path时,可以使用"."操作符,如Property.property,甚至可以使用索引器如Property.[1]或Property[1]。如果Binding的源是一个集合,如果想获取第一个元素作为Binding的Path,则使用“/”获取第一个元素,如果获取的元素仍是一个集合,则可以继续使用“/”获取集合的第一个元素,示例代码:
public class Student { public String Name { get; set; } public List<Book> Books { get; set; } } public class Book { public String BookName { get; set; } } List<Student> students = new List<Student>(); students.Add(new Student() { Name = "Jeff", Books = new List<Book>() { new Book() { BookName = "C#" }, new Book() { BookName = "Java" } } }); students.Add(new Student() { Name = "Sam", Books = new List<Book>() { new Book() { BookName = "Game" }, new Book() { BookName = "Hobbit" } } }); txtBlock1.SetBinding(TextBlock.TextProperty, new Binding("/Name") { Source = students });//Jeff txtBlock2.SetBinding(TextBlock.TextProperty, new Binding("/Books/BookName") { Source = students });//C#
如果数据源本身是基础类型(如String、int等),则无需指定Path,在C#代码中使用“.”,在XAML语法中可以不指定Path属性或也可以指定“.”。
为Binding指定源的几种方法
普通的CLR对象作为数据源,集合对象包括数组、List、ObservableCollection<T>,ADO.NET对象,XmlDataProvider,依赖对象,控件的DataContext属性,通过Binding对象的ElementName属性指定Source,通过Binding的RelativeSource属性指定Source,把ObjectDataProvider对象指定为Source,把使用LINQ检索得到的数据作为数据源。
使用DataContext作为数据源时,绑定时可以实现自动向父级控件查找数据源,直到找到包含DataContext的控件,自动查找过程是通过依赖属性实现的,DataContext为依赖属性,当控件未指定DataContext时会自动继承父级的属性。
为ItemsControl控件绑定数据源时只需把数据源对象赋值到ItemsSource属性,数据源对象应为集合对象,控件会自动为集合中的元素设置Binding。为了使集合中元素个数更改时ItemsControl对象能得到通知,集合对象必须实现IObservable接口。注意如果想实现在集合中的元素属性更改时能通知到Binding对象,集合元素必须实现INotifyPropertyChanged接口。ObservableCollection<T>为实现了IObservable的集合。
使用ADO.Net的DataTable对象作为ItemsControl的数据源时,把DataTable的DefaultView属性赋值给ItemsSource。通常用ListView来显示表格数据,同时需要设置ListView的View属性为GridView,GridView的内容为GridViewColumn,GridViewColumn的DisplayMemberBinding属性为一个Binding对象,通过设置该对象来指定该列与Source对象的哪个属性关联,这与ListBox不同。
如果需要把XML数据作为Binding的源,需要使用XMLDataProvider对象,设置Binding的路径时需要使用Xpath设置,使用@符号时表示获取XML元素的Attribute,不使用时表示获取元素的子级元素。
ObjectDataProvider用来包装一个以方法暴露数据的对象,通过设置ObjectInstance指定被包装的对象,MethodName指定要调用的方法,MethodParameters指定方法的参数。当把ObjectDataProvider绑定到Target上时不需要设置Binding的Path属性,ObjectDataProvider本身就是数据。
使用Binding的RelativeSource用来指定相对数据源,通过RelativeSource类的属性设置控制Binding搜索Source的范围和方式。Mode属性的类型是RelativeSourceMode,取值为PreviousData、TemplatedParent、Self、FindAncestor。可以通过静态属性PreviousData、TemplatedParent和Self快速获取RelativeSource的实例。
Binding的数据校验
Binding的ValidationRules属性用来控制数据校验,可以同时设置多个校验对象。每个校验规则为ValidationRule对象,ValidationRule是抽象类,使用时需要实现自己的派生类。Binding的校验默认只校验来自Target的数据,即默认Source中的数据永远是正确的,如果想在Source更新时也进行校验,需要设置ValidationRule的属性ValidatesOnTargetUpdated为True,这样在Source的数据更新时也会调用校验方法。如果想在校验错误时触发事件,需要设置Binding的NotifyOnValidationError属性为true,并在Binding的Target上或其父级上监听Validation的Error附加事件。示例代码:
this.textBlock1.AddHandler(Validation.ErrorEvent, new RoutedEventHandler(ValidationError)); private void ValidationError(Object sender,RoutedEventArgs e) { if(Validation.GetErrors(textBlock1).Count>0) { textBlock1.ToolTip = Validation.GetErrors(textBlock1)[0].ErrorContent.ToString(); } }
Binding的数据转换
当Binding的Source的Path属性的值类型与Target的属性值类型不一致时,要进行类型转换。如果是简单类型如String,Double等,在WPF中已经内置转换方法,如果是其他复杂类型,需要自己编写转换类。实现IValueConverter接口,该接口定义两个方法,当数据从Binding的Source流向Target时Convert方法被调用,反之ConvertBack方法被调用。Binding的Mode影响着方法被调用的情况,如果Mode为TwoWay,两个方法都有可能被调用,如果Mode为OneWay,则只有Convert被调用。创建绑定时设置Binding的Converter属性指定Binding要使用的转换器。
MultiBinding
当UI中需要显示的信息由不只一个数据来源决定时,这时候需要使用多路绑定。MultiBinding与Binding一样以BindingBase为基类,MultiBinding有一个类型为BindingBase集合的属性Bindings,通过该属性把多个Binding汇集到一起。汇集起来的Binding共同决定去往MultiBinding的Target的数据。MultiBinding对于添加子集Binding的顺序是敏感的,添加顺序决定传到Converter中的数据的顺序。MultiBinding的Converter实现的是IMultiValueConverter接口,MultiBinding貌似必须要指定自定义的Converter。