




已阅读5页,还剩5页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
深入浅出WPF 第三辑 数据的绿色通道,Binding(上) 小序: 啊哦,实在是不好意思,最近实在是太忙了,忙的原因也非常简单自己的技术太差了,还有很多东西要学呀。门里门外,发现专业程序员非常重要的一项技能是读别人写的代码,这项技能甚至比自己写代码更重要。Anstinus同学就是读代码的高手,我写的代码他看两眼就知道怎么回事了,并且能够立刻修改,而他的代码我读了好几天还不知道是怎么回事儿呢。 因此,毫不夸张地说,从本篇文章起接下来的几篇文章几乎可以说是WPF的核心内容,非常重要。这几篇文章分别介绍了Binding、Dependency Property、Routed Event & Command等内容。精彩不断,敬请关注!正文:在学习新东西的时候,人们总是习惯拿它与自己已经了解的旧有知识去做比较,这样才掌握得快、记忆深刻。所以,经常有朋友问我:“WPF与Windows Form最大的区别是什么?请用最简短的话告诉我。”OK,这个问题问的非常好看上去WPF与WinForm最大的区别像是前面讲的那个XAML语言,但XAML只是个表层现象,WPF真正引人入胜、使之与WinForm泾渭分明的特点就是“数据驱动界面”。围绕着这个核心,WPF准备了很多概念相当前卫的技术,其中包括为界面准备的XAML、为底层数据准备的Dependency Property和为消息传递准备的Routed Event & Command。“数据驱动界面”,听起来有点抽象。用白话解释(中文白话似乎总也上不了台面、更不能往书里写,而老外的书里却可以白话连篇)就是数据是底层、是心脏,数据变了作为表层的UI就会跟着变、将数据展现给用户;如果用户修改了UI元素上的值,相当于透过UI元素直接修改了底层的数据;数据处于核心地位,UI处于从属地位。这样一来,数据是程序的发动机(驱动者)、UI成了几乎不包含任何逻辑专供用户观察数据和修改数据的“窗口”(被驱动者)。顺便插一句,如果你是一位WinForm程序员,“数据驱动界面”一开始会让你感觉不太习惯。比如,在WinForm编程时,如果想对ListBox里的Item排序,我们会直接去排列这些Item,也就是针对界面进行操作,这在WPF里就行不通了实际上,在WPF里因为界面完全是由数据决定的(甚至包括界面元素的排序),所以,我们只需要将底层数据排序,作为界面的Items也就在数据的驱动下乖乖地排好序了。那么,数据是怎样从底层传递到界面的呢?我们今天的主角,Binding同学,就要登场啦!深入浅出话BindingBinding同学最近很不开心,是因为它的中文名字“很暴力”绑定。我猜,最早的译者也没什么标准可遵循,大概是用了谐音吧!这一谐音不要紧,搞的中国程序员就有点摸不清头脑了。“绑”是捆绑的意思,再加上一个“定”字,颇多了几分“绑在一起、牢不可分”的感觉。而实际呢?Binding却是个地地道道的松耦合的关系!依在下拙见,Binding译为“关联”是再合适不过了。在英语词典里,也的确有这一词条。关联吗,无需多讲,人人都知道是“之间有些关系”的意思。Data Binding也就不应该再叫“数据绑定”了,应该称为“数据关联”,意思是说,在数据和界面(或其他数据)之间具有某些关系和联动。具体到WPF中,Binding又是怎样一种关系和联动呢?就像我们的大标题一样Binding就是数据的“绿色通道”。“绿色通道”代表着“直接”和“快速”,Binding就是这样。让我们分享一个有趣的例子,请看下面的截图:这里是两个TextBox和一个Slider组成的UI,现在客户的需求是当Slider的滑块移动时,上面那个TextBox里显示Slider的Value;反过来,当在上面那个TextBox里输入合适的值后,鼠标焦点移开后,Slider的滑块也要滑到相应的位置上去。站在一个WinForm程序员的角度去考虑,他会做这样几件事情:1. 响应slider1的ValueChanged事件,在事件处理函数中让textBox1显示slider1的Value 2. 响应textBox1的LostFocus事件,把textBox1的Text转换成数值,并赋值给slider1 注意了!这就是典型的“非数据驱动界面”的思想。为什么呢?当我们响应slider1的ValueChanged事件时,我们要的是slider1的Value这个值,这时候,slider1处于核心地位、是数据的“源”(Source);当我们响应textBox1的LostFocus事件时,我们需要的是它的Text属性值,这时候,textBox1又成了数据的source。也就是说,在这种处理方法中,数据没有固定的“源”,两个UI元素是对等的、不存在谁从属于谁的问题。换句话说:它们之间是“就事论事”,并没有什么“关联”。接下来,让我们体验一下“绿色通道”的快捷!上述例子的XAML源代码如下:view plaincopy to clipboardprint?1. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 剔除那些花里呼哨的装饰品后,最重要的只有下面3行(而实际上第2行那个textBox2只是为了让鼠标的焦点有个去处):view plaincopy to clipboardprint?1. 2. 3. 4. 5. 然后,我只需在第1行代码上做一个小小的修改,就能完成WinForm中需要用两个事件响应、十多行代码才能完成的事情:view plaincopy to clipboardprint?1. 2. 3. 4. 5. 细心的你,一定一眼就看到只多了这样一句话:Text=Binding ElementName=slider1, Path=Value这句话的意思是说:Hi,textBox1,从此以后,你的Text属性值就与slider1这个UI元素的Value属性值关联上了,Value变的时候你的Text也要跟着变。这时候的效果是你拖动Slider的滑块,textBox1就会显示值(双精度,0到100之间);你在textBox1里输入一个0到100之间的数字,当把鼠标移动到textBox2里时,slider1的滑块会跳到相应的值上去,如图:非常简单是不是?请注意,这里面可蕴含了“数据驱动界面”的模型哦!在这里,我们始终把slider1的Value当成是数据源(Data Source),而textBox1则是用来显示和修改数据的窗口(Data Presenter)slider1是核心,它的Value属性值将驱动textBox1的Text进行改变;人为改变textBox1的Text属性值,也会被送回到slider1的Value属性值上去。是时候让我们了解Data Binding的几个关键概念了1. 数据源(Data Source,简称Source):顾名思义,它是保有数据的实体、是数据的来源、源头。把谁当作数据源完全由程序员来决定只要你想把它当做数据核心来使用。它可以是一个UI元素、某个类的实例,也可以是一个集合(关于对集合的绑定,非常重要,专门用一篇文章来讨论之)。 2. 路径(Path):数据源作为一个实体可能保有着很多数据,你具体关注它的哪个数值呢?这个数值就是Path。就上面的例子而言,slider1是Source,它拥有很多数据除了Value之外,还有Width、Height等,但都不是我们所关心的所以,我们把Path设为Value。 3. 目标(Target):数据将传送到哪里去?这就是数据的目标了。上面这个例子中,textBox1是数据的Target。有一点需要格外注意:Target一定是数据的接收者、被驱动者,但它不一定是数据的显示者也许它只是数据联动中的一环后面我们给出了例子。 4. 关联(Binding):数据源与目标之间的通道。正是这个通道,使Source与Target之间关联了起来、使数据能够(直接或间接地)驱动界面! 5. 设定关联(Set Binding):为Target指定Binding,并将Binding指向Target的一个属性,完成数据的“端对端”传输。 绿色通道上的“关卡”:话说眼看就要到奥运会了,北京的各大交通要道上也都加强了安检力度。微软同学也给Binding这条“绿色通道”准备了几道很实用的“关卡”。这些“关卡”的启闭与设置是通过Binding的属性来完成的。其中常用的有: 如果你想把“绿色通道”限制为“单行道”,那就设置Binding实例的Mode属性,它是一个BindingMode类型的枚举值,其中包含了TwoWay、OneWay和OneWayToSource几个值。上面这个例子中,默认地是TwoWay,所以才会有双向的数据传递。 如果用户提出只要textBox1的文本改变slider1的滑块立刻响应,那就设置Binding的UpdateSourceTrigger属性。它是一个UpdateSourceTrigger类型枚举值,默认值是UpdateSourceTrigger.LostFocus,所以才会在移走鼠标焦点的时候更新数据。如果把它设置为UpdateSourceTrigger.PropertyChanged,那么Target被关联的属性只要一改变,就立刻传回给Source我们要做的仅仅是改了一个单词,而WinForm程序员这时候正头疼呢,因为他需要去把代码搬到另一个事件响应函数中去。 如果Binding两端的数据类型不一致怎么办?没关系,你可以设置Binding的Converter属性,具体内容在下篇文章中讨论。 如果数据中有“易燃易爆”的不安全因素怎么办?OK,可以设置Binding的ValidationRules,为它加上一组“安检设施”(同样也在下篇文中讨论)。 在C#代码中设置BindingXAML代码是如此简单,简单就那么一句话。这可不是吾等C#程序员、刨根问底之徒可以善罢甘休的!形象地讲,Binding就像一个盒子,盒子里装了一些机关用于过滤和控制数据,盒子两端各接着一根管子,管子是由管壳和管芯构成的,看上去就像下面的图:当脑子里有了这样一个形象之后,遵循下面的步骤就OK了:1. Source:确定哪个对象作为数据源 2. Target:确定哪个对象作为目标 3. Binding:声明一个Binding实例 4. 把一根管子接到Source上并把管芯插在Source的Path上 5. 把另一根管子接到Target上并把管芯插在Target的联动属性上 如果有必要,可以在3与4之间设置Binding的“关卡”们。其实,第3步之后的顺序不是固定的,只是这个步骤比较好记一概向右连接。所得结果看上去是这样:我猜你可能会问:“那个D.P.是什么呀?”D.P.的全称是“Dependency Property”,直译过来就是“依赖式属性”,意思是说它自己本身是没有值的,它的值是“依赖”在其它对象的属性值上、通过Binding的传递和转换而得来的。表现在例子里,它就是Target上的被数据所驱动的联动属性了!这里是等价的C#代码,我把它写在了Window1的构造函数里:view plaincopy to clipboardprint?1. publicWindow1()2. 3. InitializeComponent();4. 5. /1.我打算用slider1作为Source 6. /2.我打算用textBox1作为Target 7. Bindingbinding=newBinding();8. binding.Source=this.slider1;9. binding.Path=newPropertyPath(Value);10. this.textBox1.SetBinding(TextBox.TextProperty,binding);11. 有意思的是,Source端的操作,接管子和插管芯分两步,而Target端却是在SetBinding方法中一步完成。注意啦,TextBox.TextProperty就是一个Dependency Property的庐山真面目!关于Dependency Property的文档业已完成,我将择一黄道吉日挂将出来:p上面的代码稍有简化的余地,那就是把Path的设定转移到Binding的构造中去:view plaincopy to clipboardprint?1. publicWindow1()2. 3. InitializeComponent();4. 5. /1.我打算用slider1作为Source 6. /2.我打算用textBox1作为Target 7. Bindingbinding=newBinding(Value);8. binding.Source=this.slider1;9. this.textBox1.SetBinding(TextBox.TextProperty,binding);10. 这样做的好处是随便你给binding指定一个Source,只要这个Source有“Value”这个属性,binding就会自动提取它的值并传输给Target端。我们还可以为binding设些“关卡”:view plaincopy to clipboardprint?1. publicWindow1()2. 3. InitializeComponent();4. 5. 6. /1.我打算用slider1作为Source 7. /2.我打算用textBox1作为Target 8. Bindingbinding=newBinding(Value);9. binding.Source=this.slider1;10. binding.Mode=BindingMode.TwoWay;11. binding.UpdateSourceTrigger=UpdateSourceTrigger.PropertyChanged;12. this.textBox1.SetBinding(TextBox.TextProperty,binding);13. 还有一个小小的提示:如果Source碰巧是一个UI元素,那么也可将binding.Source = this.slider1;改写成binding.ElementName = slider1;或者binding.ElementName = this.slider1.Name;自定义数据源:在我们项目组日常的工作中,经常需要自己写一个类,并且拿它的实例当作数据源。怎样才能让一个类成为“合格的”数据源呢?要诀就是:1. 为这个类定义一些Property,相当于为Binding提供Path 2. 让这个类实现INotifyPropertyChanged接口。实现这个接口的目的是当Source的属性值改变后通知Binding(不然人家怎么知道源头的数据变了并进行联动协同呢?),好让Binding把数据传输给Target本质上还是使用事件机制来做,只是掩盖在底层、不用程序员去写event handler了。 让我们写一个这样的类:view plaincopy to clipboardprint?1. /第一步:声明一个类,准备必要的属性 2. 3. publicclassStudent4. 5. privateintid;6. publicintId7. 8. getreturnid;9. setid=value;10. 11. 12. privatestringname;13. publicstringName14. 15. getreturnname;16. setname=value;17. 18. 19. privateintage;20. publicintAge21. 22. getreturnage;23. setage=value;24. 25. 26. 接下来就是使用INotifyPropertyChanged接口“武装”这个类了,注意,这个接口在System.ComponentModel名称空间中:view plaincopy to clipboardprint?1. /第二步:实现INotifyPropertyChanged接口 2. 3. publicclassStudent:INotifyPropertyChanged4. 5. publiceventPropertyChangedEventHandlerPropertyChanged;/这个接口仅包含一个事件而已 6. 7. privateintid;8. publicintId9. 10. getreturnid;11. set1
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 电梯收购合同与收购协议
- 湿地鱼池承包协议书模板
- 股东实物作价入股协议书
- 股东利润分成协议书模板
- 灯箱合作合同协议书模板
- 私人暖气模板合同协议书
- 村集体大楼租赁合同范本
- 项目签合同前的居间协议
- 长沙离婚协议补充协议书
- 碧桂园转让酒店合同范本
- DB13-T 1545-2025 预拌混凝土质量管理规程
- 《医疗机构药学服务课件》
- 生态修复中的环境保护监理措施
- 宁夏固原公开招聘农村党务(村务)工作者笔试题含答案2024年
- 企业财务人员防诈课件
- 抗凝剂皮下注射技术临床实践指南(2024版)解读 2
- 开封市事业单位联考招聘笔试真题2024
- TCECS24-2020钢结构防火涂料应用技术规程
- 危大工程安全监理实施细则
- 球节点钢网架施工方案
- 苏教牛津译林版小学英语六年级上册单词背诵默写本
评论
0/150
提交评论