一、何为数据绑定
场景:考虑一个Window上有一个TextBox和一个Slider两个元素,当我们拖动滑动条的时候,会在TextBox中显示当前滑动条的数值;当我们在TextBox中输入一个有效值,滑动条
中的滑块会滑到TextBox中输入的值所对应的位置。
定义:数据绑定可以理解为两个对象之间的一种关联,对象中的某个属性总是保持同步于另个对象的某个属性值。我们可以形象的把绑定比作一个桥梁,它负责同步桥头两侧的物体。
保持同步的数据元素必须是属性,一个叫源属性,一个叫目标属性;
目标属性必须是一个依赖属性。
二、如何绑定元素对象
关于上面滑动条和文本框的绑定,我们可以用以下Xaml代码实现:
<Window x:Class="BindingDemo1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:BindingDemo1" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Grid> <StackPanel> <TextBox Name="tbx" Text="{Binding Path=Value, ElementName=sldrSlider}" ></TextBox> <Slider Name="sldrSlider" Minimum="0" Maximum="100"></Slider> </StackPanel> </Grid> </Window>
等价的后台代码实现如下:
using System.Windows; using System.Windows.Controls; using System.Windows.Data; namespace BindingDemo1 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // Binding binding = new Binding() { ElementName = "sldrSlider",Path= new PropertyPath("Value")}; Binding binding = new Binding("Value") { ElementName = "sldrSlider" }; tbx.SetBinding(TextBox.TextProperty, binding); } } }
后台代码主要完成了以下功能:
1)创建一个binding对象;
2)设置了binding对象的Source和Path值;
3)调用了目标(textbox)的SetBinding方法创建一个binding表达式,它连接了binding对象和目标依赖属性。
三、数据绑定的方向
我们可以通过设置Binding对象的Mode属性来控制数据绑定的方向。
OneWay:数据源改变时,更新目标;
TwoWay:双向更新;
OneWayToSource:目标改变时,更新数据源;
OneTime:只更新目标一次,使用数据源的初值,以后目标不再被更新;
Default:使用目标的默认绑定模式。
四、触发器---更新数据源的时机
仍然考虑上面滑动条和文本框的绑定关系,我们会发现当文本框的数值改变后,滑动条的滑块不会立即改变,而是在文本框失去焦点的时候,才会改变。
这就涉及到当目标属性变化的时候,数据源如何以及何时变化。
关于数据的更新方向和时机,可以参见下图:
当目标依赖属性发生变化,我们可以利用Binding对象的UpdateSourceTrigger属性来控制何时更新数据源,UpdateSourceTrigger属性值包括以下:
注:UpdateSourceTrigger属性值不影响目标的更新方式,它仅仅控制TwoWay模式或OneWayToSource模式的绑定更新源的方式。
而文本框正是使用LostFocus方式从目标向源进行更新的。
如果要完全控制源对象的更新时机,则可以选择UpdateSourceTrigger.Explicit模式。
此时就需要额外编写代码手动触发更新。可以添加一个Apply按钮,并在按钮的Click事件处理程序中调用BindingExpression.UpdateSource方法立即更新数据源。
参考代码以下:
1 <Window x:Class="BindingDemo1.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:BindingDemo1" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="350" Width="525"> 9 <Grid> 10 <StackPanel> 11 <TextBox Name="tbx" Text="{Binding Path=Value, ElementName=sldrSlider,UpdateSourceTrigger=Explicit}" ></TextBox> 12 <Slider Name="sldrSlider" Minimum="0" Maximum="100"></Slider> 13 <Button Height="30" Width="100" Content="Apply" Click="Button_Click"></Button> 14 </StackPanel> 15 </Grid> 16 </Window>
1 using System.Windows; 2 using System.Windows.Controls; 3 using System.Windows.Data; 4 5 namespace BindingDemo1 6 { 7 /// <summary> 8 /// Interaction logic for MainWindow.xaml 9 /// </summary> 10 public partial class MainWindow : Window 11 { 12 public MainWindow() 13 { 14 InitializeComponent(); 15 //Binding binding = new Binding() { ElementName = "sldrSlider",Path= new PropertyPath("Value")}; 16 //Binding binding = new Binding("Value") { ElementName = "sldrSlider" ,UpdateSourceTrigger=UpdateSourceTrigger.}; 17 //tbx.SetBinding(TextBox.TextProperty, binding); 18 } 19 20 private void Button_Click(object sender, RoutedEventArgs e) 21 { 22 //获得应用于文本框上的绑定 23 BindingExpression be = tbx.GetBindingExpression(TextBox.TextProperty); 24 //调用UpdateSource更新源对象 25 be.UpdateSource(); 26 } 27 } 28 }
五、转换器
上面的例子中,当拖动滑动条的时候,文本框中显示的数字会显示好多位小数,现在我们想让在文本框中只显示两位小数,这时候就需要转换器来实现了。
定义:转化器是一个类,用于拦截数据源和目标之间的数据,拦截之后,就可以按照我们的意愿对数据进行操作。
转换器包括单值转换器和多值转换器。单值转换器需要实现IValueConverter接口,多值转换器需要实现IMultiValueConverter接口。
对于上面的需求,我们可以使用单值转换器,参考代码如下:
1 using System; 2 using System.Globalization; 3 using System.Windows; 4 using System.Windows.Controls; 5 using System.Windows.Data; 6 7 namespace BindingDemo1 8 { 9 /// <summary> 10 /// Interaction logic for MainWindow.xaml 11 /// </summary> 12 public partial class MainWindow : Window 13 { 14 public MainWindow() 15 { 16 InitializeComponent(); 17 //Binding binding = new Binding() { ElementName = "sldrSlider",Path= new PropertyPath("Value")}; 18 //Binding binding = new Binding("Value") { ElementName = "sldrSlider" ,UpdateSourceTrigger=UpdateSourceTrigger.}; 19 //tbx.SetBinding(TextBox.TextProperty, binding); 20 } 21 22 private void Button_Click(object sender, RoutedEventArgs e) 23 { 24 //获得应用于文本框上的绑定 25 BindingExpression be = tbx.GetBindingExpression(TextBox.TextProperty); 26 //调用UpdateSource更新源对象 27 be.UpdateSource(); 28 } 29 } 30 public class SliderValue2StringConverter:IValueConverter 31 { 32 public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 33 { 34 double valueFromSource = (double)value; 35 return valueFromSource.ToString("F2");//保留两位小数 36 } 37 38 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 39 { 40 throw new NotImplementedException("Method ConvertBack not implemented"); 41 } 42 } 43 }
1 <Window x:Class="BindingDemo1.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:BindingDemo1" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="350" Width="525"> 9 <Window.Resources> 10 <local:SliderValue2StringConverter x:Key="s2vCvt"></local:SliderValue2StringConverter> 11 </Window.Resources> 12 <Grid> 13 <StackPanel> 14 <TextBox Name="tbx" Text="{Binding Path=Value, ElementName=sldrSlider,UpdateSourceTrigger=Explicit,Converter={StaticResource ResourceKey=s2vCvt}}" ></TextBox> 15 <Slider Name="sldrSlider" Minimum="0" Maximum="100"></Slider> 16 <Button Height="30" Width="100" Content="Apply" Click="Button_Click"></Button> 17 </StackPanel> 18 </Grid> 19 </Window>
现在考虑以下场景,我们有一个椭圆,椭圆的颜色是根据RGB三个不同的分量变化的,我们让三个滑动条分别控制RGB三个分量,下面就用多值转换器进行实现。
1 using System; 2 using System.Globalization; 3 using System.Windows; 4 using System.Windows.Controls; 5 using System.Windows.Data; 6 using System.Windows.Media; 7 8 namespace BindingDemo1 9 { 10 /// <summary> 11 /// Interaction logic for MainWindow.xaml 12 /// </summary> 13 public partial class MainWindow : Window 14 { 15 public MainWindow() 16 { 17 InitializeComponent(); 18 //Binding binding = new Binding() { ElementName = "sldrSlider",Path= new PropertyPath("Value")}; 19 //Binding binding = new Binding("Value") { ElementName = "sldrSlider" ,UpdateSourceTrigger=UpdateSourceTrigger.}; 20 //tbx.SetBinding(TextBox.TextProperty, binding); 21 } 22 23 private void Button_Click(object sender, RoutedEventArgs e) 24 { 25 //获得应用于文本框上的绑定 26 BindingExpression be = tbx.GetBindingExpression(TextBox.TextProperty); 27 //调用UpdateSource更新源对象 28 be.UpdateSource(); 29 } 30 } 31 public class SliderValue2StringConverter:IValueConverter 32 { 33 public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 34 { 35 double valueFromSource = (double)value; 36 return valueFromSource.ToString("F2");//保留两位小数 37 } 38 39 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 40 { 41 throw new NotImplementedException("Method ConvertBack not implemented"); 42 } 43 } 44 public class Rgb2ColorConverter : IMultiValueConverter 45 { 46 //正向修改,整合颜色值 47 public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 48 { 49 if (values == null || values.Length < 3) 50 return null; 51 byte r = System.Convert.ToByte(values[0]); 52 byte g = System.Convert.ToByte(values[1]); 53 byte b = System.Convert.ToByte(values[2]); 54 Color myColor = Color.FromRgb(r, g, b); 55 SolidColorBrush myBrush = new SolidColorBrush(myColor); 56 return myBrush; 57 } 58 public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 59 { 60 throw new NotImplementedException("Method ConvertBack not implemented"); 61 } 62 63 } 64 }
1 <Window x:Class="BindingDemo1.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:BindingDemo1" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="350" Width="525"> 9 <Window.Resources> 10 <local:SliderValue2StringConverter x:Key="s2vCvt"></local:SliderValue2StringConverter> 11 </Window.Resources> 12 <Grid> 13 <Grid.ColumnDefinitions> 14 <ColumnDefinition Width="191*"/> 15 <ColumnDefinition Width="326*"/> 16 </Grid.ColumnDefinitions> 17 <Grid.RowDefinitions> 18 <RowDefinition Height="111*"/> 19 <RowDefinition Height="209*"/> 20 </Grid.RowDefinitions> 21 <StackPanel> 22 <TextBox Name="tbx" Text="{Binding Path=Value, ElementName=sldrSlider,UpdateSourceTrigger=Explicit,Converter={StaticResource ResourceKey=s2vCvt}}" ></TextBox> 23 <Slider Name="sldrSlider" Minimum="0" Maximum="100"></Slider> 24 <Button Height="30" Width="100" Content="Apply" Click="Button_Click"></Button> 25 26 </StackPanel> 27 <StackPanel Grid.Column="1"> 28 <StackPanel Orientation="Horizontal"> 29 <Label Content="R:"></Label> 30 <Slider Name="sldR" Foreground="Red" Width="300" Minimum="0" Maximum="255" Ticks="1"></Slider> 31 </StackPanel> 32 <StackPanel Orientation="Horizontal"> 33 <Label Content="G:"></Label> 34 <Slider Name="sldG" Foreground="Red" Width="300" Minimum="0" Maximum="255" Ticks="1"></Slider> 35 </StackPanel> 36 <StackPanel Orientation="Horizontal"> 37 <Label Content="B:"></Label> 38 <Slider x:Name="sldB" Foreground="Red" Width="300" Minimum="0" Maximum="255" Ticks="1"/> 39 </StackPanel> 40 <StackPanel Orientation="Horizontal"> 41 <Label Content="O:"></Label> 42 <Slider Name="sldO" Foreground="Red" Width="300" Minimum="0" Maximum="1" Ticks="0.1" Value="0.5"></Slider> 43 </StackPanel> 44 45 </StackPanel> 46 <Ellipse Grid.Row="1" Grid.Column="1" Stroke="Pink"> 47 <Ellipse.Resources> 48 <local:Rgb2ColorConverter x:Key="colorCvt"></local:Rgb2ColorConverter> 49 </Ellipse.Resources> 50 <Ellipse.Fill> 51 <MultiBinding Converter="{StaticResource colorCvt}"> 52 <Binding ElementName="sldR" Path="Value"></Binding> 53 <Binding ElementName="sldG" Path="Value"></Binding> 54 <Binding ElementName="sldB" Path="Value"></Binding> 55 </MultiBinding> 56 </Ellipse.Fill> 57 <Ellipse.Opacity> 58 <Binding Path="Value" ElementName="sldO" > 59 60 </Binding> 61 </Ellipse.Opacity> 62 </Ellipse> 63 </Grid> 64 </Window>
六、绑定的删除
我们可以使用类BindingOperations中的以下函数来删除绑定
public static void ClearAllBindings(DependencyObject target);
public static void ClearBinding(DependencyObject target, DependencyProperty dp);