DotNet关键知识点——WPF篇(五)

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。

参考资料:
[1] http://msdn.microsoft.com/library/ms752347.aspx

posted @ 2009-11-28 21:56  quanben  阅读(236)  评论(0编辑  收藏  举报