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

  

     

     

原文地址:https://www.cnblogs.com/monkeyZhong/p/4588621.html