• 全方位Bindind分析


    Binding,音译为绑定,通道捆她想一条数据的高速绑着“源”与“目标”;

    “源”乃提供数据的一方;“目标”乃接收数据并作出相应反应的一方;

    过程感觉就像是,给一个“激励”,就会作出“反应”那样~~~

    首先引入一个小例子,希望移动游标时,文本框中显示游标相应的刻度:

    clip_image002

    那么,我们可以把游标看作是“源”,文本显示框看作是“目标”!

    <TextBox x:Name="txt"/>

    <Slider x:Name="slider"/>

    这样“源”和“目标”就都有了!

    把“源”和“目标”绑定起来的过程,我放到后台去实现,感觉这样理解binding会更清晰!

    //实例化一个Binding

    Binding binding = new Binding();

    //binding指定源,以及需要绑定的源的属性

    binding.Source = slider;//源

    binding.Path = new PropertyPath("Value");//源的属性

    //指定目标,目标的属性,并关联Binding

    txt.SetBinding(TextBox.TextProperty, binding);//或者写成:BindingOperations.SetBinding(txt, TextBox.TextProperty, binding);

    效果如下:

    clip_image004

    总结:

    1、Binding的几个步骤:

    1、新建Binding;

    2、指定Binding的源,以及源的属性;

    3、指定Binding的目标,以及目标的属性;

    2、txt.SetBinding(TextBox.TextProperty, binding);等价写法:

    BindingOperations.SetBinding(txt, TextBox.TextProperty, binding);

    3、后台三合一写法:

    txt.SetBinding(TextBox.TextProperty, new Binding("Value") { Source = slider});

    4、等价后台的前台写法:

    <TextBox x:Name="txt" Text="{Binding Path=Value, ElementName=slider}"/>

    <Slider x:Name="slider"/>

    对比后台写法,我们发现当指定源时,用的是ElementName属性,而不是Source;事实上前台指定Source=slider

    这样是不成功的。

    后台txt.SetBinding(TextBox.TextProperty, new Binding("Value") { ElementName = “slider”});

    没有问题。

    摘至:《WPF深入浅出》

    clip_image006

    5、前台XAML往往为我们设置好了转换器,而后台C#是没有为我们准备转换的,需要自己去转换。

    1、如前台中直接Path=Value,而后台中是:binding.Path = new PropertyPath("Value");

    2、如前台中直接Text=,而后台中是:TextBox.TextProperty

    //----------------------------------前面的只是铺垫,接下来才是主菜-------------------------

    如果说,我自己定义一个类,在类里自定义一个属性,让后让某个WPF某个元素的属性,与自定义的属性绑定在一起这个怎么实现呢?

    我们知道WPF里的每个元素都包含一个DataContext元素,当Bingding只知道自己的Path,而不知道自己的Souce时;换句话说,当

    Bingding只知道绑定源的属性,却不知道是哪个源的时候;他就会自己去看父级元素的DataContext属性中,是否有它绑定的Path。

    如果没有就看爷爷的DataContext中有没有,一直向上找。接下来我就看一个例子:

    首先新建一个自己的类People,我们希望自定义的属性被绑定。

     1 class People
     2     {
     3         private string name = "我是谁?";
     4 
     5         public string Name
     6         {
     7             get { return name; }
     8             set { name = value; }
     9         }
    10 
    11         private string sex = "人妖";
    12 
    13         public string Sex
    14         {
    15             get { return sex; }
    16             set { sex = value; }
    17         }
    18 
    19         public  void Call()
    20         {
    21             name = "Hi,我是宋桓公";
    22             sex = "";
    23             MessageBox.Show(name+"--"+sex);
    24         }
    25     }

    接着是XAML:

     1 <Window x:Class="test1.MainWindow"
     2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     4         xmlns:local="clr-namespace:test1"
     5         Title="MainWindow" Height="350" Width="525">
     6     <StackPanel x:Name="st">
     7         <StackPanel.DataContext>
     8             <local:People/>
     9         </StackPanel.DataContext>
    10         <TextBox x:Name="txtName" Text="{Binding Path=Name}"/>
    11         <TextBox x:Name="txtSex" Text="{Binding Path=Sex}"/>
    12         <Button x:Name="btn" Click="btn_Click_1"/>
    13     </StackPanel>
    14 </Window>

    这段,就是在StackPanel的DataContext属性中包含一个People实例。注意这里的语法,local是我们为映射

    的命名空间,自己取的名字,而local后面只能加一个类名,而不是实例名。<local:People/>而这句话的意思是

    创建一个People实例~!

    3、<TextBox x:Name="txtName" Text="{Binding Path=Name}"/>

    <TextBox x:Name="txtSex" Text="{Binding Path=Sex}"/>

    接着,我们分别为两个TextBox的Text属性绑定名为Name和Sex的属性,但是不指定Souce是谁。于是乎,他们

    知道Path,而不知道Souce,就会去看自己的“父亲”——StackPanel的DataContext中有啥?发现了People,

    而People正好具备了Name和Sex的属性,那么Text会显示什么呢?

    clip_image008

    看来绑定成功了,我们属性的初始化值正是这两个!但是仅仅是这样,并不能满足我,我想动态的去改变这个绑定的值

    才是我最终的目的!于是在后台,我这么写:

    private void btn_Click_1(object sender, RoutedEventArgs e)

    {

          People people = new People();

          people.Call();

    }

    我希望通过调用Call方法,来改变people的属性Name和Sex的值。但是点击按钮后,值却没有发生改变!

    原因很简单,这是因为<local:People/>创建的实例,和btn_Click_1中创建的PeoPle实例,完全是两个实例!自然

    不会变。于是,我想如果这这样<local:People x:Name ="pp" />为local创建的实例取个名字,让后去按键中修改属性值,看

    Text的值是否发生变化。

    private void btn_Click_1(object sender, RoutedEventArgs e)

    {

        pp.Call();

    }

    但,遗憾的是,这样仍然没有效果~

    不要放弃,我们继续改,之后我们再一次绑定,同样不指明Souce:

    private void btn_Click_1(object sender, RoutedEventArgs e)

    {

         pp.Call();

         txtName.SetBinding(TextBox.TextProperty, new Binding("Name") { });

         txtSex.SetBinding(TextBox.TextProperty, new Binding("Sex") { });

    }

    clip_image010

    后台再一次绑定之后发现,成功了!但是还是不能让人满意,因为每次改变属性,

    都要从新绑定一次!

    //-----------这里还有一个有趣的事情想插播一下-----------------

    如果,我指名Souce为st,会得到什么结果呢?

    txtName.SetBinding(TextBox.TextProperty, new Binding("Name") { Source = st});

    txtSex.SetBinding(TextBox.TextProperty, new Binding("Sex") { Source = st});

    结果是:

    clip_image012

    姓名显示,st,性别没显示~~

    如果,我指名Souce为st.DataContext,会得到什么结果呢?

    clip_image014

    结果又正常了~~~

    这个是为什么呢?我分析原因是这个样子的:

    当为Binding指定Path而不指定Souce时,他会去看父级元素的DataContext属性中,是否有它绑定的Path。

    反之,如果指定Path且指定Souce时,他就只会看父亲元素,而不会去看父级元素的DataContext属性中,是否有它绑定的Path。所以,姓名显示st(因为StackPanel确实有个Name属性叫st,呵呵),性别没显示~~

    //------------------------------------------------------------

    我们上面也看到了,如果是wpf提供的元素之间的绑定,是很简单的,而自定义的属性,虽然借助了DataContext属性这个特性,

    但是结果仍然不尽人意,那么如何把自定义元素打造成和wpf提供的元素一样呢?我们还得从长计议~~

    好!我们就来对People类,进行“大”改造!

     1 class People : INotifyPropertyChanged 
     2     {
     3         public event PropertyChangedEventHandler PropertyChanged;//这个事件名字不能改的,因为是在INotifyPropertyChanged里定义好的!
     4         
     5         private string name = "我是谁?";
     6 
     7         public string Name
     8         {
     9             get { return name; }
    10             set
    11             {
    12                 name = value;
    13                 //激发事件
    14                 if (PropertyChanged != null)
    15                 {
    16                     PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Name"));//ProgressChangedEventArgs之前居然写成这个。罪过啊!
    17                 }
    18             }
    19         }
    20 
    21         private string sex = "人妖";
    22 
    23         public string Sex
    24         {
    25             get { return sex; }
    26             set
    27             {
    28                 sex = value;
    29                 //激发事件
    30                 if (PropertyChanged != null)
    31                 {
    32                     PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Sex"));//ProgressChangedEventArgs之前居然写成这个。罪过啊!
    33                 }
    34             }
    35         }
    36 
    37         public  void Call()
    38         {
    39             name = "Hi,我是宋桓公";
    40             sex = "";
    41             MessageBox.Show(name+"--"+sex);
    42         }
    43     }

    成功的关键,有下面几点:
    1、需添加程序集:using System.ComponentModel;
    2、:INotifyPropertyChanged ,让自定义类继承这个接口;
    3、 public event PropertyChangedEventHandler PropertyChanged;//这个事件名字不能改的,因为是在INotifyPropertyChanged里定义好的!
    4、set中激发这个事件,PropertyChangedEventArgs不要写成ProgressChangedEventArgs,衰。。都是自动提示
    惹的祸~~~
    5、先看看效果,没效果,写这么多点都是扯淡对吧~~
    好,就改造这个类这么多,其他都不改,运行下——结果真的没效果(啊!!!!!),找下原因:
    name = "Hi,我是宋桓公";
    sex = "男";
    Call方法里,改变的是字段,而不是属性;而激发事件PropertyChanged的条件是属性发生改变!
    所以Call改成:
    public void Call()
    {
       Name = "Hi,我是宋桓公";
       Sex = "男";
    }

    这样就OK了~~,我们的自定义属性,就可以像WPF提供的元素那样“自由”绑定了~~
    所以,真正的第5点是:注意改变属性的值,才能触发PropertyChanged事件!

  • 相关阅读:
    【SR汇总】基于传统方法
    漫展被骗
    SRCNN代码分析
    【SR汇总】效果对比
    【SR汇总】算法时间效率
    根据wsdl文件,Java工程自动生成webservice客户端调用
    Python3 Selenium WebDriver网页的前进、后退、刷新、最大化、获取窗口位置、设置窗口大小、获取页面title、获取网页源码、获取Url等基本操作
    Python3 Selenium自动化-select下拉框
    Python3 ChromeDriver与Chrome版本映射表(更新至v2.43)
    Python3 Selenium自动化测试赋值出现:WebDriverException: Message: unknown error: call function result missing 'value'
  • 原文地址:https://www.cnblogs.com/douzi2/p/4287411.html
Copyright © 2020-2023  润新知