1. 绑定对象
数据绑定是通过System.Windows.Data.Binding对象来实现,它能够将2个属性绑定在一起,在接下来的应用程序生命周期中让绑定来负责所有的同步工作。
1.1 在代码中实现
绑定代码
Binding binding = new Binding();
//设置源对象
binding.Source = treeview;
//设置源属性
binding.Path = new PropertyPath("SelectedItem.Header");
//添加到目标属性
currentFolder.SetBinding(TextBlock.TextProperty, binding);
解除绑定代码
BindingOperation.ClearBinding(currentFolder, TextBlock.TextProperty); //清除currentFolder的TextBlock
BindingOperation.ClearAllBindings(currentFolder) //清除所有和currentFolder有关的绑定
1.2 在XAML中使用绑定
<TextBlock x:Name="currentFolder" DockPanle.Dock="Top" Text="{Binding SelectedItem.Header, ElementName=treeview}"/>
1.3 与普通的.NET属性绑定
可以绑定.NET的对象的普通属性,如:
<Label x:Name="numItemsLabel" Content="{Binding Source={StaticResource photoes}, Path=Count}" DockPanel.Dock="Bottom"/>
虽然可以用,但是有弊端。因为普通的.NET对象属性和WPF中的依赖属性相比, 没有自动的变更机制支持。
为了让目标和源属性保持同步,源对象必须实现以下技术中的一种:
- 实现System.ComponentModel.INotifyPropertyChanged接口,该接口有一个PropertyChanged事件。
- 实现XXXChanged事件,其中XXX是值发生改变的属性的名称。
常用的是第一种,WPF对其进行了优化。
1.4 绑定到整个对象
绑定中的Path是可选的,你可以绑定整个对象。
这种绑定方式对于那些能够充分利用对象的元素,比如ListBox, 是非常有用的。
1.5 绑定到集合
浅数据绑定
例如,绑定一个对象到ListBox, 但是不能绑定到Items属性, 因为Items不是依赖属性。
所以我们使用ListBox.ItemSource属性
改进显示方式
默认绑定集合的显示方式,用ToString呈现,并不能让人接受。可以使用DisplayMemberPath属性和ItemSource配合。
<ListBox x:Name="pictureBox" DisplayMemberPath="Name" ItemSoruce="{Binding Source={StaticResource photos}}"
</ListBox>
管理选中项
<ListBox IsSynchronizedWithCurrentItem="True" DisplayMemeberPath="Name" ItemSource="{Binding Source={StaticResource photos}}"></ListBox>
<ListBox IsSynchronizedWithCurrentItem="True" DisplayMemeberPath="DateTime" ItemSource="{Binding Source={StaticResource photos}}"></ListBox>
<ListBox IsSynchronizedWithCurrentItem="True" DisplayMemeberPath="Size" ItemSource="{Binding Source={StaticResource photos}}"></ListBox>
因为设置了IsSynchronizedWithCurrentItem为True, 所有指向同一个源的的控件,改变任何一个控件的选中项都会同时改变其他两个控件。
1.6 使用DataContext共享数据源
使用DataContext指定共享数据源,下面的数据绑定,就可以简化。
<StackPanel x:Name="parent" DataContext="{StaticResource photos}">
<ListBox IsSynchronizedWithCurrentItem="True" DisplayMemeberPath="Name" ItemSource="{Binding}"></ListBox>
<ListBox IsSynchronizedWithCurrentItem="True" DisplayMemeberPath="DateTime" ItemSource="{Binding}"></ListBox>
<ListBox IsSynchronizedWithCurrentItem="True" DisplayMemeberPath="Size" ItemSource="{Binding}"></ListBox>
</StackPanel>
2. 控制呈现
WPF中使用数据模板和值转换器来控制控件的呈现
2.1 使用数据模板
很多控件都提供一些属性可以用来添加数据模板。例如ContentControl的ContentTemplate属性, ItemControl的ItemTemplate属性,HeaderedContentControl的HeaderTemplate属性。
<ListBox x:Name="pictureBox" ItemSource="{Binding Source={StaticResource photos}}">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Path=FullPath}" Height="35"/>
<DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
2.2 使用值转换器
对不兼容的数据类型做桥接
在数据绑定的时候,我们常常不能够直接使用,而需要转换。
比如实现一个bug记录软件,当bug数目超过600的时候,显示bug的数字背景要用红色。颜色的属性如何和bug数目绑定呢?
可以用值转换器来实现
<Label Background=“Binding Path=Count,Converter={StaticResource myConverter}, Source={StaticResource photos}”/>
实现的值转换器必须实现IValueConverter接口。IValueConverter接口有两个简单的方法——Convert和ConvertBack.
自定义数据显示
有时候虽然不存在数据类型不兼容的情况,但是我们可以使用值转换器来达到让结果更加易读的目的。
3. 定制一个集合的视图
事实上,无论何时绑定到一个集合(任何实现了IEnumerable的类), 都会有一个默认的视图隐式地被插入到源对象和目标对象之间。
该视图(它是一个实现了ICollectionView接口的对象)存储着当前项的信息,但是它也支持排序、分组、过滤和项导航。
3.1 排序
ICollectionView有一个SortDescriptions属性提供了一种方式来控制视图项如何进行排序。
//获取默认视图
ICollectionView view = CollectionViewSource.GetDefaultView(this.FindResource("photos"));
//清空以后的排序
view.SortDescriptions.Clear();
//添加排序
view.SortDescriptions.Add(new SortDescription('Name', ListSortDirection.Ascending));
3.2 分组
ICollectionView有一个GroupDescription属性,用它可以把源集合的项放入组和子组中。
3.3 过滤
属性Filter是一个委托,返回bool类型。
3.4 导航
view支持导航数据的能力,提供了如下这些方法和属性
IsCurrentBeforeFirst, IsCurrentAfterLast
MoveCurrentToPrevious(), MoveCurrentToLast() 等
3.5 使用其他视图
可以显式手动创建视图,而不是使用默认的视图。 下面是用CollectionViewSource创建的一个复杂的视图。
<Window.Resources>
<local:Photos x:Key="photos"/>
<CollectionViewSource x:Key="ViewSource" Source="{StaticResource photos}" Filter="viewSource_Filter" Source="{StaticResource photos}">
<CollectionViewSource.SortDescriptions>
<componentModel:SortDescription PropertyName="DateTime" Direction="Descending"/>
</CollectionViewSource.SortDescriptions>
<CollectionViewSource.GroupDescriptions>
<PoropertyGroupDescription PropertyName="DateTime"/>
</CollectionViewSource.GroupDescriptions>
</WIndow.Resources>
4. 数据提供程序
源对象可以是任意的.NET对象,所以你有足够的代码来执行任何数据绑定。
所以我们只要实现一个合适的.NET对象,可以提供正确的属性集合和通知机制,就可以更加方便的构造自己的数据源。
为了方便,WPF提供了2个类XamlDataProvider和ObjectDataProvider
4.1 XMLDataProvider
一个例子
<Window.Resource>
<XmlDataProvider x:Key="dataProvider" XPath="GameStats">
<x:XData>
...................
</x:XData>
</XmlDataProvider>
</Window.Resource>
<ListBox ItemsSource="{Binding Source={StaticResource dataProvider}, XPath=GameStat/HighScore}"
这里dataProvider是一个Windows Resource的key, 这个资源是一段xml文本, XPath指出了数据绑定的节点路径。
4.2 ObjectDataProvider
我们已经实现了使用.NET对象作为绑定的数据源了,那么使用ObjectDataProvider的意义在哪里呢?
它可以实现:
- 声明式地使用带参数的构造函数实现实例化源对象。
- 绑定到源对象的一种方法。
- 有更多的选项来做异步数据绑定。
在XAML中使用带参数的构造函数
<ObjectDataProvider x:Key="dataProvider" ObjectType="{x:Type local:Photos}">
<ObjectDataProvider.ConstructorParameters>
<sys:Int32>23</sys:Int32>
</ObjectDataProvider.ConstructorParameters>
</OjbectDataProvider>
ObjectProvider可以是一个方法
<ObjectDataProvider x:Key="dataProvider" OjbectType=“{x:Type local:Photos}" MethodName="GetFolderName"/>
5. 高级主题
5.1 自定义Binding的数据流
可以通过设置Binding的Mode属性来改变绑定的数据流。
Mode可以是BindingMode枚举值:
- OneWay, 当源改变时,目标会被更新。
- TwoWay, 源或目标改变,都会导致另一方一起被更新。
- OneWayToSoruce, 和OneWay相反,当目标改变时,源都会被更新。
- OneTime, 和OneWay一样工作,但源的改变不会被反映到目标上。目标会保留Binding初始化时源的一个快照。
TwoWay绑定适合于数据绑定表单, 因为在表单中你可能会有一些运行用户修改数据的TextBox, 且这些TextBox已经被填入了数据。
事实上,虽然大多数依赖属性默认都是OneWay绑定,但像TextBox.Text这样的依赖属性是TwoWay绑定的。
5.2 向绑定添加验证规则
我们可以在绑定的时候,添加验证规则。
验证规则类需要继承ValidationRule, 重写方法ValidationResult
<TextBox>
<TextBox.Text>
<Binding ...>
<Binding.ValidationRules>
<local:JpgValicationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>