《深入浅出WPF》学习总结之Binding

一、前言

  友好的图形用户界面(Graphics User Interface ,GUI)的流行也就是近十来年的事情,之前应用程序与用户的交互是通过控制台界面(Console User Interface ,CUI)完成的。图形用户界面的操作系统开始在中国流行应该是从Windows 95正式发布开始的,旋即冠以Visual的开发工具(以及Borland公司的一些同类产品)也跟着崭露头角。记得那时候硬件能跑起来Windows 95的就已经相当不错了——图形化的界面还是很消耗硬件资源的。

  GUI作为新鲜事物,理所当然的成为了无论是操作系统制造商还是硬件制造商们关注的焦点。我们暂且撇开硬件不谈单说操作系统开发商,也就是微软。Windows GUI 运行的机理是使用消息(Message)来驱使程序向前运行。消息的主要来源是用户的操作。比如单击鼠标、按下按钮,都会产生消息,消息又会被Windows翻译并送达目标程序然后被程序所处理。这听起来并没有什么影响到应用软件开发的方法论。为了编写Windows上运行的GUI程序,各种开发方法论也必须跟从这种“消息驱动程序”的基本原理。正是沿着这条路发展,才有了Windows API 开发的纯事件驱动,才有了MFC等C++类库的消息驱动、才有了Visual Basic开始到.Net Framework的事件驱动——总之一句话,程序是被来自UI的事件(即封装过的消息)驱使向前的。简称“消息驱动”或“事件驱动”。因为消息或事件大都来自于UI,所有统称他们为“UI驱动程序”

  消息驱动或者事件驱动本身并没有错,但从更高的层次上来看,使用“UI驱动程序”开发程序则是“为了GUI而GUI”、单纯的为了实现程序的GUI化,实际上这已经背离了程序的本质——数据+算法。同时迫使程序员把很多精力放在是实现UI的编程上。这还不算完。随着程序的日趋复杂,UI层面上的代码与用于处理数据的逻辑代码也渐渐纠缠在一起变得难以维护。为了避免这样的问题,程序员们总结出了Model-View-Controller(MVC)和Model-View-Presenter(MVP)等诸多设计模式把UI相关的代码与数据逻辑相关的代码分开。

  让我们回归程序的本质。程序的本质是数据+算法,用户给进一个输入,经过算法的处理程序反馈一个输出——这里,数据处于程序的核心地位。反过头来看“UI驱动程序”,数据处于被动地位,总是在等待程序接受来自UI的消息/事件后被处理或者算法完成处理后被显示。如何在GUI编程时把数据的地位由被动变主动、让数据处于程序的核心呢?这就用到了Data Binding

二、Data Binding在WPF中的地位

  如果把一个应用程序看作是一个城市,那么这个城市内部的交通肯定非常繁忙,但川流不息的不是行人和车辆而是数据。一般情况下,应用程序会具有三层结构,即数据存储层、数据处理层和数据展示层。存储层相当于一个城市的仓储区,有数据库和文件系统构成;处理层(逻辑层)与业务逻辑相关、用于加工处理数据的算法都集中在这里,这一层相当于城市的工业区;展示层的功能是把加工后的数据通过可视化的界面展示给用户或者通过其他种类的接口展示给别的应用程序(界面和接口两个词在英文中均为interface,所以本质上没有什么区别),还需要收集用户的操作,把他们反馈给逻辑层,所以这一层相当于城市的港口区。

  如果我们是一名市长,我们就需要对这个城市的布局和发展负责——仓储区、产业园和港口区、我们该怎么投资呢?每个园区下多大力气开发?每个园区内部应该怎么发展?几个区之间的交通如何规划才能整洁高效?怎样为未来的扩建留有余地...这些都是我们该考虑的问题,其实架构师要做的就是这些!

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

  •   A、数据库内部。
  •   B、读取和写回数据库。
  •   C、业务逻辑。
  •   D、数据展示。
  •   E、界面与逻辑的交互。

  A、B两个部分的算法一般都很稳定,不会轻易去改动,复用性也很高;C处于客户关系最紧密,最复杂,变动也最大、大多数算法都集中在这里;D、E两层负责UI与逻辑的交互,也占有一定的算法。

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

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

  从传统的Windows Form迁移到WPF之后,对于一个三层程序而言,数据存储层由数据库和文件系统来构建。数据传输和处理仍然使用.Net Framework 的ADO.NET等基本类(与Windows Form等开发一样),展示层则使用WPF类库来实现,而展示层与逻辑层的沟通就是用Data Binding来实现。可见,DataBinding在WPF系统中起到的是数据高速公路的作用。有了这条高速公路,加工好的数据会自动送达用户界面加以显示,被用户修改过的数据会自动传回逻辑层,一旦数据被加工好又会被送达用户界面...程序的逻辑层就像一个强有力的引擎不停的运转,用加工好的数据驱动程序的用户界面以文字、图形、动画等形式展示出来——这就是“数据驱动UI”。

  引入Data Binding机制后,D、E两个部分会简化很多。首先,数据在逻辑层与用户界面之间“直来直去”不涉及逻辑问题,这样用户界面部分几乎不包含算法;Data Binding本身就是双向通信,所以相当于把D和E合二为一;对于多个UI元素关注同一个数据的情况,只需要使用Data Binding把这些UI元素一一与数据关联上(以数据为中心的星形结构),当数据变化后这些UI元素会同步显示这一变化。所以前面我们提到的问题就迎刃而解了。更重要的是,经过这样的优化,所有与业务相关的算法都处在数据逻辑层,逻辑层就成为一个独立运转的、完整的体系,而用户界面层则不包含任何代码、完全依赖和从属于数据逻辑层。这样做有两个显而易见的好处,第一,如果把UI层看作是应用程序“皮”、把存储层和逻辑看作是程序的“瓤”,那么我们可以很轻易的把皮从瓤上撕下来并换一个新的;第二,因为数据层能独立运转、自成体系,所以我们可以进行更完善的单元测试而无需借助UI自动化测试工具——你完全可以把单元测试代码想象成一个“看不见的UI”,单元测试只是使用这个“UI”绕过真实的UI直接测试业务逻辑罢了。

三、Binding 基础

  如果把Binding比作数据的桥梁,那么他的两端分别是Binding的源(Source)和目标(Target)数据从哪里来,哪里就是源,Binding是架在中间的桥梁,Binding目标是数据要往哪儿去(所以我们就要把桥架向哪里)。一般情况下,Binding源是逻辑层的对象,Binding目标是UI层的控件对象,这样,数据会源源不断通过Binding送达UI层,被UI层展现,也就完成了数据驱动UI的过程。我们可以想象Binding这座桥梁上铺设了高速公路,我们不但可以控制公路是在原目标之间双向通行还是某个方向的单行道,还可以控制对数据放行的时机,甚至可以在桥上架设一些“关卡”用来转换数据类型或者校验数据正确性。

  对Binding有了形象的基本概念后,让我们看一个最基本的例子。这个例子就是创建一个简单的数据源并通过Binding把它连接到UI元素上。

  首先我们创建一个名为Student的类,这个类的实例将作为数据源来使用。

 1     public class Student
 2     {
 3         private string _name;
 4
 5         public string Name
 6         {
 7             get { return _name; }
 8             set { _name = value; }
 9         }
10
11     }

  可以看到Student这个类非常简单,简单到只有一个string类型的Name属性,前面说过,数据源是一个对象,一个对象身上可能有很多数据,这些数据又通过属性暴露给外界,那么,其中那个数据是你想通过Binding送达UI元素的呢?换句话说,UI上的元素关心的是哪个属性值的变化呢?这个属性就成为Binding的路径(Path)。但光有属性还不行——Binding是一种自动机制,当属性变化后属性要有能力通知Binding,让Binding把变化的传递给UI元素。怎样才能让一个属性具备这种通知Binding值已经变化的能力呢?方法是在属性的set语句中计划一个ProertyChanged事件。这个事件不需要我们自己申明,我们要做的是让作为数据源的类实现System.ComponentModel名称空间中的INotifyPropertyChanged接口。当为Binding设置了数据源后,Binding就会自动侦听来自这个接口的PropertyChanged事件。

  实现了INotifyPropertyChanged接口的Student类看起来是这样的:

 1 public class Student : INotifyPropertyChanged
 2 {
 3     private string _name;
 4
 5     public string Name
 6     {
 7         get { return _name; }
 8         set
 9         {
10             _name = value;
11             if (PropertyChanged != null)
12                 PropertyChanged(this, new PropertyChangedEventArgs("Name"));
13         }
14     }
15
16     public event PropertyChangedEventHandler PropertyChanged;
17 }

  经过这样一升级,当Name属性的值发生变化时PropertyChanged事件就会被激发。Binding接受到这个事件后发现事件的消息告诉它名为Name的属性发生了值的改变,于是就会通知Binding目标端的UI元素显示新的值。

  然后,我们在窗体上准备一个TextBox和一个Button。TextBox将作为Binding目标,我们还会在Button的Click事件发生时改变Student对象的Name属性值。

 1 <Window x:Class="WpfApp2.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6         xmlns:local="clr-namespace:WpfApp2"
 7         mc:Ignorable="d"
 8         Title="MainWindow" Height="110" Width="300">
 9     <StackPanel>
10         <TextBox x:Name="txtBoxName" Margin="5" BorderBrush="Black"/>
11         <Button Content="Test" Margin="5" Click="Button_Click"/>
12     </StackPanel>
13 </Window>

  接下来,我们将进行最重要的一步——使用Binding把数据源和UI元素连接起来,代码如下

 1 public partial class MainWindow : Window
 2 {
 3     Student mStdTest;
 4     public MainWindow()
 5     {
 6         InitializeComponent();
 7
 8         mStdTest = new Student();
 9
10         var binding = new Binding("Name") { Source=mStdTest};
11         BindingOperations.SetBinding(txtBoxName, TextBox.TextProperty, binding);
12     }
13
14     private void Button_Click(object sender, RoutedEventArgs e)
15     {
16         mStdTest.Name += "Name";
17     }
18 }

  让我们逐一解读一下这段代码:这段代码是Windows1类的后台部分,它的UI部分是上面给出的XAML代码。“Student mStdTest”是为Windows1类声明了一个Student类型的成员变量,这样做的目的是为了在Windows1的构造器和Button_Click事件处理器中都能访问由它引用的Student实例(数据源)。

  在Windows1的构造器中"InitializeComponent();"是自动生成的代码,用途是初始化UI元素。"mStdTest = new Student();"这句是创建了一个Student类型的实例并用mStdTest成员变量引用它,这个实例就是我们的数据源。

  在准备Binding的部分,先是用”Binding binding = new Binding("Name"){Source = mStdTest};“声明Binding类型变量并创建实例,并在构造函数和构造器里面设置了Path(访问路径)和Source(访问源)。

  把数据源和目标连接在一起的任务是使用"BindingOperations.SetBinding(...);"方法完成的。这个方法的三个参数是我们记忆的重点。

  1.   第一个参数用于指定Binding的目标,本例中是txtBoxName。
  2.   与数据源的Path原理类似,第二个参数用于为Binding指明把数据送达目标的哪个属性。只是你会发现在这里用的不是对象的属性而是类的一个静态只读(Static Readonly)的依赖属性。其实很好理解,这类属性的值可以通过Binding依赖在其他对象的属性值上,被其他对象属性值所驱动。
  3.   第三个参数很明了,就是指定使用哪个Binding实例将数据源与目标关联起来。

  处于末尾的Button_Click...方法是Button元素的单击事件,在他内部我们对数据源的Name属性进行了更新。运行程序,当你单击Button时,TextBox就会即时显示更新后的Name属性值如下:

原文地址:https://www.cnblogs.com/insipid/p/12232187.html

时间: 2024-11-12 19:31:00

《深入浅出WPF》学习总结之Binding的相关文章

8 WPF学习之深入浅出话属性

转载:http://blog.csdn.net/fwj380891124/article/details/8131080 通过前面的学习,我们已经知道Data Binding是WPF"数据驱动UI"理念的基础.上一章我们将主要的精力放在了Binding的数据源这一端,研究了Binding的Source和Path.本章我们将把目光移向Binding的目标端,研究一下什么样的对象才能作为Binding的Target以及Binding将把数据送往何处. 1.1      属性(Propert

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

本文转自:http://liutiemeng.blog.51cto.com/120361/95273 小序: 怎么直接从2蹦到7啦?!啊哦,实在是不好意思,最近实在是太忙了,忙的原因也非常简单——自己的技术太差了,还有很多东西要学呀.门里门外,发现专业程序员非常重要的一项技能是读别人写的代码,这项技能甚至比自己写代码更重要.Anstinus同学就是读代码的高手,我写的代码他看两眼就知道怎么回事了,并且能够立刻修改,而他的代码我读了好几天还不知道是怎么回事儿呢. 2到7之间是留给XAML语言基础的

《深入浅出WPF》 学习笔记

<深入浅出WPF> 序言 1. 什么是WPF    2. 为什么要学习WPF 第一章 XAML概览 1. XAML是什么? 2. XAML有哪些优点 第二章 从零起步认识XAML 1. 新建WPF项目 2. 剖析最简单的XAML代码 第三章 系统学习XAML语法 1. XAML文档的树形结构 2. XAML中为对象属性赋值的语法 2.1 使用标签的Attribute为对象属性赋值 2.2 使用TypeConverter 2.3 属性元素 2.4 标记扩展(Markup Extensions)

命令——WPF学习之深入浅出

WPF学习之深入浅出话命令 WPF为我们准备了完善的命令系统,你可能会问:“有了路由事件为什么还需要命令系统呢?”.事件的作用是发布.传播一些消息,消息传达到了接收者,事件的指令也就算完成了,至于如何响应事件送来的消息事件并不做任何限制,每个接收者可已用自己的行为来响应事件.也就是说,事件不具有约束力.命令和事件的区别就在于命令具有约束力. 的确,在实际编程工作中,即使只用事件不用命令程序的逻辑一样被驱动的很好,但我们不能够阻止程序员按照自己的习惯去编写代码.比如保存事件的处理器,程序员可以写S

WPF学习09:数据绑定之 Binding to List Data

从WPF学习03:Element Binding我们可以实现控件属性与控件属性的绑定. 从WPF学习07:MVVM 预备知识之数据绑定 我们可以实现控件属性与自定义对象属性的绑定. 而以上两个功能在实际应用中还是不够的,我们经常需要将列表数据与控件属性进行绑定. 例子 ListBox切换人物,下面两个文本框跟随切换,很常用的功能. XAML代码: <Window x:Class="DataTemplate.MainWindow" xmlns="http://schema

WPF学习之Binding的学习(一)

程序的本质是数据加算法.通俗一点来说呢,其实就是用户给一个输入,经过算法的处理之后,计算机反馈一个输出给用户.可以很清楚的看出,在这个过程中,处于主导地位的是数据.但是,当我们在进行图形用户界面(Graphic User Interface,GUI)编程的时,数据总是处于被动地位.也就是说,程序总是在等待接收来自UI的消息/事件,在这些事件被处理之后,才会反馈给用户一个输出.我们用Data Binding可以在GUI编程时让数据回到程序的核心. **1.Data Binding在WPF中的地位*

《深入浅出WPF》笔记——绘画与动画

<深入浅出WPF>笔记——绘画与动画 本篇将记录一下如何在WPF中绘画和设计动画,这方面一直都不是VS的强项,然而它有一套利器Blend:这方面也不是我的优势,幸好我有博客园,能记录一下学习的过程.在本记录中,为了更好的理解绘画与动画,多数的例子还是在VS里面敲出来的.好了,不废话了,现在开始. 一.WPF绘画 1.1基本图形 在WPF中可以绘制矢量图,不会随窗口或图型的放大或缩小出现锯齿或变形,除此之外,XAML绘制出来的图有个好处就是便于修改,当图不符合要求的时间,通常改某些属性就可以完成

【分享】深入浅出WPF全系列教程及源代码

来源:http://blog.csdn.net/yishuaijun/article/details/21345893 本来想一篇一篇复制的.但是考虑到别人已经做过了,就没有必要了吧,就给大家一个目录吧 前言:WPF之What&Why WPF之XMAL----XMAL概览 WPF之从0开始学习XMAL WPF学习---系统的学习XAML语法 WPF学习之X名称空间详解 WPF学习之控件与布局 WPF之Binding深入探讨 WPF学习之深入浅出话属性 WPF之深入浅出话事件 WPF学习之深入浅出

【分享】深入浅出WPF全系列教程及源码

本人10月份提出离职,可是交接非常慢,预计年底才会交接完,趁着交接之际,自学了一下WPF,由于这是微软未来的发展趋势,自WIN7以来包含前不久公布的WIN8,核心还是WPF,在此,将自己的学习成果做一个总结,同一时候将自己学习积累的东西和广大博友分享,希望大家能共同进步.在文章的结尾我会将全系列的源码地址提供给大家,有须要的朋友能够下载下来自己研究研究.由于之前申请WPF博客专栏一直没有申请下来,所以用一篇文章来结束我的学习,同一时候也给初学的人提供一个学习文件夹,提高学习效率,废话不多说,直接

【【分享】深入浅出WPF全系列教程及源码 】

因为原书作者的一再要求,在此声明,本书中的部分内容引用了原书名为<深入浅出WPF>的部分内容,假设博文不能满足你现有的学习须要,能够购买正版图书! 本人10月份提出离职,可是交接非常慢,预计年底才会交接完,趁着交接之际,自学了一下WPF,由于这是微软未来的发展趋势,自WIN7以来包含前不久公布的WIN8,核心还是WPF,在此,将自己的学习成果做一个总结,同一时候将自己学习积累的东西和广大博友分享,希望大家能共同进步.在文章的结尾我会将全系列的源码地址提供给大家,有须要的朋友能够下载下来自己研究