Binding(一)——Binding基础

来源:互联网 发布:在淘宝开网店赚钱吗 编辑:程序博客网 时间:2024/05/16 15:23

一、Binding概述

Binding注重表达它是一种像桥梁一样的关联关系。WPF中,正是在这段桥梁上我们有机会为往来流通的数据做很多事情。

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

让我们来看一个最简单的例子。创建一个简单的数据源并通过Binding把它连接到UI元素上。

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

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


前面说过,数据源是一个对象,一个对象身上可能有很多数据,这些数据通过属性暴露给外界。那么,其中的哪些数据是想通过Binding送达到UI层呢?UI上的元素关心的是哪些属性值的变化呢?这个属性就称为Binding的路径(Path)。但光有属性还不够——Binding是一种自动机制,当数值变化后要有能力通知Binding。方法是在属性的set语句中激发一个PropertyChanged事件。这个事件不需要自己声明,只需要让作为数据源的类实现INotifyPropertyChanged接口。

当为Binding设置了数据源后,Binding就会自动侦听来自这个接口的PropertyChanged事件。修改Student类。

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

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

前端代码如下。

<Window x:Class="WpfApplication1.MainWindow"        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        Title="Simple Binding" Height="110" Width="300">    <StackPanel>        <TextBox x:Name="textBoxName" BorderBrush="Black" Margin="5"/>        <Button Content="Add Age" Margin="5" Click="Button_Click"/>    </StackPanel></Window>

接下来,最重要的一步,使用Binding把数据源和UI元素连接起来。

    public partial class MainWindow : Window    {        Student stu;        public MainWindow()        {            InitializeComponent();            Student stu = new Student();            Binding binding = new Binding();            binding.Source = stu;            binding.Path = new PropertyPath("Name");            BindingOperations.SetBinding(this.textBoxName, TextBox.PaddingProperty, binding);        }        private void Button_Click(object sender, RoutedEventArgs e)        {            stu.Name += "Name";        }    }

在准备Binding的部分,先是用Binding binding = new Binding(); 声明Binding类型变量并创建实例;然后使用bingding.Source = stu;为Binding实例指定数据源;最后使用binding.Path =  new PropertyPath("Name");为Binding指定访问路径。

把数据源和目标连接在一起的任务是使用BindingOperations.SetBinding(...);方法来完成的。

  • 第一个参数用于指定Binding的目标。
  • 第二个参数用于为Binding指明把数据送达目标的哪个属性。这里使用的不是对象的属性而是类的一个静态只读的依赖属性。
  • 第三个参数指定使用哪个Binding实例讲数据源与目标关联。

实际工作中,代码可能不一样。原因是TextBox这类UI元素的基类FrameworkElement对BindingOperations.SetBinding(...)方法进行了封装。

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

可以借助类的构造器和对象化初始化器来简化代码。

    public partial class MainWindow : Window    {        Student stu;        public MainWindow()        {            InitializeComponent();            this.textBoxName.SetBinding(TextBox.TextProperty, new Binding("Name") { Source = stu = new Student() });        }        private void Button_Click(object sender, RoutedEventArgs e)        {            stu.Name += "Name";        }    }

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

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

<Window x:Class="WpfApplication1.MainWindow"        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 x:Name="textBox1" Text="{Binding Path=Value, ElementName=slider1}" BorderBrush="Black" Margin="5"/>        <Slider x:Name="slider1" Maximum="100" Minimum="0" Margin="5"/>    </StackPanel></Window>

如大家所见,除了可以在C#中利用PropertyChanged建立Binding外。在XAML代码里也可以方便地设置Binding。值得注意的是,在C#代码中可以访问XAML代码中声明的变量但XAML代码中却无法访问C#代码中声明的变量。

来看一下这句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"/>

三、控制Binding的方向及数据更新

默认情况下数据既能够通过Binding送达目标,也能够从目标返回源。有时候数据只需要展示给用户、不允许用户修改,这时可以把Binding模式更改为从源向目标的单向流通。除此以外Binding还支持其他很多的流通模式,这需要根据实际情况去选择。

控制Binding数据流向的属性是Mode,它的类型是BindingMode枚举。BindingMode可取值是TwoWay、OneWay、OnTime、OneWayToSource和Default。这里的Default值并不固定,它会根据指定目标的实际情况来确定。比如若是可编辑的TextBox.Text属性,,Default就采用双向模式;若是只读的TextBlock.Text属性,则采用单向模式。

在上个例子中,拖动Slider手柄时,TextBox里就会显示Slider当前的Value;如果在TextBox里输入一个恰当的值,然后按下Tab让焦点离开TextBox,则Slider的手柄会跳到相应的位置。

为什么一定要在TextBox失去焦点之后Slider的值才会改变?这就引出了Binding的另一个属性UpdateSourceTrigger,它的类型是UpdateSourceTrigger枚举,可取PropertyChanged、LostFocus、Explicit和Default。同样地,对于TextBox默认值的行为与LostFocus一致。我们只需把这个属性改为PropertyChanged,则Slider的手柄就会随着我们在TextBox里的输入改变而改变位置。

原创粉丝点击