5-2.Binding指定源
指定源的集中方法:
- 普通对象
- 集合
- ADO.NET
- XML
- 依赖对象
- DataContext
- 通过ElementName
- 通过RelativeSource
- ObjectDataProvider
- Linq
DataContext作为源
DataContext是WPF控件的基类属性,也就是WPF树形结构都具有这个属性,当一个Binding只有Path而没有Source时,会沿着UI元素树一路向树的根部搜索过去,看哪个节点的DataContext具有Path所指向的属性,如果有就把这个对象作为自己的Source,如果没有就一直找下去。
如果不需要数据时,Source属性也可以直接不写Text="{Binding Path=Name}"
自动向根节点查询的原理:DataContext是一个依赖属性,依赖属性的特点是当没有为控件的某个属性显式赋值时,控件会把自己容器的属性当做自己的属性值,也就是属性值沿着UI元素向下传递了。
使用场景:
- UI上多个控件关注同一对象
- 当Source的对象不能被直接访问时,窗体B想访问窗体A的控件,但是窗体A的控件是Private,这时可以把窗体A的控件作为A的DataContext,因为DataContext属性是Public。
使用集合对象作为列表控件的源
只要为一个ItemsControl对象设置了ItemsSource,就会自动迭代其中的数据元素,并为每个元素准备一个条目容器(条目容器就是数据的外衣),并使用Binding在条目容器和数据元素之间建立联系。
1 2 3 4 5
| this.listBoxStudents.ItemsSource = stuList; this.listBoxStudents.DisplayMemberPath="Name";
|
以上创建Binding的过程是在DisplayMemberTemplateSelector类中的SelectTemplate方法中完成的,该方法的返回值为DataTemplate类型,ListBox的ItemTemplate属性的类型是DataTemplate。
自定义设置DataTemplate案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <StackPanel> <TextBlock Text="id"/> <TextBox x:Name="txtBoxId" Text="{Binding Path=SelectedItem.Id, ElementName=listBox}"/> <TextBlock Text="student List"/> <ListBox x:Name="listBox"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Id}"/> <TextBlock Text="{Binding Name}"/> <TextBlock Text="{Binding Age}"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </StackPanel>
|
在构造函数中指定源this.listBox.ItemsSource = stuList;

在使用集合作为列表的ItemsSource时,一般考虑用ObservableCollection<T>代替List<T>
,因为ObservableCollection<T>
实现了INotifyCollectionChanged和INotifyPropertyChanged接口,能把集合的变化立刻通知控件进行显示。
ADO.NET对象作为源
1 2 3 4 5 6 7
| this.listView.ItemsSource = stuList; DataTable dt = Load(); this.listView.ItemsSource = dt.DefaultView;
|
1 2 3 4 5 6 7 8 9 10 11
| <StackPanel> <ListView x:Name="listView"> <ListView.View> <GridView> <GridViewColumn Header="Id" Width="60" DisplayMemberBinding="{Binding Id}"/> <GridViewColumn Header="Name" Width="60" DisplayMemberBinding="{Binding Name}"/> <GridViewColumn Header="Age" Width="60" DisplayMemberBinding="{Binding Age}"/> </GridView> </ListView.View> </ListView> </StackPanel>
|

ListView是ListBox的派生类,ListView的View属性是一个ViewBase类型(GridView的基类)。
GridView的内容属性是Columns(GridViewColumnCollection类型对象),GridViewColumn对象一个重要属性是DisplayMemberBinding,功能类似于ListBox的DisplayMemberPath属性。如果用更复杂的结构来表示Header和数据,可以为GridViewColumn设置HeaderTemplate和CellTemplate属性,类型都是DataTemplate。
XML作为源
使用XML数据作为Binding的Source时要使用XPath属性而不是Path属性来指定数据的来源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <StudentList> <Student Id="1"> <Nmae>Tim</Nmae> </Student> <Student Id="2"> <Nmae>Tom</Nmae> </Student> <Student Id="3"> <Nmae>Vina</Nmae> </Student> </StudentList>
<StackPanel> <ListView x:Name="listView"> <ListView.View> <GridView> <GridViewColumn Header="Id" Width="80" DisplayMemberBinding="{Binding XPath=@Id}"/> <GridViewColumn Header="Name" Width="80" DisplayMemberBinding="{Binding XPath=Name}"/> </GridView> </ListView.View> </ListView> </StackPanel>
|
1 2 3 4 5 6 7 8 9 10
| XmlDocument doc = new XmlDocument(); doc.Load(xmlPath);
XmlDataProvider xdp = new XmlDataProvider(); xdp.Document = doc;
xdp.XPath = @"StudentList/Student"; this.listView.DataContext = xdp; this.listView.SetBinding(ListView.ItemsSourceProperty, new Binding());
|
也可以把XML数据和XMLDataProvider对象直接写在XAML代码中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <Window.Resources> <XmlDataProvider x:Key="xdp" XPath="FileSystem/Folder"> <x:XData> <FileSystem xmlns=""> <Folder Name="PRO"> <Folder Name="Win"> <Folder Name="A"/> <Folder Name="A2"/> <Folder Name="A3"/> <Folder Name="A4"/> </Folder> </Folder> <Folder Name="PRO1"> <Folder Name="Win1"> <Folder Name="A1"/> <Folder Name="A12"/> <Folder Name="A13"/> <Folder Name="A14"/> </Folder> </Folder> </FileSystem> </x:XData> </XmlDataProvider> </Window.Resources> <StackPanel> <TextBlock Text="ok"/> <TreeView ItemsSource="{Binding Source={StaticResource xdp}}" > <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding XPath=Folder}"> <TextBlock Text="{Binding XPath=@Name}"/> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> </StackPanel>
|

LINQ结果作为源
Linq查询结果是一个IEnumberable<T>
,可以作为列表控件的ItemsSource使用。
this.listView.ItemsSource = from stu in stuList where stu.Name.StartsWith("T") Select stu;
ObjectDataProvider作为源
当需要的数据没有被暴露出来,如设置了private,此时就需要使用ObjectDataProvider,它把对象作为数据源提供给Binding,前面用的XmlDataProvider和ObjectDataProvider的父类都是DataSourceProvider抽象类。
1 2 3 4 5
| <StackPanel> <TextBox x:Name="txtArg1"/> <TextBox x:Name="txtArg2"/> <TextBox x:Name="txtResult"/> </StackPanel>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public class Calculator { public string Add(string a,string b) { return a + b; } }
private void SetBindging1() { ObjectDataProvider odp = new ObjectDataProvider(); odp.MethodName = "Add"; odp.MethodParameters.Add("0"); odp.MethodParameters.Add("0");
Binding bindingToArg1 = new Binding("MethodParameters[0]") { Source = odp, BindsDirectlyToSource =true, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }; Binding bindingToArg2 = new Binding("MethodParameters[1]") { Source = odp, BindsDirectlyToSource = true, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
Binding bindingToResult = new Binding(".") { Source = odp };
this.txtArg1.SetBinding(TextBox.TextProperty, bindingToArg1); this.txtArg2.SetBinding(TextBox.TextProperty, bindingToArg2); this.txtResult.SetBinding(TextBox.TextProperty, bindingToResult); }
|

- 将枚举作为Combobox的ItemsSource的案例
1 2 3 4 5 6 7 8 9 10 11
| <ObjectDataProvider x:Key="CombinationTypeEnum" MethodName="GetValues" ObjectType="{x:Type sys:Enum}"> <ObjectDataProvider.MethodParameters> <x:Type TypeName="model:CombinationType" /> </ObjectDataProvider.MethodParameters> </ObjectDataProvider>
<ComboBox ItemsSource="{Binding Source={StaticResource WorkConditionGradeEnum}}"/>
|
上面ObjectDataProvider对应的C#代码为Array a= System.Enum.GetValues(typeof(CombinationType));
一般情况下,认为数据从哪里来,哪里就是source,到哪去哪里就是Target。所以会认为前两个TextBox应该是ObjectDataProvider的数据源,但实际上,3个TextBox都是以ObjectDataProvider为数据源,前两个TextBox只是在数据流上做了限制。
使用Binding的RelativSource
当不确定源的名字,但是知道源和目标在UI上的相对关系,这时便可以使用RelativeSource属性。
RelativSource属性的数据类型为RelativeSource,可以通过这个类控制搜索相对数据源的方式。
1 2 3 4 5 6 7 8 9
| <Grid x:Name="g1" Background="Red" Margin="10"> <DockPanel x:Name="d1" Background="Gray" Margin="10"> <Grid x:Name="g2" Background="Blue" Margin="10"> <DockPanel x:Name="d2" Background="Yellow" Margin="10"> <TextBox x:Name="txtBox" Background="Green" Margin="10"/> </DockPanel> </Grid> </DockPanel> </Grid>
|

1 2 3 4 5
| RelativeSource rs = new RelativeSource(RelativeSourceMode.FindAncestor); rs.AncestorLevel = 1; rs.AncestorType = typeof(Grid); Binding binding = new Binding("Name") { RelativeSource = rs }; this.txtBox.SetBinding(TextBox.TextProperty, binding);
|
在XAML的等效代码
<TextBox Text="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Grid},AncestorLevel=1},Path=Name}"/>

关联自身Name属性
1 2 3
| RelativeSource rs = new RelativeSource(RelativeSourceMode.Self); Binding binding = new Binding("Name") { RelativeSource = rs }; this.txtBox.SetBinding(TextBox.TextProperty, binding);
|
