WPF之Binding深入探讨
Posted 山涧清泉
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF之Binding深入探讨相关的知识,希望对你有一定的参考价值。
原文:http://blog.csdn.net/fwj380891124/article/details/8107646
1,Data Binding在WPF中的地位
程序的本质是数据+算法。数据会在存储、逻辑和界面三层之间流通,所以站在数据的角度上来看,这三层都很重要。但算法在3层中的分布是不均匀的,对于一个3层结构的程序来说,算法一般分布在这几处:
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。
从传统的Winform转移到WPF上,对于一个三层程序而言,数据存储层由数据库和文件系统组成,数据传输和处理仍然使用.NetFramework的ADO.NET等基本类(与Winform开发一样)。展示层则使用WPF类库来实现,而展示层和逻辑层的沟通就使用Data Binding来实现。可见,Data Binding在WPF中所起的作用就是高速公路的作用。有了这条高速公路,加工好的数据自动送达用户界面并加以显示,被用户修改过的数据也会自动传回业务逻辑层,一旦数据被加工好又会被送往界面。。。。程序的逻辑层就像是一个强有力的引擎一直在运作,用加工好的数据驱动用户界面也文字、图形、动画等形式把数据显示出来------这就是数据驱动UI。
引入Data Binding之后,D,E两部分被简化了很多。首先,数据在逻辑层和用户界面直来之去、不涉及逻辑问题,这样的用户界面部分基本上不包含算法:Data Binding本身就是双向通信,所以相当于把D和E合二为一;对于多个UI元素关注同一个数据的情况,只需要用Data Binding将这些UI元素和数据一一关联上(以数据为中心的星形结构),当数据变化后,这些UI元素会同步显示这一变化。前面提到的问题也都迎刃而解了。更重要的是经过这样的优化,所有与业务逻辑相关的算法都处在业务逻辑层,逻辑层成了一个可以独立运转,完整的体系,而用户界面则不需要任何逻辑代码。完全依赖和从属于业务逻辑层。这样做有两个显而易见的好处,第一:如果把UI看做是应用程序的皮,把存储层和逻辑层看作是程序的瓤,我们可以很轻易的把皮撕下来换一个新的。第二:因为数据层能够独立运作,自成体系,所以我们可以进行更完善的单元测试而无需借助UI自动化测试工具----你完全可以把单元测试看作是一个“看不见的UI”,单元测试只是使用这个UI绕过真实的UI直接测试业务逻辑罢了。
2 , Binding 基础
如果把Binding比作数据的桥梁,那么它的两端分别是源(Source)和目标(Target)。数据丛哪里来哪里就是源,到哪里去哪里就是目标。一般情况下,Binding的源是业务逻辑层的对象,Binding的目标是UI层的控件对象。这样数据就会源源不断的通过Binding送达UI界面,被UI层展现,这就完成了数据驱动UI的过程。有了这座桥梁,我们不仅可以控制车辆在源与目标之间是双向通行还是单向通行。还可以控制数据的放行时机,甚至可以在桥上搭建一些关卡用来转换数据类型或者检验数据的正确性。
通过对Binding有了一个基本概念之后,让我们看一个最基本的例子。这个例子是创建一个简单的数据源并通过Binding把它连接到UI元素上。
首先,我们创建一个名为"Student"的类,这个类的实例将作为数据源来使用。
- publicclass Student
- {
- privatestring name;
- publicstring Name
- {
- get { return name; }
- set
- {
- name = value;
- }
- }
public class Student { private string name; public string Name { get { return name; } set { name = value; } }
这个类很简单,简单到只有一个string类型的Name属性。前面说过数据源是一个对象,一个对象本身可能会有很多数据,这些数据又通过属性暴露给外界。那么其中哪个元素是你想通过Binding送达UI元素的呢,换句话说,UI元素关心的是哪个属性值的变化呢?这个属性值称之为Binding的路径(Path)。但光有属性还不行-------Binding是一种自动机制,当值变化后属性要有能力通知Binding,让Binding把变化传递给UI元素。怎样才能让一个属性具备这种通知Binding值已经改变的能力呢?方法是在属性的Set语句中激发一个PropertyChanged事件。这个事件不需要我们自己声明,我们要做的事是让作为数据源的类实现System.ComponentModel名称空间中的INotifyPropertyChanged接口。当为Binding设置了数据源之后,Binding就会自动侦听来自这个接口PropertyChanged事件。
实现INotifyPropertyChanged接口的类看起来是这样:
- publicclass Student : INotifyPropertyChanged
- {
- privatestring name;
- publicstring Name
- {
- get { return name; }
- set
- {
- name = value;
- if (PropertyChanged != null)
- {
- this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Name"));
- }
- }
- }
- publicevent PropertyChangedEventHandler PropertyChanged;
- }
public class Student : INotifyPropertyChanged { private string name; public string Name { get { return name; } set { name = value; if (PropertyChanged != null) { this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Name")); } } } public event PropertyChangedEventHandler PropertyChanged; }
经过这样一升级,当Name属性的值发生变化时PropertyChanged事件就会被激发,Binding接收到这个事件后发现事件的消息告诉它是Name属性值发生了变化,于是通知Binding目标端的UI元素显示新的值。
然后我们在窗体上准备一个TextBox和Button,代码如下:
- <Windowx:Class="WpfApplication1.MainWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="MainWindow"Height="350"Width="525">
- <Grid>
- <TextBoxHeight="23"HorizontalAlignment="Left"Margin="185,43,0,0"Name="textBox1"VerticalAlignment="Top"Width="120"/>
- <ButtonContent="Button"Height="23"HorizontalAlignment="Left"Margin="209,96,0,0"Name="button1"VerticalAlignment="Top"Width="75"Click="button1_Click"/>
- </Grid>
- </Window>
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <TextBox Height="23" HorizontalAlignment="Left" Margin="185,43,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" /> <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="209,96,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" /> </Grid> </Window>
后台代码这样写:
- /// <summary>
- /// MainWindow.xaml 的交互逻辑
- /// </summary>
- public partial class MainWindow : Window
- {
- Student stu = null;
- public MainWindow()
- {
- InitializeComponent();
- stu = new Student();
- Binding bind = new Binding();
- bind.Source = stu;
- bind.Path = new PropertyPath("Name");
- this.textBox1.SetBinding(TextBox.TextProperty, bind);
- }
- privatevoid button1_Click(object sender, RoutedEventArgs e)
- {
- stu.Name += "f";
- new Window1().Show();
- }
- }
/// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { Student stu = null; public MainWindow() { InitializeComponent(); stu = new Student(); Binding bind = new Binding(); bind.Source = stu; bind.Path = new PropertyPath("Name"); this.textBox1.SetBinding(TextBox.TextProperty, bind); } private void button1_Click(object sender, RoutedEventArgs e) { stu.Name += "f"; new Window1().Show(); } }
让我们逐句解释一下这段代码,这段代码是MainWIndow的后台代码,它的前端代码就是上面的XAML代码。“Student stu;”是为MainWindow声明一个Student类型的成员变量,这样做的目的是为了在MainWindow的构造器和Button.Click事件处理器中都可以访问到由它引用的Student实例(数据源)。
在MainWindow的构造器中“InitializeComponent();”是自动生成的代码,用途是初始化UI元素。“stu=new Student();”这句是创建一个Student实例并用stu成员变量引用它,这个对象就是我们的数据源。
在准备Binding的部分,先使用“Binding bind = new Binding();”声明Binding类型变量并创建实例,然后使用“bind.Source=stu;”为Binding实例指定数据源,最后使用“bind.Path= new PropertyPath(\'Name\')”语句为Binding指定访问路径。
把数据源和目标连接在一起的任务是使用“BindingOperations.SetBinding(...)”方法完成的,这个方法的3个参数是我们记忆的重点:
第一个参数是指定Binding的目标,本例中的this.textBoxName。
与数据源的Path原理类似,第二个参数用于为Binding指明为Binding指明把这个数据送达目标的哪个数据。
第三个参数很明显,就是指定使用哪个Binding实例将数据源和目标关联起来。
运行程序,单击按钮我们将会看到如下的效果图:
通过上面的例子,我们已经在头脑中建立起来如图所示的模型
先用这个做基础,后面我们将研究Binding的每个特点。
1.3 Binding的源与路径
Binding 的源也就是数据的源头。Binding对源的要求并不苛刻------只要它是一个对象,并且通过属性(Property)公开自己的数据,它就能作为Binding 的源。
前面一个例子已经向大家证明,如果想让作为Binding源的对象具有自动通知Binding自己属性值已经已经变化的能力。那么就需要让类实现INotifyChanged接口并在属性的Set语句中激发PropertyChanged事件。在日常生活中,除了使用这种对象作为数据源之外,我们还有更多的选择,比如控件把自己的容器或子集元素当源、用一个控件做为另一个控件的数据源,把集合作为ItemControl的数据源、使用XML作为TreeView或Menu的数据源。把多个控件关联到一个“数据制高点”上,甚至干脆不给Binding指定数据源、让他自己去找。下面我们就分述这些情况。
1.3.1 把控件作为Binding源与Binding标记扩展。
前面讲过,大多数情况下Binding的源是逻辑层对象,但有的时候为了让UI产生联动效果也会使用Binding在控件间建立关联。下面的代码是吧一个TextBox的Text属性关联到Slider的Value的属性上。
- <Windowx:Class="WpfApplication1.Window1"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="Window1"Height="321"Width="401">
- <Grid>
- <TextBoxHeight="23"HorizontalAlignment="Left"Margin="141,46,0,0"Name="textBox1"VerticalAlignment="Top"Width="120"Text="{Binding Path=Value,ElementName=slider1}"/>
- <SliderHeight="23"HorizontalAlignment="Left"Margin="84,106,0,0"Name="slider1"VerticalAlignment="Top"Width="212"/>
- <ButtonContent="Button"Height="23"HorizontalAlignment="Left"Margin="166,197,0,0"Name="button1"VerticalAlignment="Top"Width="75"Click="button1_Click"/>
- </Grid>
- </Window>
<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="321" Width="401"> <Grid> <TextBox Height="23" HorizontalAlignment="Left" Margin="141,46,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding Path=Value,ElementName=slider1}"/> <Slider Height="23" HorizontalAlignment="Left" Margin="84,106,0,0" Name="slider1" VerticalAlignment="Top" Width="212" /> <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="166,197,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" /> </Grid> </Window>
运行效果如下图:
正如大家所见,除了可以在C#中建立Binding外在XAML代码里也可以方便的设置Binding,这就给设计师很大的自由度来决定UI元素之间的关联情况。值得注意的是,在C#代码中,可以访问在XAML中声明的变量但是XAML中不能访问C#中声明的变量,因此,要想在XAML中建立UI元素和逻辑对象的Binding还要颇费些周折,把逻辑代码声明为XAML中的资源(Resource),我们放资源一章去讲。
回头来看XAML代码,它使用了Binding标记扩展语法:
- <TextBoxHeight="23"HorizontalAlignment="Left"Margin="141,46,0,0"Name="textBox1"VerticalAlignment="Top"Width="120"Text="{Binding Path=Value,ElementName=slider1}"/>
<TextBox Height="23" HorizontalAlignment="Left" Margin="141,46,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding Path=Value,ElementName=slider1}"/>
与之等价的C#代码是:
- this.textBox1.SetBinding(TextBox.TextProperty, new Binding("Value") { ElementName="Slider1"});
this.textBox1.SetBinding(TextBox.TextProperty, new Binding("Value") { ElementName="Slider1"});
因为Binding类的构造器本身具有可以接收Path的参数,所以也常写作:
- <TextBoxHeight="23"HorizontalAlignment="Left"Margin="141,46,0,0"Name="textBox1"VerticalAlignment="Top"Width="120"Text="{Binding Value,ElementName=slider1}"/>
<TextBox Height="23" HorizontalAlignment="Left" Margin="141,46,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding Value,ElementName=slider1}"/>
注意:
因为我们在C#代码中可以直接访问控件对象,所以一般不会使用Binding的ElementName属性,而是直接赋值给Binding的Sourece属性。
Binding的标记扩展语法,初看有些平淡甚至有些别扭,但细品就会体验到其的精巧之处。说它别扭,是因为我们已经习惯了Text=“Hello World”这种键--值式的赋值方式,而且认为值与属性的值类型一定要一致-------大脑很快会质询Text="{Binding Value,ElementName=Slider1}"的字面意思----Text的类型是String,为什么要赋一个Binding类型的值呢?其实我们并不是为Text属性赋值,为了消除这种误会,我们可以把代码读作:为Text属性设置Binding为...。再想深一步,我们不是经常把函数视为一个值吗?只是这个值在函数执行之后才能得到。同理,我们也可以把Binding视为一种间接的、不固定的赋值方式-----Binding扩展很恰当的表达了这个赋值方式。
1.3.2 控制Binding的方向及数据更新
Binding在源与目标之间架起了沟通的桥梁,默认情况下数据即可以通过Binding送达目标,也可以通过目标回到源(收集用户对数据的修改)。有时候数据只需要展示给用户,不需要用户修改,这时候可以把Binding模式设置为从目标向源的单向沟通以及只在Binding关系确立时读取一次数据,这需要我们根据实际情况选择。
控制Binding数据流向的属性是Model,它的类型是BindingModel的枚举。BindingModel可以取值为TwoWay、OneWay、OneTime、OneWayToSource和Default。这里的Default指的是Binding的模式会根据目标是实际情况来确定,不如是可以编辑的(TextBox的Text属性),Default就采用双向模式。如果是TextBlock,不可编辑,就使用单向模式。
接上一节的小例子,拖动Slider手柄时,TextBox就会显示Slider的当前值(实际上这一块涉及到一个Double到String类型的转换,暂且忽略不计);如果我们在TextBox里面输入一个恰当的值按Tab键、让焦点离开TextBox,则Slider手柄就会跳转至相应的值那里。如下图所示:
为什么一定要在TextBox失去焦点以后才改变值呢?这就引出了Binding的另外一个属性-----UpdateSourceTrigger,它的类型是UpdateSourceTrigger枚举,可取值为PropertyChanged、LostFous、Explicit和Default。显然,对于Text的Default行为与LostFocus一致,我们只需要把这个值改成PropertyChanged,则Slider就会随着输入值的变化而变化了。
注意:
顺便提一句,Binding还具有NotifyOnSourceUpdated属性和NotifyOnTargetUpdated两个bool类型是属性。如果设置为True,则在源或目标被更新以后就会触发相应的SourceUpdated事件和TargetUpdated事件。实际工作中我们可以监听这两个事件来找出来哪些数据或控件被更新了。
1.3.3 Binding的路径(Path)
做为Binding的源可能会有很多属性,通过这些属性Binding源可以把数据暴露给外界。那么,Binding到底需要关注哪个属性值呢?就需要用Binding的Path属性来指定了。例如前面这个例子,我们把Slider控件对象作为数据源,把它的Value属性作为路径。
尽管在XAML代码中或者Binding类的构造器参数列表中我们使用字符串来表示Path,但Path的实际类型是PropertyPath。下面让我们来看看如何创建Path来应付实际情况(我将使用C#和XAML两种代码进行描述)。
最简单的方法就是直接把Binding关联到Binding源的属性上,前面的例子就是这样,语法如下:
- <TextBoxHeight="23"HorizontalAlignment="Left"Margin="141,46,0,0"Name="textBox1"VerticalAlignment="Top"Width="120"Text="{Binding Path=Value,ElementName=slider1}"/>
<TextBox Height="23" HorizontalAlignment="Left" Margin="141,46,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding Path=Value,ElementName=slider1}"/>
等效的C#代码就是:
- this.textBox1.SetBinding(TextBox.TextProperty, new Binding("Value") {Source=slider1});
this.textBox1.SetBinding(TextBox.TextProperty, new Binding("Value") {Source=slider1});
Binding还支持多级路径(通俗的讲就是一路“点”下去),比如,我们想让一个TextBox显示另外一个TextBox内容的长度,我们可以这样写:
- <TextBoxHeight="23"HorizontalAlignment="Left"Margin="152,50,0,0"Name="textBox1"VerticalAlignment="Top"Width="158"/>
- <TextBoxHeight="23"HorizontalAlignment="Left"Margin="152,105,0,0"Name="textBox2"Text="{Binding Path=Text.Length,ElementName=textBox1,Mode=OneWay}"VerticalAlignment="Top"Width="158"/>
<TextBox Height="23" HorizontalAlignment="Left" Margin="152,50,0,0" Name="textBox1" VerticalAlignment="Top" Width="158" /> <TextBox Height="23" HorizontalAlignment="Left" Margin="152,105,0,0" Name="textBox2" Text="{Binding Path=Text.Length,ElementName=textBox1,Mode=OneWay}" VerticalAlignment="Top" Width="158"/>
等效的C#代码是:
- this.textBox2.SetBinding(TextBox.TextProperty, new Binding("Text.Length") {Source = textBox1, Mode= BindingMode.OneWay });
this.textBox2.SetBinding(TextBox.TextProperty, new Binding("Text.Length") {Source = textBox1, Mode= BindingMode.OneWay });
运行效果如下图:
我们知道,集合类型是索引器(Indexer)又称为带参属性。既然是属性,索引器也能作为Path来使用。比如我们想让一个TextBox显示另外一个TextBox的第4个字符,我们可以这样写:
- <TextBoxHeight="23"HorizontalAlignment="Left"Margin="152,50,0,0"Name="textBox1"VerticalAlignment="Top"Width="158"Text="ABCDE"/>
- <TextBoxHeight="23"HorizontalAlignment="Left"Margin="152,105,0,0"Name="textBox2"Text="{Binding Path=Text[3],ElementName=textBox1,Mode=OneWay}"VerticalAlignment="Top"Width="158"/>
<TextBox Height="23" HorizontalAlignment="Left" Margin="152,50,0,0" Name="textBox1" VerticalAlignment="Top" Width="158" Text="ABCDE" /> <TextBox Height="23" HorizontalAlignment="Left" Margin="152,105,0,0" Name="textBox2" Text="{Binding Path=Text[3],ElementName=textBox1,Mode=OneWay}" VerticalAlignment="Top" Width="158"/>
C#代码如下:
- this.textBox2.SetBinding(TextBox.TextProperty, new Binding("Text.[3]") { Source=textBox1,Mode= BindingMode.OneWay});
this.textBox2.SetBinding(TextBox.TextProperty, new Binding("Text.[3]") { Source=textBox1,Mode= BindingMode.OneWay});
我们甚至可以把Text与[3]之间的点去掉,一样可以正确工作,运行效果如下图:
当使用一个集合或者DataView做为数据源时,如果我们想把它默认的元素做为数据源使用,则需要使用下面的语法:
- List<string> infos = new List<string>() { "Jim","Darren","Jacky"};
- textBox1.SetBinding(TextBox.TextProperty, new Binding("/") { Source=infos});
- textBox2.SetBinding(TextBox.TextProperty, new Binding("/[2]") { Source = infos, Mode= BindingMode.OneWay });
- textBox3.SetBinding(TextBox.TextProperty, new Binding("/Length") { Source = infos, Mode= BindingMode.OneWay });
List<string> infos = new List<string>() { "Jim","Darren","Jacky"}; textBox1.SetBinding(TextBox.TextProperty, new Binding("/") { Source=infos}); textBox2.SetBinding(TextBox.TextProperty, new Binding("/[2]") { Source = infos, Mode= BindingMode.OneWay }); textBox3.SetBinding(TextBox.TextProperty, new Binding("/Length") { Source = infos, Mode= BindingMode.OneWay });
显示效果如下:
如果集合中仍然是集合,我们想把子集集合中的元素做Path,我们可以使用多级斜线的语法(即“一路”斜线下去),例如:
- /// <summary>
- /// Window4.xaml 的交互逻辑
- /// </summary>
- public partial class Window4 : Window
- {
- public Window4()
- {
- InitializeComponent();
- List<Contry>infos = new List<Contry>() { new Contry() { Name = "中国", Provinces= new List<Province>(){ new Province(){ Name="四川",Citys=new List<City>(){new City(){Name="绵阳市"
- }}}}}};
- this.textBox1.SetBinding(TextBox.TextProperty, new Binding("/Name") { Source=infos});
- this.textBox2.SetBinding(TextBox.TextProperty, new Binding("/Provinces/Name") { Source = infos });
- this.textBox3.SetBinding(TextBox.TextProperty, new Binding("/Provinces/Citys/Name") { Source = infos });
- }
- }
- class City
- {
- public string Name { set; get; }
- }
- class Province
- {
- public string Name { set; get; }
- public List<City> Citys { set; get; }
- }
- class Contry
- {
- public string Name { set; get; }
- public List<Province> Provinces { get; set; }
- }
/// <summary> /// Window4.xaml 的交互逻辑 /// </summary> public partial class Window4 : Window { public Window4() { InitializeComponent(); List<Contry> infos = new List<Contry>() { new Contry() { Name = "中国", Provinces= new List<Province>(){ new Province(){ Name="四川",Citys=new List<City>(){new City(){Name="绵阳市" }}}}}}; this.textBox1.SetBinding(TextBox.TextProperty, new Binding("/Name") { Source=infos}); this.textBox2.SetBinding(TextBox.TextProperty, new Binding("/Provinces/Name") { Source = infos }); this.textBox3.SetBinding(TextBox.TextProperty, new Binding("/Provinces/Citys/Name") { Source = infos }); } } class City { public string Name { set; get; } } class Province { public string Name { set; get; } public List<City> Citys { set; get; } } class Contry { public string Name { set; get; } public List<Province> Provinces { get; set; } }
运行效果如图:
1.3.4 "没有Path"的Binding
有的时候我们会在代码中我们看大Path是一个“.”或者干脆没有Path的Binding,着实让人摸不着头脑。原来这是一种比较特殊的情况---Binding源本身就是一种数据且不需要Path来指明。典型的string,int等基本类型都是这样,他们是实例本身就是数据,我们无法指定通过那个属性来访问这个数据,这是我们只需要将这个数据设置为.就可以了。在XAML中这个.可以忽略不写,但是在C#中编程必须要带上。下面请看下面这段代码:
- <Windowx:Class="WpfApplication1.Window5"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:String="clr-namespace:System;assembly=mscorlib"
- Title="Window5"Height="331"Width="538">
- <StackPanelHeight="184"Name="stackPanel1"Width="288">
- <StackPanel.Resources>
- <String:Stringx:Key="myString">
- 菩提本无树,何处染尘埃。
- </String:String>
- </StackPanel.Resources>
- <TextBlockHeight="23"Name="textBlock1"Text="{Binding Path=.,Source={StaticResource ResourceKey=myString}}"/>
- </StackPanel>
- </Window>
<Window x:Class="WpfApplication1.Window5" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:String="clr-namespace:System;assembly=mscorlib" Title="Window5" Height="331" Width="538"> <StackPanel Height="184" Name="stackPanel1" Width="288"> <StackPanel.Resources> <String:String x:Key="myString"> 菩提本无树,何处染尘埃。 </String:String> </StackPanel.Resources> <TextBlock Height="23" Name="textBlock1" Text="{Binding Path=.,Source={StaticResource ResourceKey=myString}}" /> </StackPanel> </Window>
上面的代码可以简写成:
- <TextBlockHeight="23"Name="textBlock1"Text="{Binding .,Source={StaticResource ResourceKey=myString}}"/>
<TextBlock Height="23" Name="textBlock1" Text="{Binding .,Source={StaticResource ResourceKey=myString}}" />
或者直接写成:
- <TextBlockHeight="23"Name="textBlock1"Text="{Binding Source={StaticResource ResourceKey=myString}}"/>
<TextBlock Height="23" Name="textBlock1" Text="{Binding Source={StaticResource ResourceKey=myString}}" />
注意:
最后这种简写很容易被误解为没有指定Path,其实只是省略掉了。与只等效的C#代码如下:
- string myString = "菩提本无树,明镜亦无台。本来无一物,何处染尘埃。";
- this.textBlock1.SetBinding(TextBlock.TextProperty, new Binding(".") { Source=myString});
string myString = "菩提本无树,明镜亦无台。本来无一物,何处染尘埃。"; this.textBlock1.SetBinding(TextBlock.TextProperty, new Binding(".") { Source=myString});
注意:
最后顺便带一句,PropertyPath除了用于Binding的Path属性之外,在动画编程的时候也会派上用场(Storyboard.TargetProperty)。在用于动画编程的时候,PropertyPath还有另外一种语法,到时候我们细说。
1.3.5 把Binding指定为源(Source)的几种方法
上一节我们学习了如何通过Binding的path属性如何在一个对象上寻找数据。这一节我们将学习如何为Binding指定源(So 以上是关于WPF之Binding深入探讨的主要内容,如果未能解决你的问题,请参考以下文章