深入浅出WPF之Binding

  • Binding
    • Binding基础
      • Binding模型
    • 把控件作为Binding源与Binding标记扩展
    • Binding的(Path)路径
      • Binding支持多级路径(一直“.”下去)
      • 集合类型的索引器作为Path来使用
      • 默认元素作为Path使用
      • “没有Path”的Binding
      • DataContext
      • DataTemplate
      • DataTable
      • XML数据作为Binding源
      • 使用LINQ检索结果作为Binding的源
      • Binding的RelativeSource
    • MultiBinding

Binding

Binding基础

把Binding比作数据的桥梁,那么它的两端分别是Binding的源(Source)和目标(Target)。

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

class Student
{private string name;public string Name{get{ return name;}set{ name = value;}}
}

再次,我们使用一个称为Binding的路径(Path)的属性来通过Binding送达UI元素。但光有属性还不行——Binding是一种自动机制,当值变化后属性要有能力通知Binding,让Binding把变化传递给UI元素。怎样才能让一个属性具备这种通知Binding值已经变化的能力呢?方法是在属性的set语句中激发一个PropertyChanged事件。这个事件不需要我们自己声明,我们要做的是让
作为数据源的类实现System.ComponentModel名称空间中的INotifyPropertyChanged接口。当为Binding设置了数据源后,Binding就会自动侦听来自这个接口的PropertyChanged事件。

class Student : INotifyPropertyChanged
{public event PropertyChangedEventHandler PropertyChanged;  private string name;public string Name{get{return name;}set{name = value;// 激发事件if(this.PropertyChanged != null){this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Name"));}}}
}

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

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

<StackPanel><TextBox x:Name="textBoxName"  BorderBrush="Black"  Margin="5" /><Button Content="Add Age"  Margin="5"  Click="Button_Click" />
</StackPanel>

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

 public partial class MainWindow : Window{Student stu;public MainWindow(){InitializeComponent();//准备数据源stu = new Student();//准备BindingBinding binding = new Binding();binding.Source = stu;binding.Path = new PropertyPath("Name");//使用Binding连接数据源与Binding目标BindingOperations.SetBinding(this.textBoxName, TextBox.TextProperty, binding);}private void Button_Click(object sender, RoutedEventArgs e){stu.Name += "Name";}}

主要流程:

  1. 创建数据源
  2. 建立binding(binding实例,path)
  3. 将数据源与binding目标建立连接(BindingOperations.SetBinding()方法)

其中3中方法的3个参数是我们记忆的重点

  • 第一个参数用于指定Binding的目标,本例中是this.textBoxName。
  • 与数据源的Path原理类似,第二个参数用于为Binding指明把数据送达目标的哪个属性。只是你会发现在这里用的不是对象的属性而是类的一个 静态只读(static readonly) 的DependencyProperty类型成员变量!这就是我们后面要详细讲述的与Binding息息相关的依赖属性。其实很好理解,这类属性的值可以通过Binding依赖在其他对象的属性值上,被其他对象的属性值所驱动。
  • 第三个参数很明了,就是指定使用哪个Binding实例将数据源与目标关联起来。

实际工作中,实施Binding的代码可能与上面看到的不太一样,原因是TextBox这类UI元素的基类FrameworkElement对BindingOperations.SetBinding(…)方法进行了封装,封装的结果 也叫SetBinding,只是参数列表发生了变化。代码如下:

public BindingExpressionBase SetBinding(DependencyProperty dp, BindingBase binding){return BindingOperations.SetBinding(this, dp, binding);}

同时,有经验的程序员还会借助Binding类的构造器及C# 3.0的对象初始化器语法来简化代码。这样一来,上面这段代码有可能成为这样:

public Window()
{InitializeComponent();// 三合一操作this.textBoxName.SetBinding(TextBox.TextProperty, new Binding("Name"){ Source = stu  = new Student() });}

Binding模型

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

前面提过,大多数情况下Binding的源是逻辑层的对象,但有时候为了让UI元素产生一些联动效果也会使用Binding在控件间建立关联。下面的代码是把一个TextBox的Text属性关联在了Slider的Value属性上。

<Window x:Class="WpfApplication1.Window1"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="Control as Source" Height="110" Width="300"><StackPanel><TextBox Name="textBox1" Text="{Binding Path=Value,ElementName=slider1}" BorderBrush = "Black" Margin="5" /><Slider Name="slider1" Maximum= "100" Minimum = "0" Margin="5"/></StackPanel>
</Window>

正如大家所见,除了可以在C#代码中建立Binding外在XAML代码里也可以方便地设置Binding。
值得注意的是,在C#代码中可以访问XAML代码中声明的变量但XAML代码中却无法访问C#代码中声明的变量,因此,要想在XAML中建立UI元素与逻辑层对象的Binding还要颇费些周折,把逻辑层对象声明为XAML代码中的资源(Resource)。
看这句XAML代码,它使用了Binding标记扩展语法:

<TextBox x:Name="textBox1" Text="{Binding Path=Value,ElementName=slider1}" BorderBrush = "Black" Margin="5" />

与之等价的C#代码是:

this.textBox1.SetBinding(TextBox.TextProperty, new Binding("Value"),{ElementName = "slider1"});

因为Binding类的构造器本身可以接收Path作为参数,所以也常写为:

<TextBox x:Name="textBox1" Text="{Binding Value,ElementName=slider1}" BorderBrush = "Black" Margin="5" />

注意
因为在C#代码中我们可以直接访问控件对象,所以一般也不会使用Binding的ElementName属性,而是直接把对象赋值给Binding的Source属性。

Binding的(Path)路径

Binding支持多级路径(一直“.”下去)

比如,如果我们想让一个TextBox显示另外一个TextBox的文本长度,我们可以写:

    <StackPanel><TextBox x:Name="textBox1" BorderBrush= "Black" Margin="5" ></TextBox><TextBox x:Name="textBox2" Text="{Binding Path=Text.Length, ElementName= textBox1, Mode=OneWay}" BorderBrush="Black" Margin="5"></TextBox></StackPanel>

等效的C#代码是:

this.textBox2.SetBinding(TextBox.TextProperty, new Binding("Text.Length"), {Source = this.textBox1, Mode = BindingMode.OneWay});

集合类型的索引器作为Path来使用

比如我想让一个TextBox显示另一个TextBox文本的第四个字符,我们可以这样写:

    <StackPanel><TextBox x:Name="textBox1" BorderBrush= "Black" Margin="5" ></TextBox><TextBox x:Name="textBox2" Text="{Binding Path=Text.[3], ElementName= TextBox1, Mode=OneWay}" BorderBrush="Black" Margin="5"></TextBox></StackPanel>

等效的C#代码是:

this.textBox2.SetBinding(TextBox.TextProperty, new Binding("Text.[3]"), {Source = this.textBox1, Mode = BindingMode.OneWay});

默认元素作为Path使用

当使用一个集合或者DataView作为Binding源时,如果我们想把它的默认元素当作Path使
用,则需要使用这样的语法:

List<string> list = new List<string> () {"Tim", "Tom", "Smith"}
this.textBox1.SetBinding(TextBox.TextProperty, new Binding("/"), {Source = list});
this.textBox2.SetBinding(TextBox.TextProperty, new Binding("/Length"), {Source = list, Mode = BindingMode.OneWay});
this.textBox3.SetBinding(TextBox.TextProperty, new Binding("/[2]"), {Source = list, Mode = BindingMode.OneWay});

如果集合元素的属性仍然还是一个集合,我们想把子级集合中的元素当作Path,则可以使
用多级斜线的语法(即一路“斜线”下去),例如:

     class City{public string Name { get; set; }}class Province{public string Name { set; get;  }public List<City> CityList = new List<City>  { get; set; }}class Nation { public string Name { set; get; }public List <Province> ProvList = new List <Province> { get; set; }}//BindingList<Nation> nationList = new List<Nation>() {/* 初始化... */ };this.textBox1.SetBinding(TextBox.TextProperty, new Binding("/Name"), { Source = nationList});this.textBox2.SetBinding(TextBox.TextProperty, new Binding("/ProvList.Name"), { Source = nationList });this.textBox2.SetBinding(TextBox.TextProperty, new Binding("/Provinces/CityList.Name"), { Source = nationList });

“没有Path”的Binding

Binding源本身就是数据且不需要Path来指明。典型的,string、int等基本类型就是这样,他们的实例本身就是数据,我们无法指出通过它的哪个属性来访问这个数据,这时我们只需将Path的值设置为“.” 就可以了。在XAML代码里这个“.”可以省略不写,但在C#代码里却不能省略。

DataContext

可以不用为Binding设置Source,而从xaml中创建context控件引用后台c#创建的类,通过设置Path的方式,让Binding自己去寻找类的属性。

DataTemplate

模板,可以作为资源,创建一些元素丰富的控件(例如图片)的实例。

注意

最后特别提醒大家一点:在使用集合类型作为列表控件的ItemsSource时一般会考虑使用
ObservableCollection 代 替 List, 因 为 ObservableCollection 类 实 现 了
INotifyCollectionChanged和INotifyPropertyChanged接口,能把集合的变化立刻通知显示它
的列表控件,改变会立刻显现出来。

DataTable

列表控件与DataTable之间直接建立Binding

XML数据作为Binding源

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

  • 符合 DOM (Document Object Modle,文档对象模型)标准的类库:包括
    XmlDocument、XmlElement、XmlNode、XmlAttribute等类。这套类库的特点是中规中矩、
    功能强大,但也背负了太多XML的传统和复杂。
  • 以 LINQ(Language- Integrated Query成查询)为基础的类库:包括XDocument、XElement、XNode、XAttribute等类。这套类库的特点是可以使用LINQ进行查询和操作,方便快捷。
    XML文本是树形结构的,所以XML可以方便地用于表示线性集合(如ArrayList等)和树形结构数据。

需要注意的是,当使用XML数据作为Binding的Source时我们将使用XPath属性而不是Path属
性来指定数据的来源。

使用LINQ检索结果作为Binding的源

使用LINQ,我们可以方便地操作集合对象、DataTable对象和XML对象而不必动辄
就把好几层foreach循环嵌套在一起却只是为了完成一个很简单的任务。

Binding的RelativeSource

当我们不知道准确的Source的名称时,我们可以使用RelativeSource类来获得控件之间相对位置和类型的源。

MultiBinding

具有一个名为Bindings的属性,其类型是Collection,通过这个属性MultiBinding把一组Binding对象聚合起来,处在这个集合中的Binding对象可以拥有自己的数据校验与转换机制,它们汇集起来的数据将共同决定传往MultiBinding目标的数据,如图所示。

深入浅出WPF学习笔记之Binding相关推荐

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

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

  2. WPF学习笔记(数据绑定篇3)

    接上回的<WPF学习笔记(数据绑定篇2)>,继续 BindValidation 此示例演示了: 如何使用错误模板: 使用样式显示错误信息: 如何在校验发生异常时执行回调: 首先,你可以看见 ...

  3. WPF学习笔记(7):DataGrid中数字自定义格式显示

    WPF学习笔记(7):DataGrid中数字自定义格式显示 原文:WPF学习笔记(7):DataGrid中数字自定义格式显示 DataGrid中数据显示如下图,数据格式比较杂乱.希望达到以下要求:(1 ...

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

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

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

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

  6. 深入浅出MFC学习笔记

    深入浅出MFC学习笔记 ithzhang CSDN博客:http://blog.csdn.net/ithzhang/article/category/1159054 转载于:https://blog. ...

  7. 深入浅出SSD 学习笔记整理——Johnathan Sung

    SSD ( Solid State Drive),即固态硬盘,是一种以半导体闪存( NAND Flash)作为介质的存储设备.和传统机械硬盘(Hard Disk Drive,HDD)不同,SSD以半导 ...

  8. 深入浅出DPDK学习笔记(3)——— Cache和内存

    深入浅出DPDK学习笔记(3)--- Cache和内存 系统架构的演进 Cache系统简介 Cache的种类 TLB Cache Cache地址映射和变换 全关联型Cache 直接关联型Cache 组 ...

  9. 【转载】wpf学习笔记1

    http://blog.csdn.net/fantasiax/article/details/4575968 深入浅出WPF(7)--数据的绿色通道,Binding(上) 小序: 怎么直接从2蹦到7啦 ...

最新文章

  1. 开启mysql慢查询日志,不重启数据库的方法
  2. 对下图所示的连通网络G,用克鲁斯卡尔(Kruskal)算法求G的最小生成树T,请写出在算法执行过程中,依次加入T的边集TE中的边。说明该算法的基本思想及贪心策略,并简要分析算法的时间复杂度
  3. PHP做二次开发:本机安装ThinkCMF系统
  4. 搭建Harbor私有仓库
  5. python查看对象占用内存_『Python』内存分析_List对象内存占用分析
  6. python双向索引什么意思_Python 双向链表的实现
  7. 【Social listening实操】作为一个合格的“增长黑客”,你还得重视外部数据的分析!...
  8. vtp协议服务器配置,配置交换机VTP协议
  9. Java take和poll,Java多线程-CompletionService
  10. List<? extends T>和List<? super T>之间的区别
  11. pic16f1829 c语言,PIC16F1829 TIMER2初始化程序及应用
  12. vue怎么调用子元素的方法_vue 父组件中调用子组件函数的方法
  13. 神经网络编译器图层面IR
  14. 哎哟,不错哦之玩乐动物园
  15. 宽带服务器盒信号灯红色闪烁,光纤灯红色闪烁怎么解决(图文)
  16. EIP712以太坊签名和验签
  17. android textview 显示表情和文字 表情带超链接
  18. 信息无障碍专业术语---信息无障碍
  19. SQLZOO练习答案(一):SELECT names/zh
  20. 阿里云Centos7.x安装中文支持

热门文章

  1. Uniapp中调整web-view的高度、获取当前的web-view页面URL
  2. 插件技术被浏览器抛弃,网银安全何去何从?
  3. AWS EKS使用Pod安全组
  4. Solarwinds推出新免费网管工具-IP SLA Monitor
  5. 笔记本外接显示器卡顿解决方案
  6. lammps构建高熵合金模型+结构优化初步筛选能量最小的结构
  7. 前端HTML+CSS面试题汇总一
  8. CANoe——CAPL案例
  9. 为什么会有缓冲区溢出攻击专栏
  10. PHP之Zip扩展,解压缩文件,ZipArchive类