1:Data Binding在WPF中的地位

程序的本质是数据加算法。数据会在存储、逻辑和展示三个层流通,所以站在数据的角度上来看,这三层都很重要。但算法在程序中的分布就不均匀了,对于一个三层结构的程序来说,算法一般分布在这几处:

(1)数据库内部。

(2)读取和写回数据。

(3)业务逻辑。

(4)数据展示。

(5)页面与逻辑的交互。

1和2两个部分的算法一般都非常稳定,不会轻易去改动,复用性也很高;3处于客户需求关系最紧密、最复杂,变动也最大,大多数算法都集中在这里;4和5两层负责UI与逻辑交互,也占有一定量的算法。

显然,3部分是程序的核心、是开发的重中之重,所以我们应该把精力集中在3部分。然而4、5部分却经常成为麻烦的来源。首先,这两部分都与逻辑层紧密相关,一不小心就有可能把本来该放在逻辑层里的算法写进这两部分;其次,这两部分以消息或事件的方式与逻辑层沟通,一旦出现同一数据需要在多处展示/修改时,用于同步的代码就会错综复杂;最后4和5本应是互逆的一对儿,但是却需要分开来写——显示数据写一个算法、修改数据又是一个算法。总之导致的结果就是4和5两部分会占去一部分算法,搞不好还会牵扯不少精力。

问题的根源就在于逻辑层与表示层的地位不固定——当实现客户需求的时候,逻辑层的确处在中心地位,但到了实现UI交互的时候展示层又处在中心地位。WPF作为一种专门的展示层技术,华丽的外观和动画只是它的表层现象,更重要的是在深层次上帮助程序员把思维的重心固定在了逻辑层、让展示层永远处于逻辑层的从属地位。WPF具有这种能力的关键是它引入了Data Binding概念以及与之配套的Dependency Property系统和Data Template。

表示层使用WPF类库实现,而展示层与逻辑层的沟通就使用Data Binding来实现。可见Data Binding在WPF系统中起到的是数据高速公路的作用。有了这条高速公路,加工好又会被送达用户界面加以显示,被用户修改过的数据也会自动传回逻辑层,一旦数据被加工好又会被送达用户界面,程序的逻辑层就像一个强有力的引擎不停运转,用加工好的数据驱动程序的用户界面以文字、图形、动画等形式把数据显示出来——这就是“数据驱动UI”。

引入Data Binding机制后,4、5两个部分会简化很多。首先、数据在逻辑层与用户界面之间“直来直去”、不涉及逻辑问题,这样用户界面部分几乎不包含算法;Data Binding本身就是双向通信,所以相当于把4、5合二为一;对于多个UI元素关注同一个数据的情况,只需要使用Data Binding把这些UI元素一一与数据关联上,当数据变化后这些UI元素会同步显示这一变化。经过这样的优化,所有与业务逻辑相关的算法都处在数据逻辑层,逻辑层成为一个能够独立运转的、完整的体系,而用户界面则不包含任何代码、完全依赖和从属数据逻辑层。

2:Binding基础

如果不知道Binding一词的含义,那么它将永远是大脑中的一个符号。Binding更注重表达它是一种像桥梁一样的关联关系。在WPF中,正是在这段桥梁上我们有机会为往来流通的数据做很多事情。

如果把Binding比作数据的桥梁,那么它的两端分别是Binding的源(Source)和目标(Target)。一般情况下,Binding源是逻辑层的对象,Binding目标是UI层的控件对象,这样,数据就会源源不断通过Binding送达UI层、被UI层展现,也就完成了数据驱动UI的过程。

3:Binding的源于路径

Binding的源也就是数据的源头。Binding对源的要求并不苛刻——只要它是一个对象,并且通过属性(property)公开自己的数据,它就能作为Binding的源。

如果想让作为Binding源的对象具有自动通知Binding自己的属性值发生变化的能力,那么就需要让类实现INotifyPropertyChanged接口并在属性的set语句中激发PropertyChange事件。

1:把控件作为Binding源与Binding标记扩展。

大多数情况下Binding的源是逻辑层的对象,但有时候为了让UI元素产生一些联动效果也会使用Binding在控件间建立关联。

2:控制Binding的方向及数据更新

Binding在源与目标之间架起了沟通的桥梁,默认情况下数据既能通过Binding送达目标,也能从目标返回源。有时候数据只需要展示给用户、不允许用户修改,这时候可以把Binding模式更改为从源向目标的单向沟通。Binding还支持从目标向源的单向沟通以及只在Binding关系确立时读取一次数据,这是需要我们根据实际情况去选择。

控制Binding数据流向的属性是Mode,它的类型是BindingMode枚举。BindingMode可取值为TwoWay、OneWay、OnTime、OneWayToSource和Default。这里的Default值是指Binding的模式会根据目标的实际情况来确定。

3:Binding的路径(Path)

作为Binding源的对象可能有很多属性,通过这些属性Binding元可以把数据暴露给外界。那么,Binding到底需要到底需要关注哪个属性的值呢?这就需要有Binding的Path属性来指定了。

4:“没有Path”的Binding

有的时候我们会在代码中看到一些Path是一个“.”或者干脆没有Path的Binding,着实让人摸不着头脑。原来,这是一种比较特殊的情况——Binding源本身就是数据且不需要Path来指明。

5:为Binding指定源(Source)的几种方法

Binding的源是数据的来源,所以,只要一个对象包含数据并能通过属性把数据暴露出来,它就能当作Binding的源来使用。包含的数据比比皆是,但必须为Binding的Source指定合适的对象Binding才能正确工作,常见的办法有:

(1)把普通的CLR类型单个对象指定为Source:包括.NET Framework自带类型的对象和用户自定义类型的对象。如果类型实现了INotifyPropertyChanged接口,则可通过在属性的set语句里激发PropertyChanged事件来通知Binding数据已被更新。

(2)把普通CLR(公共语言运行时)集合类型对象指定为Source:包括数组、List<T>、ObservableCollection<T>等集合类型。实际工作中,我们经常需要把一个集合作为ItemControl派生类的数据源来使用,一般是把控件的ItemSource属性使用Binding关联到一个集合对象上。

(3)ADO.NET 数据对象指定为Source:包括DataTable和DataView等对象。

(4)使用XmlDataProvider把XML数据指定为Source:XML作为标准的数据存储和传输格式几乎无处不在,我们可以用它表示单个数据对象或者集合;一些WPF控件是级联式的,我们可以把树状结构的XML数据作为源指定给与之关联的Binding。

(5)把依赖对象(Dependency Object)指定为Source;依赖对象不仅可以作为Binding的目标,同时也可以作为Binding的源。这样就有可能形成Binding链。依赖对象中的依赖属性可以作为Binding的Path。

(6)把容器的DataContext指定为Source:有时候我们会遇到这样的情况——我们明确知道将从哪个属性获取数据,但具体把哪个对象作为Binding源还不确定。这时候,我们只能先建立一个Binding,只给他设置Path而不设置Source,让这个Binding自己去寻找Source。这时候,Binding会自动把控件DataContext当做自己的Source。

(7)通过ElementName指定Source:在C#代码里可以直接把对象作为Source赋值给Binding,但XAML无法访问对象,所以只能使用对象的Name属性来找到对象。

(8)通过Binding的RelativeSource属性相对地指定Source;当控件需要关注自己的、自己容器的或者自己内部元素的某个值就需要使用这种方法。

(9)把ObjectDataProvider对象指定为Source;当数据源的数据不再通过属性而是通过方法暴露给外界的时候,我们可以使用这两种对象来包装数据源再把它们指定为Source。

(10)把使用LINQ检索得到的数据对象作为Binding的源。

6:没有Source的Binding——使用DataContext作为Binding的源

DataContext属性被定义在FrameworkElement类里,这个类是WPF控件的基类,这意味着所有WPF控件都具备这个属性。如前所诉,WPF的UI布局是树形结构,这棵树的每个结点都是控件,由此我们推出另一个结论——在UI元素树每个结点都有DataContext。这一点非常重要,因为当一个Binding只知道自己的Path而不知道自己的Source时,它会沿着UI树一路向树的根部找过去,每路过一个结点就要看看这个结点的DataContext是否具有Path所指定的属性。如果有,那就把这个对象作为自己的Source;如果没有,那就继续找下去;如果到了树的根部还没有找到,那这个Binding就没有Source,因而也不会得到数据。

Binding是怎样自动向UI元素树的上层寻找DataContext对象并把它作为Source的呢?其实,“Binding沿着UI元素树向上找”只是WPF给我们的一个错觉,Binding并没有那么智能,之所以会有这种效果是因为DataContext是一个“依赖属性”,依赖属性有一个很重要的特点就是当你没有为控件的某个依赖属性显示赋值时,控件会把自己容器的属性值“借过来”当做自己的属性值。实际上是属性值沿着UI树向下传递了。

在实际工作中DataContext的用法是非常灵活的。比如:

(1)当UI上的多个控件都使用Binding关注同一个对象时,不妨使用DataContext。

(2)当作为Source的对象不能被直接访问的时候。比如B窗体内的控件想把A窗体内的控件当做自己的Binding源时,但A窗体的控件是Private访问级别,这时候就可以把这个控件作为窗体A的DataContext(这个属性是public访问级别的)从而暴露数据。

7:使用集合对象作为列表控件的ItemsSource

WPF中的列表式控件们派生自ItemsControl类,自然业务继承了ItemsSource这个属性。ItemsSource属性可以接收一个IEnumerable接口派生类的实例作为自己的值。每个ItemsControl的派生类都具有自己对应的条目容器。

8:使用ADO.NET 对象作为Binding的源

在.NET 开发中,我们使用ADO.NET类对数据库进行操作。常见的工作是从数据库中把数据读取到DataTable中,再把DataTable显示在UI列表控件里。尽管在流行的软件框架中并不把DataTable的数据直接显示在UI列表控件里而是先通过LINQ等手段把DataTable里的数据转换成恰当的用户自定义类型集合,但WPF也支持在列表控件与DataTable之间直接建立Binding。

9:使用XML数据作为Binding的源

迄今为止,.NET Framework提供了两套处理XML数据的类库:

(1)符合DOM标准的类库:包括XmlDocument、XmlElement、XmlNode、XmlAttribute等类。这套类库的特点是中规中矩、功能强大,但也背负了太多XML的传统和复杂。

(2)以LINQ为基础的类库:包括XDocument、XElement、XNode、XAttribute等类。这套类库的特点是可以使用LINQ进行查询和操作,方便快捷。

现代程序设计只要涉及数据传输就离不开XML,因为大多数数据传输都基于SOAP相关的协议,而SOAP又是通过将对象序列化XML文本进行传输。XML文本是树形结构的,所以XML可以方便地用于表示线性集合和树形结构数据。使用@符号加字符串表示的是XML元素的Attribute,不加@符号的字符串表示的是子级元素。

10:使用LINQ检索结果作为Binding源

自3.0版开始,.NET Framework开始支持LINQ(语言集成查询),使用LINQ,我们可以方便地操作集合对象、DataTable对象和XML对象而不必动辄就把好几层foreach循环嵌套在一起却只是为了完成一个很简单的任务。

LINQ查询结果是一个IEnumerable<T>类型对象,而IEnumerable<T>有派生自IEnumerable,所以它可以作为列表控件的ItemsSource来使用。

11:使用ObjectDataProvider对象作为Binding的Source

ObjectDataProvider,顾名思义就是把对象作为数据源提供给Binding。

12:使用Binding的RelativeSource

当一个Binding有明确的数据来源时我们可以通过Source或ElementName赋值的办法让Binding与之关联。有些时候我们不能确定作为Source的对象叫什么名字,但知道它与作为Binding目标的对象在UI布局上有相对关系,比如控件自己关联自己的某个数据、关联自己某级容器的数据。这时候我们就要使用Binding的RelativeSource属性。

RelativeSource属性的数据类型为RelativeSource类,通过这个类的几个静态或非静态属性我们可以控制它相对数据源的方式。

4:Binding对数据的转换和校验

Binding用于数据有效性校验的关卡是ValidationRules属性,用于数据类型转换的关卡是Converter属性。

1:Binding的数据校验

Binding的ValidationRules属性类型是Collection<Validation>,从它的名称和数据类型可以得知可以为每个Binding设置多个数据校验条件,每个条件是一个Validation类型对象。ValidationRules类是个抽象类,在使用的时候我们需要创建它的派生类并实现它的Validate方法。Validate方法返回值是ValidationResult类型对象,如果校验通过,就把ValidationResult对象的IsValid属性设为true,反之,需要IsValid属性设为false并为其ErrorContent属性设置一个合适的消息内容。

2:Binding的数据转换

Binding有一种机制为数据转换(Data Convert),当Source端Path所关联的数据与Target端目标数据类型不一致时,我们可以添加数据转换器(Data Converter)。

5:MultiBinding(多路Binding)

有的时候UI需要显示的信息有不止一个数据来源决定,这时候就需要使用MultiBinding,即多路Binding。MultiBinding以Binding一样均以BindingBase为基类,也就是说,凡是能使用Binding对象的场合都能使用MultiBinding。MultiBinding具有一个名为Binding的属性,其类型时Collection<BindingBase>,通过这个属性MultiBinding把一组Binding对象聚合起来,处在这个集合中的Binding对象可以拥有自己的数据校验与转换机制,它们汇集起来的数据共同决定传往MultiBinding目标的数据。

参考教材书:深入浅出WPF 刘铁猛  著

深入浅出WPF笔记——Binding相关推荐

  1. 深入浅出WPF之Binding的使用(一)

    from:   http://www.cnblogs.com/akwwl/p/3421005.html 在WPF中Binding可以比作数据的桥梁,桥梁的两端分别是Binding的源(Source)和 ...

  2. 深入浅出WPF之Binding的使用(二)

    from:    http://www.cnblogs.com/akwwl/p/3421250.html 在上一篇中介绍了Binding的基本绑定方法,这一篇中我们在深入的介绍Binding的其他用法 ...

  3. (WPF)WPF要点之事件-深入浅出WPF笔记

    路由事件源于UIElement也就是可视化元素,需要利用其自有的CLR事件函数对路由事件的绑定进行封装,在可视化元素源元素的既有事件之上来增加其触发. 附加事件则始于路由事件概念,但是可将非可视化元素 ...

  4. 深入浅出WPF学习笔记之Binding

    深入浅出WPF之Binding Binding Binding基础 Binding模型 把控件作为Binding源与Binding标记扩展 Binding的(Path)路径 Binding支持多级路径 ...

  5. WPF的binding

    深入浅出WPF之Binding的使用(一) 在WPF中Binding可以比作数据的桥梁,桥梁的两端分别是Binding的源(Source)和目标(Target).一般情况下,Binding源是逻辑层对 ...

  6. 《深入浅出WPF》笔记——绑定篇(一)

    上一节,有记录写到:在WPF里,数据驱动UI,数据占核心地位,UI次之.怎么恢复数据的核心地位,那就要先了解一下Binding. 一.Binding 基础 1.1WPF中Data Binding的带来 ...

  7. 《深入浅出WPF》笔记——模板篇

    原文:<深入浅出WPF>笔记--模板篇 我们通常说的模板是用来参照的,同样在WPF中,模板是用来作为制作控件的参照. 一.认识模板 1.1WPF菜鸟看模板 前面的记录有提过,控件主要是算法 ...

  8. 《深入浅出WPF》笔记——事件篇

    如果对事件一点都不了解或者是模棱两可的话,建议先去看张子阳的委托与事件的文章(比较长,或许看完了,也忘记看这一篇了,没事,我会原谅你的)http://www.cnblogs.com/JimmyZhan ...

  9. [转]深入浅出WPF(7)——数据的绿色通道,Binding

    本文转自:http://liutiemeng.blog.51cto.com/120361/95273 小序: 怎么直接从2蹦到7啦?!啊哦,实在是不好意思,最近实在是太忙了,忙的原因也非常简单--自己 ...

  10. WPF之Binding的三种简单写法

    环境 类代码 public class Person:INotifyPropertyChanged { private string name; public string Name { get { ...

最新文章

  1. 2021年大数据Spark(三十一):Spark On Hive
  2. oracle选择语言设置,oracle本地语言变量设置
  3. 浙大团队研发铜基沸石纱布口罩,1分钟杀灭新冠病毒逾99%
  4. 为GridView添加表头thead
  5. 被认为最具影响力的4种编程语言!
  6. Eclipse Collections随Java版本的演变
  7. PHP 判断是否包含某字符串
  8. 客户端的js js脚本的引入 js的解析过程
  9. oracle catalog命令,catalog 命令
  10. 程序员面试金典——4.3高度最小的BST
  11. jquery实现查看全部示例
  12. idea前端可视化_jsp可视化开发工具_netbeans jsp可视化_idea 可视化开发 jsp
  13. MATLAB中的pause用法,pause使用方法 C语言 pause()函数问题
  14. revel MySQL_mysql – 如何在Revel Controller中访问Gorm?
  15. 五四青年节。无奋斗,不青春!
  16. 基于Android+servlet的宠物商店【源码+文档+ppt】
  17. 《高性能MySQL》阅读-高性能索引策略
  18. Executor及Executors
  19. 从12个球任取8个球
  20. Filter_过滤器

热门文章

  1. http请求接口开发的几种方式
  2. TCPUDP调试工具 Linux 版
  3. QGIS教程02---QGIS加载数据的4种方法
  4. PHP7函数大全(4553个函数)
  5. 2022高压电工操作证考试题库及模拟考试
  6. BP神经网络代码示例
  7. 给opensuse安装文泉驿字体
  8. 100小时学习SAP之自学环境(一)
  9. VBA编程之ODBC连接数据库
  10. matlab画图函数双精度,Matlab中图像函数大全2_matlab函数大全