一、何为数据绑定
场景:考虑一个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>
View Code
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 }
View Code
五、转换器
上面的例子中,当拖动滑动条的时候,文本框中显示的数字会显示好多位小数,现在我们想让在文本框中只显示两位小数,这时候就需要转换器来实现了。
定义:转化器是一个类,用于拦截数据源和目标之间的数据,拦截之后,就可以按照我们的意愿对数据进行操作。
转换器包括单值转换器和多值转换器。单值转换器需要实现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 }
View Code
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>
View Code
现在考虑以下场景,我们有一个椭圆,椭圆的颜色是根据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 }
View Code
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>
View Code
六、绑定的删除
我们可以使用类BindingOperations中的以下函数来删除绑定
public static void ClearAllBindings(DependencyObject target);
public static void ClearBinding(DependencyObject target, DependencyProperty dp);