站在一个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源代码如下:

  1. <Window x:Class="BindingSample.Window1"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. Title="http://blog.csdn.net/FantasiaX" Height="300" Width="300">
  5. <Window.Background>
  6. <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
  7. <GradientStop Color="Blue"  Offset="0.3"/>
  8. <GradientStop Color="LightBlue" Offset="1"/>
  9. </LinearGradientBrush>
  10. </Window.Background>
  11. <Grid>
  12. <TextBox Height="23" Margin="10,10,9,0" Name="textBox1" VerticalAlignment="Top" />
  13. <TextBox Height="23" Margin="10,41,9,0" Name="textBox2" VerticalAlignment="Top" />
  14. <Slider Height="21" Margin="10,73,9,0" Name="slider1" VerticalAlignment="Top" Maximum="100" />
  15. </Grid>
  16. </Window>

剔除那些花里呼哨的装饰品后,最重要的只有下面3行(而实际上第2行那个textBox2只是为了让鼠标的焦点有个去处):

  1. <Grid>
  2. <TextBox Height="23" Margin="10,10,9,0" Name="textBox1" VerticalAlignment="Top" />
  3. <TextBox Height="23" Margin="10,41,9,0" Name="textBox2" VerticalAlignment="Top" />
  4. <Slider Height="21" Margin="10,73,9,0" Name="slider1" VerticalAlignment="Top" Maximum="100" />
  5. </Grid>

然后,我只需在第1行代码上做一个小小的修改,就能完成WinForm中需要用两个事件响应、十多行代码才能完成的事情:

  1. <Grid>
  2. <TextBox Height="23" Margin="10,10,9,0" Name="textBox1" VerticalAlignment="Top" Text="{Binding ElementName=slider1, Path=Value}"/>
  3. <TextBox Height="23" Margin="10,41,9,0" Name="textBox2" VerticalAlignment="Top" />
  4. <Slider Height="21" Margin="10,73,9,0" Name="slider1" VerticalAlignment="Top" Maximum="100" />
  5. </Grid>

细心的你,一定一眼就看到只多了这样一句话: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几个值。上面这个例子中,默认地是Default,所以才会有双向的数据传递。
  • 如果用户提出只要textBox1的文本改变slider1的滑块立刻响应,那就设置Binding的UpdateSourceTrigger属性。它是一个UpdateSourceTrigger类型枚举值,默认值是UpdateSourceTrigger.LostFocus,所以才会在移走鼠标焦点的时候更新数据。如果把它设置为UpdateSourceTrigger.PropertyChanged,那么Target被关联的属性只要一改变,就立刻传回给Source——我们要做的仅仅是改了一个单词,而WinForm程序员这时候正头疼呢,因为他需要去把代码搬到另一个事件响应函数中去。
  • 如果Binding两端的数据类型不一致怎么办?没关系,你可以设置Binding的Converter属性,具体内容在下篇文章中讨论。
  • 如果数据中有“易燃易爆”的不安全因素怎么办?OK,可以设置Binding的ValidationRules,为它加上一组“安检设施”(同样也在下篇文中讨论)。

在C#代码中设置Binding

XAML代码是如此简单,简单就那么一句话。这可不是吾等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的构造函数里:

  1. public Window1()
  2. {
  3. InitializeComponent();
  4. // 1. 我打算用slider1作为Source
  5. // 2. 我打算用textBox1作为Target
  6. Binding binding = new Binding();
  7. binding.Source = this.slider1;
  8. binding.Path = new PropertyPath("Value");
  9. this.textBox1.SetBinding(TextBox.TextProperty, binding);
  10. }

有意思的是,Source端的操作,接管子和插管芯分两步,而Target端却是在SetBinding方法中一步完成。注意啦,TextBox.TextProperty就是一个Dependency Property的庐山真面目!关于Dependency Property的文档业已完成,我将择一黄道吉日挂将出来:p

上面的代码稍有简化的余地,那就是把Path的设定转移到Binding的构造中去:

  1. public Window1()
  2. {
  3. InitializeComponent();
  4. // 1. 我打算用slider1作为Source
  5. // 2. 我打算用textBox1作为Target
  6. Binding binding = new Binding("Value");
  7. binding.Source = this.slider1;
  8. this.textBox1.SetBinding(TextBox.TextProperty, binding);
  9. }

这样做的好处是——随便你给binding指定一个Source,只要这个Source有“Value”这个属性,binding就会自动提取它的值并传输给Target端。

我们还可以为binding设些“关卡”:

  1. public Window1()
  2. {
  3. InitializeComponent();
  4. // 1. 我打算用slider1作为Source
  5. // 2. 我打算用textBox1作为Target
  6. Binding binding = new Binding("Value");
  7. binding.Source = this.slider1;
  8. binding.Mode = BindingMode.TwoWay;
  9. binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
  10. this.textBox1.SetBinding(TextBox.TextProperty, binding);
  11. }

还有一个小小的提示:如果Source碰巧是一个UI元素,那么也可将binding.Source = this.slider1;改写成binding.ElementName = "slider1";或者binding.ElementName = this.slider1.Name;

[WPF]入门理解Binding 数据驱动思想的更多相关文章

  1. WPF教程三:学习Data Binding把思想由事件驱动转变为数据驱动

    之前大家写代码都喜欢用事件驱动,比如说鼠标输入的click事件.初始化的内容全部放在窗体加载完毕的load事件,等等,里面包含了大量的由事件触发后的业务处理代码.导致了UI和业务逻辑高度耦合在一个地方 ...

  2. WPF入门教程系列(二) 深入剖析WPF Binding的使用方法

    WPF入门教程系列(二) 深入剖析WPF Binding的使用方法 同一个对象(特指System.Windows.DependencyObject的子类)的同一种属性(特指DependencyProp ...

  3. WPF入门教程系列(一) 创建你的第一个WPF项目

    WPF入门教程系列(一) 创建你的第一个WPF项目 WPF基础知识 快速学习绝不是从零学起的,良好的基础是快速入手的关键,下面先为大家摞列以下自己总结的学习WPF的几点基础知识: 1) C#基础语法知 ...

  4. WPF入门教程系列一

    WPF入门教程 一.  前言  公司项目基于WPF开发,最近项目上线有点空闲时间写一篇基于wpf的基础教材,WPF也是近期才接触,学习WPF也是在网上查资料与微软的MSDN进行学习,写本博客的目为了温 ...

  5. WPF入门:数据绑定

    上一篇我们将XAML大概做了个了解 ,这篇将继续学习WPF数据绑定的相关内容 数据源与控件的Binding Binding作为数据传送UI的通道,通过INotityPropertyChanged接口的 ...

  6. WPF入门教程系列四

    WPF之Binding的使用(二) 一.  前言 初学WPF经常被Binding搞得苦不堪言,Binding的重用性就不做介绍了,在WPF应用程序开发中Binding是一个非常重要的部分.WPF也是近 ...

  7. WPF入门教程系列二十三——DataGrid示例(三)

    DataGrid的选择模式 默认情况下,DataGrid 的选择模式为“全行选择”,并且可以同时选择多行(如下图所示),我们可以通过SelectionMode 和SelectionUnit 属性来修改 ...

  8. WPF入门教程系列三——Application介绍(续)

    接上文WPF入门教程系列二——Application介绍,我们继续来学习Application 三.WPF应用程序的关闭 WPF应用程序的关闭只有在应用程序的 Shutdown 方法被调用时,应用程序 ...

  9. WPF入门教程系列二——Application介绍

    一.Application介绍 WPF和WinForm 很相似, WPF与WinForm一样有一个 Application对象来进行一些全局的行为和操作,并且每个 Domain (应用程序域)中仅且只 ...

随机推荐

  1. gridView自己定义做时间排版

    公司有需求要做一个时间排版,原型例如以下 由于要用的gridView,曾经就是它的排版非常多,最看是想用一个checkbox搞定. 后来证实功能可以达到.可是排版是乱的.还是老老实实多写点吧(直接上代 ...

  2. 采购订单me22n 或者me21n增强 (点击保存和回车)

    IF_EX_ME_PROCESS_PO_CUST DATA:l_header TYPE mepoheader, l_item TYPE mepoitem. DATA:lt_items TYPE pur ...

  3. Mysql 和Oracle rows 区别

    mysql> explain select t1.* from t2 ,t1 where t2.id=t1.id and t2.id<3;\ +----+-------------+--- ...

  4. typedef和define具体的具体差别

      1) #define是预处理指令,在编译预处理时进行简单的替换,不作正确性检查,不关含义是否正确照样带入,仅仅有在编译已被展开的源程序时才会发现可能的错误并报错.比如: #define PI 3. ...

  5. json转String 和 String转json 和判断对象类型

    function ajaxGetMenuList(){ $.getJSON("login.do", function(json){ var r = ""; zN ...

  6.   PS2: 这篇文章中的图片绘图工具使用的是Dia (sudo apt-get install dia)。据说yEd也很不错。

    SBCL编译过程 - O.Nixie的专栏 - 博客频道 - CSDN.NET PS2: 这篇文章中的图片绘图工具使用的是Dia (sudo apt-get install dia).据说yEd也很不 ...

  7. MySQL学习笔记之中的一个 MySQL入门

    本人之前接触的关系型数据库主要是oracle和sqlserver,而对于mysql知之甚少,但查阅网上资料发现,mysql与oracle非常相似,所以学起来应该不会非常费劲,在总结的时候可能很多其它的 ...

  8. UVA 534 - Frogger(kruskal扩展)

    UVA 534 - Frogger 题目链接 题意:给定一些点.如今要求一条路径从第一个点能跳到第二个点,而且这个路径上的最大距离是最小的 思路:利用kruskal算法,每次加最小权值的边进去,推断一 ...

  9. Memcahce(MC)系列(一)Memcache介绍、使用、存储、算法、优化

    写在前面:前不久在工作中被问到关于MC一致哈希的问题,由于时隔太久差点儿忘记,特前来恶补一下MC,下面是前几年在工作中学习MC时的一些资料,来历不明,特整理一下,希望对大家的学习也能有帮助. 32 的 ...

  10. 每个Android开发者必须知道的资源集锦

    英文原文:Resources every Android developer must know 随着 Android 平台持续惊人的增长,越来越多的开发人员开始工作于 Android 应用程序.而且 ...