首页 > 代码库 > 数据绑定

数据绑定

1. 数据绑定(Binding)一般配置

常用的绑定的目标(Dependency Object & associated Dependency Property,目标单元): 
内容控件目标:object ContentControl.Content 
集合控件目标:IEnumerable ItemsControl.ItemSource

绑定器: 
Binding ( : BindingBase : MarkupExtension ) 
Binding binding = new Binding(); 
binding.ElementName = "WPF控件名称";  / binding.Source = …; 
binding.Path = new System.Windows.PropertyPath("路径串");

绑定到源(Object & its Property): 
代码动态绑定:BindingExtensionBase FrameworkElement.SetBinding(DependencyProperty dp,  BindingBase binding)  (dp = ContentControl.ContentProperty, ItemsControl.ItemsSourceProperty, …) 
              
XAML举例:<Label Content=”{Binding … Path=…}”…> ,  <ListBox ItemsSource=”{Binding … Path=…}"> … 
({Binding}中单独出现Path时,“Path=”可以省略)

取消绑定:BindingOperations.ClearBinding(this.Label1, ContentControl.ContentProperty);

使用DataContext: 
设置某个单元的FrameworkElement.DataContext属性,为其子单元提供默认的绑定源(Source,ElementName,RelativeSource未设置的Binding),可通过代码和XAML设置。

设置绑定模式: 
Default - 采用目标使用的默认方式 
OneTime - 仅在程序启动或数据源引用或context变动时候更新,但源本身的值发生变化不触发更新 
OneWay - 仅目标因源而改变(实验中发现,有的时候目标因用户修改,例如TextBox,可能不会再接受源的更改) 
OneWayToSource - 仅源因目标而改变 
TwoWay - 双向变化 
源更新: 
在上述使用向源反馈更新模式时,设置Binding.UpdateSourceTrigger决定更新的频率: 
Explicit - 仅在BindingExpression.UpdateSource()时更新,BindingExpression通过这个形式获取:textBox.GettBindingExpression(TextBox.TextProperty); 
LostFocus - 在包含目标的单元失去焦点时更新; 
PropertyChanged - 每当目标发生变化时; 
Default - 根据目标的特性的默认值。

2. 绑定到Object

用Binding.Source而不是Binding.ElementName,Binding.Path通常忽略。 
常用的如画刷设置,以下示例静态对象引用格式: 
<Button Background=”{Binding Source={x:Static SystemColors.WindowColor}}” …

另一种应用是绑定到逻辑资源: 
在资源中定义某个类型的一个资源<名空间别名:类名 x:Key="对象名称"> 
绑定:Content=”{Binding Source={StaticResource 对象名称}, Path=对象属性}"… 
其中名空间别名定义: xmlns:别名="clr-namespace:工程代码中的名空间名称[;assembly=库名]"

3. 绑定到WPF控件

ElementName设为组件名字符串;Path设为表示值的组件的变量。

4. 绑定到相对位置

Binding中的源采用RelativeSource。 
相对搜索模式: 
FindAncestor模式从上级搜索源,用AncestorType指定源类型,AncestorLevel指定搜索个数; 
TemplatedParent模式用于指向当前模板应用的对象; 
Self通常完成自身两个属性的绑定; 
PreviousData绑定到数据列表中的前一个数据项。

5. 绑定到List

ItemsControl.ItemsSource设置到实现IEnumerable的对象,格式:{Binding Source=主表  Path=xxx},Path用于指定在多层表中指定中间路径,用‘.‘分隔,CurrentItem指某级的当前项。 
ItemsControl.IsSynchronizedWithCurrentItem将选定项和CurrentItem同步。 
ItemsControl.DisplayMemberPath用于指定作为最终显示的字段的属性。 
ItemsControl.ItemTemplate用于指定数据模板(Data Template)。内容显示由ItemsControls.DisplayMemberPath或ItemsControl.ItemTemplate内部定义(见“数据模板”)两者之一决定。

内容控件的Content绑定到列表项的某个属性,则需将Path设置到该属性(多层表用‘.‘分隔,CurrentItem指某级当前项)

使用ICollectionView检视列表数据(命名空间System.ComponentModel) 
ICollectionView   o-- 
DispatcherObject   -- CollectionView – ListCollectionView 
                                     - BindingListCollectionView 
ICollectionView icv = CollectionViewSource.GetDefaultView(myCollection); 
(如果是IBindingList则返回IBindingListCollectionView,是IList则返回IListCollectionView) 
icv可以用于浏览,icv.CurrentItem指向当前项(目前未发现能实现多级表)

6. 绑定到ADO.NET

用{Binding Path=xx/yy/zz (Source=tt)}模式。 
Data Set:首先调用Adapters的Fill将DataSet对象中的DataTable,DataContext可以设到DataSet或其下辖DataTable,在Path中相应调整即可。 
多层级目标:无当前项指标(CurrentItem),只需表示层级路径即可。 
关联数据表:将父表设IsSynchronizedWithCurrentItem="True",关联属性表现在Path中,将目标字段表现在DisplayMemberPath,这样关联即显示在界面上。关联设置在VS中导入的XSD文件上进行,设置父子表和相应的关键栏和外部关键键栏。

7. 绑定到ObjectDataProvider

将WPF单元内容绑定到一个对象的方法的返回值,主要需要设置ObjectDataProvider.ObjectType到对象类型,ObjectDataProvider.MethodName到方法名称,MethodParameters,ConstructorParameters属性用来指定方法参数和对象构造函数参数。在XAML中格式: 
在资源(xxx.Resources)中定义: 
<ObjectDataProvider x:Key=”myDataProvider” ObjectType={x:Type 名空间:类名}" MethodName=”…” /> 
引用: {Binding Source={StaticResource myDataProvider}} 
此种方式始终只读,即不允许改变数据源。

8. 绑定到XML

在资源(xxx.Resources)中定义: 
<XmlDataProvider x:Key=”…” Source=”xxx.xml”/> 
或者: 
<XmlDataProvider x:Key=”…”> 
<x:XData> 
   直接嵌入XML数据 
</x:XData> 
</XmlDataProvider> 
引用时类似7将Source指向资源。

在多层级列表模式下,DataContext=”{Binding …}" 用于定位。一般在全局区(如Grid),DataContext的Binding设置Source,并用XPath过滤掉不需要考虑的高层级。 
在下一级,DataContext的Binding设置Path=CurrentItem,如果是二级以上,必须设ElementName以指向上一列表控件,而Path=SelectedItem,这样将上一级的选定项设为本级的Context。而ItemsSource的Binding则用XPath进行过滤修正,而最终显示项则取决于ItemsControls.DisplayMemberPath或ItemsControl.ItemTemplate两者之一。对XML中的属性内容在路径中用@propName格式。以下举例说明ItemTemplate的使用: 
<ListBox.ItemTemplate> 
  <DataTemplate> 
    <TextBlock Text=”{Binding XPath=@propName}” />  
  </DataTemplate> 
</ListBox.ItemTemplate>

9. 数据模板(Data Templates)

上一节已经涉及了数据模板的使用。数据模板用于定制数据的显示。 
列表控件:设置在ItemsControl.ItemTemplate(DataTemplate类型)上。ItemTemplate顾名思义,对应一个列表项的显示,因此可以通过适当组织将一个列表项对应多个数据字段。 
内容控件:设置在ContentControl.ContentTemlate。 
数据模板的类型均为DataTemplate,显然内容也就定义在<DataTemplate></DataTemplate>中,是一个DependencyObject。涉及元素项内容通常用{Binding Path=…}(XML用XPath)绑定。 
在资源中定义数据模板:类似一般资源,在DataTemplate上标记x:Key,在使用的时候用ItemTemplate="{StaticResource xxx}"引用。

10. 数据排序(Sorting)

1. 用CollectionViewSource.GetDefaultView(…)先获取一个ICollectionView(使用命名空间System.ComponentModel) 
2. 在SortDescriptionCollection ICollectionView.SortDescriptions上调Add方法,添加new SortDescription("字段名称", ListSortDirection.Ascending(升序)) 
3. 可以继续添加SortDescription,根据添加先后确定排序优先级。

自定义排序: 
对于默认生成ListCollectionView(即实现IList,但非IBindingList,如ObservableCollection),通过设置IComparer ListCollectionView.CustomSort属性可以应用自定义排序。IComparer需要实现的方法是:int Compare(object x, object y)

在XAML中直接定义CollectionViewSource: 
1. <CollectionViewSource x:Key=’somekey’ (这个Source定义成资源) 
                                     Source=“{Binding xxx}” (绑定到源列表,类似GetDefaultView的参数,XML可设XPath) 
2. 随后可以定义<CollectionViewSource.SortDescriptions>或以下的<CollectionViewSource.GroupDescriptions>中的内容,类似于上述Add方法。 
3. 后续依赖于源列表和这个基于CollectionViewSource的分组或排序定义的单元可以直接将Binding的源定义到上面的Key。

11. 数据编组(Grouping)

编组即排序加区分。 
1. …获取一个ICollectionView 
2. 在ObservableCollection<GroupDescription> ICollectionView.GroupDescriptions上调Add方法,添加new PropertyGroupDescription("字段名"[, CustomGrouper]),其中PropertyGroupDescription 继承自GroupDescription,自定义编组需CustomGrouper实现IValueConverter接口。 
3. 设置列表控件的组的显示特性ItemsControl.GroupStyle(如<ListBox.GroupStyle>)。GroupStyle有五个主要属性,常用: 
HeaderTemplate,它是一个控制组表头的显示的DataTemplate,其{Binding Path=xxx},实际绑定到PropertyGroupDescription构造函数第一个参数指定的属性(在某些情况下可忽略为{Binding}); 
Panel,它是一个设置列表元素布局的模板,ItemsPanelTemplate类型,注意这个Panel是以Group为单位进行布局的,而不是直接针对Item; 
ContainerStyle,Style类型,用于指定每个具体的编组元素(TargetType为GroupItem)的样式。有关Template使用详见后续章节。

DispatcherObject - FrameworkTemplate – DataTemplate 
                                     - ItemsPanelTemplate 
自定义编组: 
IValueConverter需要实现object Convert(object value, Type targetType, object parameter, Sytem.Globalization.CultureInfo culture)和同参数的ConvertBack函数,在编组中ConvertBack函数不被调用。Convert方法完成PropertyGroupDescription指定的数据转换,转换输出的内容被作为最终分组的依据,通常可以进行字符串格式化操作和一些映射等(详见13节)。

12. 数据过滤(Filtering)

根据Predicate委托类型:bool Predigate<T>(T obj),实现一个过滤器,设置ICollectionView.Filter属性到这个方法上,只有通过过滤(返回true)的才会包含进来并显示。

在使用ADO.NET或使用BindingListCollectionView时,则不能用上述过滤方法。而要用BindingListCollectionView.CustomFilter字符串,通常形如"字段名==‘值‘"。具体格式同DataColumn.Expression,非WPF关注内容,参见相关文档。

13. 数据转换

如11所述,IValueConverer数据转换可用于编组,需要实现的方法也已介绍。 
通常定义实现IValueConverter的类之前需要标志[ValueConversion(typeof(输入类型), typeof(输出类型)]修饰,一般不需要实现ConvertBack方法的实际功能。 
在XAML中使用转换器: 
1. 用xmlns引用必要的名空间。 
2. 在资源如<Window.Resources>中实例化一个转换器(用x:Key标识)。 
3. 在用到数据绑定Binding的地方,设置Binding的Converter属性引用资源中这个转换器。

字符串格式化(详见相关文档)。常用: 
数值到货币:num.ToString("C"); (这个转换基于当前线程的文化信息) 
日期格式化:d-短日期格式,D-长日期格式,f-长日期短时间格式,F-长日期长时间格式,G-一般格式,M-日月,s-ISO标准格式

提取对象。例如: 
从URI字符串提取图像。

个性化列表项: 
例如设置转换器将输入日期转换到一个画刷,将转换器设置到列表项DataTemplate中单元(如Label)的Foreground的Binding上,就实现不同日期的不同颜色显示。

转换器的本地化: 
Convert方法中有一个文化信息参数culture,据此可以对转换过程做适当的处理,例如调用相应的翻译器等。

多参数转换: 
实现接口IMultiValueConverter,相应的转换方法: 
object Convert(object[] values, Type targetType, 后同); 
object[] ConvertBack(object value, Type[] targetTypes, 后同); 
多参数转换也是在XAML的资源中实例化转换器,然后在MuliBinding中使用: 
<MultiBinding Converter="资源"> 
  <Binding Path=xxx1 /> 
  <Binding Path=xxx2 /> 
</MultiBinding>

14. 数据验证(Validation)

数据验证在界面上指示数据的有效性。 
它通过Collection<ValidationRule> Binding.ValidationRules包含的验证规则实现,验证过程根据定义依次执行: 
<Binding.ValidationRules> 
  <ns:ValidationRule派生类1/> 
  <ns:ValidationRule派生类2/> 
  .. 
</Binding.VaidationRules>

派生类必须重载:ValidationResult Validate(object value, CultureInfo ci);  
ValidationResult构造:ValidationResult(bool isvalid, object 错误内容对象(通常是消息串,无错误设null));

当验证发生错误时会发生(不会触发任何异常): 
1. 相应的单元周围出现红色框; 
2. 单元上的关联属性Validation.HasErrorProperty变为True; 
3. 单元上的关联属性Validation.ErrorsProperty中添加相应的ValidationError对象; 
4. 如果相应Binding.NotifyOnValidationError设为True,则接力事件Validation.ErrorEvent被触发; 
5. 相应的数据源不会更新到错误值。

通过添加ExceptionValidationRule到ValidationRules中,可以使Binding中的任何异常都转变为上报验证错误。

处理验证错误: 
Validation.ErrorEvent的事件参数是ValidationErrorEventArgs,包含两个属性: 
Action: 触发后首先是ValidationErrorEventAction.Added,当再次验证的时会首先清除前次的错误,再次触发事件,并使Action参数为ValidationErrorEventAction.Removed; 
Error: 包含以下信息: 
- object ErrorContent:  ValidationRule对象返回ValidationResult中设置的对象 
- Exception Exception: 导致验证错误的异常(使用上述ExceptionValidationRule) 
- BindingExpression BindingInError: 获得导致验证错误的Binding对应的BindingExpression,通过BindingExpression.ParentBinding可以得到Binding。 
- RuleInError:获得对应的验证规则。

15. 数据变更通知(Data Change Notification)

自定义的数据源的数据更新往往通过: 
1. 单独的数据对象,实现INotifyPropertyChanged接口实现, 
在参数设置set过程中,调用PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("被改变的属性名"); 
2. 集合数据对象,继承ObservableCollection,可直接依赖其内建的变更通知体系,则列表内容增删变更都会通知。一般形式: 
class MyItems : System.Collections.ObjectModel.ObservableCollection<MyItem> { … } 
集合元素的对象必须实现INotifyPropertyChanged以完成自身的变更通知。

当显式设置Binding.NotifyOnSourceUpdated=”True”时,数据变更会触发接力事件Binding.SourceUpdatedEvent。

 

数据绑定