• WPF Binding


    winform有binding, WPF也有binding,区别在哪呢?这里暂时不提。以前也检查接触WPF binding, 但为什么过段时间就忘记了呢?

    可能主要原因自己的知识体系不够完善吧,下面我先总结下binding的用法,然后再深入挖掘binding的原理,最后再总结,这样希望自己能够对binding达到非常深刻的理解。

    binding的用法:

      1)绑定控件属性。下面是将一个Slider的值绑定到一个Textbox上。

         <TextBox Grid.Row="1" Text="{Binding Path=Value,ElementName=slider1}"></TextBox>
                <Slider Grid.Row="2" x:Name="slider1" Maximum="100" Minimum="0"></Slider>

          2)绑定多级路径,比如属性的属性,索引器等。

          <TextBox Grid.Row="2" x:Name="textbox2" Text="{Binding  Path=Text.Length,ElementName=textbox1,Mode=OneWay}"
                         BorderBrush="Black" Margin="5"></TextBox>
                <TextBox Grid.Row="3" x:Name="textbox3" Text="{Binding Path=Text[4],ElementName=textbox1,Mode=OneWay}"
                         BorderBrush="Red" Margin="5"></TextBox>

         3)还可以判定集合,甚至是集合的集合元素。

          List<string> stringList = new List<string>(){"xiaowang","dabing","qizhi"};
                this.txtCollection1.SetBinding(TextBox.TextProperty, new Binding("/") { Source = stringList });
                this.txtCollection2.SetBinding(TextBox.TextProperty, new Binding("/Length") { Source = stringList,
                    Mode = BindingMode.OneWay });
                this.txtCollection3.SetBinding(TextBox.TextProperty, new Binding("/[2]") { Source = stringList, Mode = BindingMode.OneWay });

                  this.txtCollection4.SetBinding(TextBox.TextProperty, new Binding("/Name") { Source = countryList });
                this.txtCollection5.SetBinding(TextBox.TextProperty, new Binding("/ProvinceList/Name")
                {
                    Source = countryList,
                });
                this.txtCollection6.SetBinding(TextBox.TextProperty, new Binding("/ProvinceList/CityList/Name") { Source = countryList }); 

          4)还可以没有Path的Binding,这里的路径直接用一个.就可以了。

                <TextBox Grid.Row="1" Text="{Binding .,Source={StaticResource ResourceKey=myString}}"></TextBox>

          5)使用DataContext作为源:

                 <StackPanel Grid.Row="1">
                    <StackPanel.DataContext>
                        <local:Student Id="4" Age="18" Name="Jim"></local:Student>
                    </StackPanel.DataContext>
                    <TextBox Text="{Binding Path=Id}" Margin="5"/>
                    <TextBox Text="{Binding Path=Name}" Margin="5"/>
                    <TextBox Text="{Binding Path=Age}" Margin="5"/>
                </StackPanel>

           6)为列表控件指定ItemsSource

                List<Student> stuList = new List<Student>()
                {
                    new Student(){ Age = 18, Id = 1, Name = "Xiao"},
                    new Student(){ Age = 19, Id = 4, Name = "smao"},
                    new Student(){ Age = 20, Id = 3, Name = "duo"},
                    new Student(){ Age = 21, Id = 2, Name = "shao"},
                };
                this.listBoxStudents.ItemsSource = stuList;
                this.listBoxStudents.DisplayMemberPath = "Name";
                Binding binding = new Binding("SelectedItem.Id") { Source = this.listBoxStudents };
                this.txtId.SetBinding(TextBox.TextProperty, binding);
                this.listBoxStudents.SelectedIndex = 0;

         7)为列表控件指定外衣来绑定集合,绑定的代码是一样的。

               <ListBox x:Name="listBoxStudents2" Grid.Row="4">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding Path=Id}" Width="30"/>
                                <TextBlock Text="{Binding Path=Name}" Width="60"/>
                                <TextBlock Text="{Binding Path=Age}" Width="30"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>

         8)DataTable作为源。

          <StackPanel Grid.Row="1">
                    <ListView x:Name="listViewStudents">
                        <ListView.View>
                            <GridView>
                                <GridViewColumn Header="Id" Width="60" DisplayMemberBinding="{Binding Path=Id}"/>
                                <GridViewColumn Header="Name" Width="80" DisplayMemberBinding="{Binding Path=Name}"/>
                                <GridViewColumn Header="Age" Width="60" DisplayMemberBinding="{Binding Path=Age}"/>
                            </GridView>
                        </ListView.View>
                    </ListView>
                </StackPanel>

                 this.listViewStudents.ItemsSource = dt.DefaultView;

          9)把类的方法包装成数据作为源ObjectDataProvider

            

                  ObjectDataProvider odp = new ObjectDataProvider();            

                   odp.ObjectInstance = new Calculator();//方法类       

                   odp.MethodName = "Add"; //方法名           

                  odp.MethodParameters.Add("0"); //添加方法参数        

                   odp.MethodParameters.Add("1");

                   Binding bindingToArg1 =  new Binding("MethodParameters[0]")            

                        { Source = odp,    BindsDirectlyToSource = true,               

                              UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };

                   Binding bindingToArg2 = new Binding("MethodParameters[1]")            

                        { Source = odp,    BindsDirectlyToSource = true,                

                             UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };   

                     Binding bindingToResult = new Binding(".") { Source = odp };           

                     txtArg1.SetBinding(TextBox.TextProperty, bindingToArg1);//把控件当做目标,源是最终的方法类          

                     txtArg2.SetBinding(TextBox.TextProperty, bindingToArg2);           

                    txtResult.SetBinding(TextBox.TextProperty, bindingToResult);//控件依然是目标,如果是.就是provider的数据

          10)自身控件或者上级控件的属性作为源的。

               <Grid Grid.Row="2" Grid.Column="2" Background="Black" Margin="15">
                <DockPanel x:Name="Dock" Background="Blue" Margin="20">
                    <Grid Background="Orange" Margin="20">
                        <StackPanel>
                            <TextBox Text="{Binding Path=Name, RelativeSource={RelativeSource AncestorLevel=1,AncestorType= {x:Type DockPanel}}}" Margin="0"></TextBox>
                            <TextBox x:Name="txt1" Text="{Binding Path=Name, RelativeSource={RelativeSource Mode=Self}}" Margin="0"></TextBox>
                        </StackPanel>
                    </Grid>
                </DockPanel>
            </Grid>

          11)使用XML数据作为Binding源。

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

      13)binding的数据校验:

             private void BindingValidation()
            {
                Binding binding = new Binding("Value") { Source = sliderValidation, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
                RangeValidationRule rvr = new RangeValidationRule();
                rvr.ValidatesOnTargetUpdated = true;
                binding.NotifyOnValidationError = true;
                binding.ValidationRules.Add(rvr);
                this.txtValidation.SetBinding(TextBox.TextProperty, binding);
                this.txtValidation.AddHandler(Validation.ErrorEvent, new RoutedEventHandler(this.ValidationError));
            }
            private void ValidationError(object sender, RoutedEventArgs e)
            {
                if (Validation.GetErrors(this.txtValidation).Count > 0)
                {
                    this.txtValidation.ToolTip = Validation.GetErrors(this.txtValidation)[0].ErrorContent.ToString();
                }
            }

          14)binding的转换:

         <ListBox x:Name="listBoxConvert" Grid.ColumnSpan="2">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <Image Width="20" Height="20" Source="{Binding Path=Category,Converter={StaticResource cts}}"></Image>
                                <TextBlock Text="{Binding Path=Name}" Width="60" Margin="80,0"></TextBlock>
                                <CheckBox IsThreeState="True" IsChecked="{Binding Path=State,Converter={StaticResource stnb}}"></CheckBox>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>

              public class CategoryToSourceConverter : IValueConverter    

               {        

                     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)      

                     {     

                              Category c = (Category)value;      

                              switch (c)        

                               {                

                                      case Category.Bomber:       

                                             return @"ImagesBomber.jpg";              

                                     case Category.Fighter:                   

                                             return @"ImagesFighter.jpg";              

                                      default:                     return null;         

                               }             //throw new NotImplementedException();         }

                     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)    

                      {             throw new NotImplementedException();         }  

                }

      public class StateToNullableBoolConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                State s = (State)value;
                switch (s)
                {
                    case State.Available:
                        return true;
                    case State.Locked:
                        return false;
                    default:
                        return null;
                }
            }
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                bool? nb = (bool?)value;
                switch (nb)
                {
                    case true:
                        return State.Available;
                    case false:
                        return State.Locked;
                    case null:
                    default:
                        return State.Unknown;
                }
            }
        }

       15)多路binding:

         public void SetMultiBinding()
            {
                Binding b1 = new Binding("Text") { Source = this.txtMult1 };
                Binding b2 = new Binding("Text") { Source = this.txtMult2 };
                Binding b3 = new Binding("Text") { Source = this.txtMult3 };
                Binding b4 = new Binding("Text") { Source = this.txtMult4 };
                MultiBinding mb = new MultiBinding() { Mode = BindingMode.OneWay };
                mb.Bindings.Add(b1);
                mb.Bindings.Add(b2);
                mb.Bindings.Add(b3);
                mb.Bindings.Add(b4);
                mb.Converter = new LogonMultiBindingConverter();
                this.btnSubmit.SetBinding(Button.IsEnabledProperty, mb);
            }

            public class LogonMultiBindingConverter : IMultiValueConverter  

            {       

                public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)     

               {          

                        if (!values.Cast<string>().Any(text => string.IsNullOrEmpty(text)) &&     

                            values[0].ToString() == values[1].ToString() && values[2].ToString() == values[3].ToString())                   {

                             return true;          

                      }

                      return false;     

                 }

              public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)         {             throw new NotImplementedException();         }     }

      16. Binding的BindingMode:

         <Slider x:Name="sliderMode" Grid.Row="1" Grid.ColumnSpan="2" Value="48" Minimum="0" Maximum="100"></Slider>
                <TextBlock Grid.Row="2" Text="OneWay:"></TextBlock>
                <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=Value, Mode=OneWay, ElementName=sliderMode}"></TextBox>
                <TextBlock Grid.Row="3" Text="TwoWay:"></TextBlock>
                <TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Path=Value, Mode=TwoWay, ElementName=sliderMode}"></TextBox>
                <TextBlock Grid.Row="4" Text="OneTime:"></TextBlock>
                <TextBox Grid.Row="4" Grid.Column="1" Text="{Binding Path=Value, Mode=OneTime, ElementName=sliderMode}"></TextBox>
                <TextBlock Grid.Row="5" Grid.Column="0" Text="OneWayToSource:"></TextBlock>
                <TextBox Grid.Row="5" Grid.Column="1" Text="{Binding Path=Value, Mode=OneWayToSource, ElementName=sliderMode}"></TextBox>
                <TextBlock Grid.Row="6" Text="Default:"></TextBlock>
                <TextBox Grid.Row="6" Grid.Column="1" Text="{Binding Path=Value, Mode=Default, ElementName=sliderMode}"></TextBox>

    在讲完binding的使用后,我们接下来深入剖析下binding:

    首先看一看binding的一些重要成员:

      binding的源:

        1)RelativeSource。相对资源。 

        2)ElementName:元素名,在指定源的时候,如果是控件,可以指定这个。 

           3)Source:指定binding的源。

              4)StaticSource:静态资源. 

      Path: PropertyPath类型,指定源的路径。

      bindingMode:指定绑定的模式。

        1)TwoWay.目标和源都可以影响对方。

        2) OneWay.只有源影响目标。

        3)OneTime.只有程序启动时或者DataContext进行更改时更新目标属性。

        4)OneWayToSource.在目标属性更改时更新源属性。

        5)Default.使用目标属性的默认Mode值。比如TextBox.Text就是双向的,TextBlock.Text就是单向的。

      UpdateSourceTrigger:指定源更新的时期

        1)PropertyChanged. 属性变化的时候更新

        2)LostFocus. 失焦的时候更新。

        3)Explicit.

        4)Default. 缺省。

      NotifyOnSourceUpdated NotifyOnTargetUpdated 这两个Bool属性。

        如果为true表示当源或者目标更新后binding会激发相应的SourceUpdated和TargetUpdated事件。

        实际工作中,我们可以监听这两个事件来找出有哪些数据或者控件被更新了。

        ValidationRules.

         ValidationRule的对象集合,这里是指定绑定的验证规则,在进行验证的时候,默认都是认为来自source的数据总是正确的,不需要验证,如果需要验证就要

      设置ValidatesOnTargetUpdated(因为source是会引起Target更新),如果验证的时候的错误需要发出去,需要设置NotifyOnValidationError属性为true,那么

      这个信号就会以binding对象的target为起点在UI树上传播。

         Converter.

        数据转换功能,当源与目标的数据类型不一样的时候,就需要使用。需要实现IValueConverter接口,

        其中:

          Convert是转换源到目标。

          ConvertBack:转换目标到源。

          到这里为止,我可能还只是对binding的使用有了一个大致的了解,binding与依赖属性,依赖对象之间的关系如何,为什么binding了以后,数据驱动的原理是什么?这些都是一个疑问,我这里只是大致了解了一些类:

           Binding是BindingBase的子类。BindingExpression是BindingExpressionBase的子类,BindingOperations是一个静态类。

          我这样大致猜想一下:当我们Binding的时候,把源和目标绑定在一起,目标必须是依赖对象,源可以是依赖对象也可以是一般的对象,但必须公开了属性。Binding完成后,如果源是依赖属性或者一般类实现了INotifyPropertyChanged接口,那么肯定就会在弱引用管理器中添加监听者与监听对象,当属性有改变的时候,就会触发相应的事件,使得数据同步得到更新,如果一般的类没有实现这个接口,自然是得不到更新的。

      知道了这些原理,自然就知道了在不同的设置下面源于目标如何发生变化的情况:

      假设源是一个A类对象,其属性P1触发了事件PropertyChanged,P2没有触发事件PropertyChanged,假设分别绑定到了控件C1,C2。

    我们来分析一下不同模式下的情况:

      OneWay: P1改变会影响C1,P2改变不会影响C2,C1改变不会影响P1,C2改变不会影响P2。

      TwoWay: P1与C1相互影响,P2与C2互不影响。

      OneTime: P1只会影响C1一次,P2与C2互不影响,C1改变不会影响P1。

      OneWayToSource: P1改变不会影响C1,P2改变不会影响C2,C1改变影响P1,C2改变不会影响P2。

      另外假设源是一个依赖对象,其依赖属性作为路径,那么其在不同模式下的情况就跟上面的假设中P1与C1的情况是一样的。

    代码如下

         http://files.cnblogs.com/files/monkeyZhong/WPFBindingDemo.zip

      

         

         

  • 相关阅读:
    《七哥说道》第五章:入职惨做苦力,画饼一望无际
    《七哥说道》第四章:理想在远方,现实在流浪
    《七哥说道》第三章:志远淋雨怒辞职,误入保险黄老萍
    《七哥说道》第二章:初出茅庐之拜师学艺
    (十)redis源码解读
    (三十二)线上调优
    (三)栈
    (一)设计模式之代理模式
    Linux whereis、find和locate命令区别以及应用场景
    使用自定义注解和AOP管理shiro权限
  • 原文地址:https://www.cnblogs.com/monkeyZhong/p/4588621.html
Copyright © 2020-2023  润新知