WPF入门教程系列(二) 深入剖析WPF Binding的使用方法
WPF入门教程系列(二) 深入剖析WPF Binding的使用方法
同一个对象(特指System.Windows.DependencyObject的子类)的同一种属性(特指DependencyProperty)只能拥有一个binding。
这一点可以通过设置binding对象的方法名得知:
public static BindingExpressionBase SetBinding(
DependencyObject target,
DependencyProperty dp,
BindingBase binding
)
方法名是SetBinding而不是AddBinding。如果想要验证一下,也可以在listView1_SelectionChanged事件方法中增加断点,可以监视到。
当多次在同一个对象上设置Binding时,其实并不会增加多余的binding,而是将原来的binding替换掉了,这里以textBox_ContactID为例如下图:

另一个相对的概念:同一个Binding可以同时与多个对象的多个属性(或同一对象的多个属性)形成Binding。这一点就不做实践验证了,在MSDN上《Data Binding Overview》中有一段“Binding and BindingExpression”就是讲这个道理的。
也许很多读者已经知道或潜意识里有了这种概念,这里只是做一些强调。
改善优化WPF Binding
下面的内容在修正错误的同时,还将对原有的Binding做一些小小的改进。
在上一篇Blog中,我曾经提到过这样一个问题,在将数据库中的DataTable对象的DefaultView通过XAML语言Binding到listView1对象的ItemsSource上时总是失败。
这里顺便公布一下答案,那就是使用DataContext,不过在动手之前先来梳理一下Binding的一些基本概念及使用方法。
在此之前先来点基础知识,都是MSDN上的内容(不过是E文的),感觉不错所以写下来,供大家阅读:
l 典型的Binding具有四个重要组成部分:Binding目标对象(binding target object)、目标对象属性(target property)、Binding数据源(binding source)、Path(用于指明要从数据源中取得的值,就是我们通常写的属性名称)。
例如:想要把一个员工(Employee)的姓名(EmpName)Binding显示到一个TextBox的Text属性上。
分析一下其中的4个组成部分:Binding目标对象是TextBox(因为它要最终使用这些数据);目标对象属性是TextProperty(这里要注意,不是Text属性,而是与Text属性相对应的Dependency Property,通常命名是属性名+Property);
数据源是员工(Employee);Path是EmpName。我的经验是:刚开始使用Binding的时候由于还不熟悉,建议在做之前先在心理把这4个部分对号入座一下,等日后就可以熟悉成自然了。
l 目标对象属性一定要是Dependency Property。由于只有DependencyObject类及其子类能够定义Dependency Property,因此,Binding目标对象就必须是DependencyObject类及其子类了。另一方面反过来说,UIElement(DependencyObject的子类)的大多数属性都有与其对应的Dependency Property,因此,大多数的UIElement属性我们都可以利用其Dependency Property作为目标对象属性从而与数据源建立Binding。
l 再有一点,建立Binding的数据源不一定要是一个CLR对象,也可以是XML数据、ADO.NET对象及DependencyObject。
关于为什么binding的对象一定要是DependencyObject,为什么binding对象的属性一定要是dependency property,这个我就不知道了。
在我第一次看到在XAML中设置Binding的语法时,简直是满头雾水。这东西到底怎么回事,简直就没有章法可循,一会儿ElementName,一会儿Source,还有时什么都没有,就一个{Binding },再有就是Mode,UpdateSourceTrigger等,简直没有头绪。
不过只要打开MSDN,看一下System.Windows.Data.Binding对象,似乎开始有点感觉了,查看Binding对象的属性栏(Properties),你会发现,原来我们在XAML中设置Binding时使用的东西和Binding对象的属性好像都是一一对应的。
确实如此,在XAML中设置Binding就是在这只System.Windows.Data.Binding对象,这里先提醒一下大家。
WPF可不是就这么一个Binding噢,实时上,从BindingBase继承过来的有三个兄弟,Binding只是最常用而已。
找到了binding的老家,相信大家对{Binding ElementName=ElemName, Path=PropertyName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, …}这样的语法应该清楚了吧。
那么{Binding } 及{Binding XXXX}这样的语法呢?其实可以就是等于new Binding()及new Binding(XXXX),其实可以考虑成是分别使用了Binding的两种构造方法,而XXXX的作用自然也就清晰了。
其实和{Binding Path=XXXX}是等价的,都是设置Path属性的。至于这里的Path属性在我们平常使用时其实是用来设置属性名的地方,为什么不直接叫做是PropertyName之类的名字呢?这个又要留着以后思考了。
当一个数据源与一个UIElement形成Binding后,数据源的改变,如果传递给UIElement的指定属性?
同样,当UIElement中指定属性值改变了,如何才能够通知到数据源?要讨论这个问题,需要按数据源来分类进行讨论。这个分类并不是互斥的之前存在一些交集。
因此,同一种Binding场景可能适用于多种情况。下面的内容是来自MSDN中《Binding Sources Overview》中的内容,由于怕翻译的不好而误导大家,所以分类的名称还是使用英文吧。
Using a CLR Class as the Binding Source Object
在典型的Binding情景中,一个CLR 对象作为数据源,将其中一个属性值Binding到某个UIElement的属性上(以上面显示员工姓名的情景为例。
用XAML实现<TextBox Text=”{Binding Source=Employee, Path=EmpName}” />)。当数据源属性值变化了,相应让UIElement中对应属性变化。
需要实现一个中通知机制,最简单的方法是通过实现INotifyPropertyChanged接口完成此操作。具体实现,可以参考MSDN上的文章《How to: Implement Property Change Notification》。
其实就是实现了”public event PropertyChangedEventHandler PropertyChanged;”事件,通常的做法是在上面加封装一个”protected void OnPropertyChanged(string name)”函数。在属性的set方法结尾处,调用此函数实现通知的作用。
如果不实行此接口,则必须自己实现一套通知系统。为你的类中的每一个想要实现通知机制的属性创建一个PropertyChanged模板。
即为你的每一个属性创建一个名为”PropertyNameChanged”事件,其中PropertyName是与这个事件对应的属性名称。然后在属性发生变化是触发这个事件。(听起来就麻烦,我也懒得去实践验证了)
如果以上两种方法都无法实现的话,那么就只好在你认为需要的时候显示的调用UpdateTarget方法来手动更新了。
(这种方法没用过,感觉已经失去了Binding的意义了)
Entire Objects Used as a Binding Source
可以理解为将整个对象作为数据源,与上一个题目的区别很小。
基本上可以认为是原来Binding实现的是数据源属性与UIElement属性之间的Binding,而这次是直接将一个Object对象与一个UIElement的属性进行Binding了。
这种情况可以使用Binding的Source属性指明数据源(假设Employee对象与一个ListBox实现banding,用XAML实现<ListBox ItemsSource=”{Binding Employee}” />),或者还可以将DataContext属性设置为Employee(DataContext属性只有UIElement的一个子类System.Windows.FrameworkElement及其子类才会有)。
这样,在设置ItemsSource时就可以直接使用”{Binding }”这样的语法了。当然,DataContext属性设置成Employee还是需要使用DataContext=”{Binding Employee}”语句的。
有人在这里会嘲笑这一举动实在是啰嗦,事实上,DataContext属性是非常有用的。之所以Binding对象失败而最终不得不使用C#语句进行Binding就是因为没有设置DataContext。
因为,当我们使用{Binding Source= Employee, Path=”EmpName”}设置属性后,WPF查找Source对象是根据Element Tree逐级向上自己的Parent Element查找的,查找每一Element上的DataContext所指定的内容是否包含这一Source内容,并以第一个匹配到的结果作为最终对象。
也就是说,DataContext是按照Element Tree向下继承的,并且决定这WPF在运行时能否找到我们所制定的Source对象。即使我们使用{Binding Path=”EmpName”}的语句也是如此。
比较常见的做法是,当我们有大量的Element需要与一个数据源中的众多属性实现Binding时,我们可以直接在一个公共Parent Element的DataContext上设置这一对象(甚至可以是整个window或page上),这将会极大的加少我们重复的代码量,同时也会是程序更加可读。
当我们需要使用的数据源是一个集合时,如果想要实现改变通知的机制。
同样需要实现一个接口INotifyCollectionChanged用来在集合发生改变时发起,用法单个对象是实现INotifyPropertyChanged接口类似。
首先是将上以前做的不是很好的那一段代码改成XAML语言实现。
将TextBox标签中的Text属性与上面listView1中被选定的记录(listView1.SelectedItem)中对应属性(例如:ContactID)做Binding。
实现后如图所示:
<TextBox Text="{Binding ElementName=listView1, Path=SelectedItem.ContactID}" MinWidth="100" />
看以来代码有点长,而且,下面的几个TextBox也都存在大量的重复内容,这里就可以考虑使用上面所说的DataContext了。
将listView1中被选定的记录(listView1.SelectedItem)作为DataContext放在公共Parent Element上,做法如下:
<WrapPanel Grid.Row="1" Orientation="Horizontal" DataContext="{Binding ElementName=listView1, Path=SelectedItem}">
然后剩下的代码就可以简化很多了:
<StackPanel Orientation="Horizontal" Margin="5,2,5,2">
<TextBlock Text="ContactID:" TextAlignment="Center" />
<TextBox Text="{Binding ContactID}" MinWidth="100" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5,2,5,2">
<TextBlock Text="FirstName:" TextAlignment="Center" />
<TextBox Text="{Binding FirstName}" MinWidth="100" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5,2,5,2">
<TextBlock Text="LastName:" TextAlignment="Center" />
<TextBox Text="{Binding EmailAddress}" MinWidth="100" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5,2,5,2">
<TextBlock Text="EmailAddress:" TextAlignment="Center" />
<TextBox Text="{Binding LastName}" MinWidth="100" />
</StackPanel>
原来的SelectionChanged事件处理方法现在已经没有用了,将多余的代码去掉:
<ListView Name="listView1" SelectionChanged="listView1_SelectionChanged" MinWidth="280" >
后台代码中的对应方法也可以删掉了private void listView1_SelectionChanged(object sender, SelectionChangedEventArgs e)。
F5 Debug运行一下,结果和原来是完全一样的。
在上一文中无得以选择使用C#实现Binding的部分代码,现在使用XAML语言实现,有几种方案可以选择,通过如下几步实现:
1) 将原先建立给listView1建立Binding那一大串代码删掉,替换成如下代码:
listView1.DataContext = dt.DefaultView;
执行结果如下图所示:
2) 将其与listView1的ItemsSource属性建立Binding,设置每一列的header及DisplayMemberBinding值。
这里由于DataContext的原因,ItemsSource可以写成"{Binding }"的形式而其Children Element也都可以简写成"{Binding ContactID}"这样的形式。
<ListView Name="listView1" MinWidth="280" ItemsSource="{Binding }">
<ListView.View>
<GridView x:Name="gridView1">
<GridView.Columns>
<GridViewColumn DisplayMemberBinding="{Binding ContactID}" Header="ContactID"></GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding FirstName}" Header="FirstName"></GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding LastName}" Header="LastName"></GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
改造完后,运行结果会发现,效果和原来是一样。效率提高了,代码更加简化。
WPF入门系列教程(二) 深入剖析WPF Binding的使用方法(全文完)
WPF入门教程系列(二) 深入剖析WPF Binding的使用方法的更多相关文章
- WPF入门教程系列二十三——DataGrid示例(三)
DataGrid的选择模式 默认情况下,DataGrid 的选择模式为“全行选择”,并且可以同时选择多行(如下图所示),我们可以通过SelectionMode 和SelectionUnit 属性来修改 ...
- WPF入门教程系列二——Application介绍
一.Application介绍 WPF和WinForm 很相似, WPF与WinForm一样有一个 Application对象来进行一些全局的行为和操作,并且每个 Domain (应用程序域)中仅且只 ...
- WPF入门教程系列二
WPF控件和布局 一. 前言 公司项目基于WPF开发,最近项目上线有点空闲时间写一篇基于wpf的基础教材,WPF也是近期才接触,学习WPF也是在网上查资料与微软的MSDN进行学习,写本博客的目为了 ...
- WPF入门教程系列二十二——DataGrid示例(二)
DataGrid示例的后台代码 1) 通过Entity Framework 6.1 从数据库(本地数据库(local)/Test中的S_City表中读取城市信息数据,从S_ Province表中读取 ...
- WPF入门教程系列二十一——DataGrid示例(一)
前面我们学习了ListView控件的使用示例,今天我们来学习DataGrid的有关知识.提到DataGrid 不管是Asp.Net中的网页开发还是WinForm应用程序开发都会频繁使用.通过它我们可以 ...
- WPF入门教程系列二十——ListView示例(二)
第四步.WPF后台逻辑代码编写 在后台用Entity Framework 6.1的Code First方式获取数据库中的数据.同时,在“刷新”按钮的方法中进行数据绑定.操作步骤如下: 1) 在“刷新 ...
- WPF入门教程系列三——Application介绍(续)
接上文WPF入门教程系列二——Application介绍,我们继续来学习Application 三.WPF应用程序的关闭 WPF应用程序的关闭只有在应用程序的 Shutdown 方法被调用时,应用程序 ...
- WPF入门教程系列(一) 创建你的第一个WPF项目
WPF入门教程系列(一) 创建你的第一个WPF项目 WPF基础知识 快速学习绝不是从零学起的,良好的基础是快速入手的关键,下面先为大家摞列以下自己总结的学习WPF的几点基础知识: 1) C#基础语法知 ...
- WPF入门教程系列一
WPF入门教程 一. 前言 公司项目基于WPF开发,最近项目上线有点空闲时间写一篇基于wpf的基础教材,WPF也是近期才接触,学习WPF也是在网上查资料与微软的MSDN进行学习,写本博客的目为了温 ...
随机推荐
- 20145236 《Java程序设计》第八周学习总结
20145236 <Java程序设计>第八周学习总结 教材学习内容总结 第十四章 NIO与NIO2 认识NIO NIO使用频道(Channel)来衔接数据节点,在处理数据时,NIO可以让你 ...
- uva 1629
1629 - Cake slicing Time limit: 3.000 seconds A rectangular cake with a grid of m * n <tex2html_v ...
- 使用Socket进行通信
客户端通常可使用Socket的构造器来连接到指定服务器,Socket通常可使用如下两个构造器. Socket(lnetAddress/String remoteAddress , int port ...
- iOS 登陆的实现四种方式
iOS 登陆的实现四种方式 一. 网页加载: http://www.cnblogs.com/tekkaman/archive/2013/02/21/2920218.ht ml [iOS登陆的实现] A ...
- 二模 (9)day1
第一题: 题目大意: 给出一个n位01串,要么不动它,要么把它删掉一个字符,要么插入一个字符(0或1),要么把一个1变成0,.使得有1的位置号的总和是n+1的倍数,或者是0. 解题过程: 1.直接枚举 ...
- 一看就懂的ReactJs入门教程(精华版)
一看就懂的ReactJs入门教程(精华版) 现在最热门的前端框架有AngularJS.React.Bootstrap等.自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和 ...
- (BFS)hdoj1242-Rescue
题目地址 初学BFS,第一次用BFS做题.题目就是一个基本的BFS模型,需要稍加注意的是遇到警卫时间要+1,以及最后比的是最短的时间而不是步数. #include<cstdio> #inc ...
- iOS如何生成.a文件
首先来谈谈为何要使用.a文件 Objective-c语言有.h .m 文件组成.静态库可以将 .m文件封装成一个.a文件,第三方应用程序只需要拿到这个.a文件和代码对应的.h文件即可使用静态库中封装的 ...
- java基础之 string
一 string public final class String 继承自java.lang.Object类. 实现了接口: java.io.Serializable, Comparable< ...
- springMVC文件上传(转)
原文链接: http://www.cnblogs.com/lonecloud/p/5989905.html 在Spring-mvc.xml注入bean 1 <!-- 配置文件上传,如果没有使用文 ...