1.元素绑定
元素绑定是数据绑定的一种,将源对象和目标对象的元素绑定在一起,使从源对象提取一些信息来设置目标对象的属性。
元素绑定的好处是使得元素的交互方式自动化,当用户修改控件时,另一元素自动更新,不需要编写样板代码(这是WinForm技术不能实现的)。
2.绑定表达式
该窗口中包含两个控件:一个Slider控件和一个单行文本TextBlock控件。通过拖动条形控件的滑块调整文本字体大小。
显然不用元素绑定也容易实现,可以简单地响应Slider.ValueChanged事件,将滑块的当前值复制到TextBlock来实现。但是使用元素绑定实现起来会更简单。
<Slider Name="slider" Minimum="1" Maximum="40" Value="10" TickFrequency="1" TickPlacement="TopLeft" Margin="3"></Slider>
<TextBlock Name="textblock" Text="Simple Text" FontSize="{Binding ElementName=slider, Path=Value}" Margin="3"></TextBlock>
绑定表达式以单词Binding开头,ElementName指示源元素,Path指示源元素的属性。之所以用Path而不用Property是因为Path可能指向一个属性的属性(如FontFamily.Source)、属性索引器(如Content.Children[0])或更多层的属性的属性的属性等。
如果在上面代码基础上再加两个按钮,用于设置最小字体和最大字体。
单击Set to Large按钮,会运行下面代码:
private void Button_Click_SetLarge(object sender, RoutedEventArgs e)
{
slider.Value = slider.Maxmum;
}
实际效果是滑块滑到最大值,文本字体大小变为对应的最大值。
然而如果我们把代码写成下面的形式则不能正常工作。
private void Button_Click_SetLarge(object sender, RoutedEventArgs e)
{
textblock.FontSize = slider.Maxmum;
}
上面代码中修改了文本的大小,但是滑块没有更新。而且再调整滑块时文本大小不会跟着变化。上面代码破坏了元素绑定。
3.绑定模式
可以通过绑定模式来解决上述问题。代码如下:
<TextBlock Name="textblock" Text="Simple Text" FontSize="{Binding ElementName=slider, Path=Value, Mode=TwoWay}" Margin="3"></TextBlock>
Binding的Mode属性为枚举类型BindingMode。具体的值有:
OneWay:单向绑定。
TwoWay:双向绑定。
OneTime:一次绑定。只在程序运行时更新目标值。
OneWayToSource:反向单向绑定。
Default:缺省值。默认是单向绑定。
4.使用代码创建绑定
也可以使用C#代码来创建绑定:
Binding binding = new Binding();
binding.Source = slider;
binding.Path = new PropertyPath("Value");
binding.Mode = BindingMode.TwoWay;
textblock.SetBinding(TextBlock.FontSizeProperty, binding);
使用下面代码移除绑定:
BindingOperations.ClearBinding(textblock, TextBlock.FontSizeProperty);
或
BindingOperations.ClearAllBindings(textblock);
5.更新时机问题
从目标到源的更新可能不会立即发生。主要出现在TwoWay和OneWayToSource模式下。例如,用一个文本输入框TextBox和Slider的Value绑定,通过在TextBox里输入数字来动态修改Slider的Value。如果使用TwoWay模式,则输入数字后Slider.Value不会自动更新,只有当TextBox失去焦点时才更新。
<TextBox Name="textbox" Height="23" TextWrapping="Wrap" Text="{Binding ElementName=slider, Path=Value, Mode=TwoWay}" Width="120"/>
如果不想出现这种情况,想要在TextBox输入任意值时就更新Slider,则有两种方法可以解决:
1.在上面代码里加入UpdateSourceTrigger=PropertyChanged使得只要输入内容改变就更新而不是失去焦点才更新;
2.让Slider去绑定TextBox,Mode还是TwoWay。
6.绑定到非元素对象
之前我们用到的是Binding.ElementName元素对象,它必须是XAML文件里的元素。然而我们也可以绑定其他非元素对象,例如绑定系统的静态数据对象、绑定资源等。
当绑定非元素对象时,需用到下面三种之一来替代ElementName:
(1)Source:通常是一个已存在的静态对象。例如绑定Net类库的静态变量代码如下:
<TextBlock Name="textblock" Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=Source}" Margin="3"></TextBlock>
绑定程序静态资源的代码如下:
<Window.Resources>
<FontFamily x:Key="CustomFont">Calibri1</FontFamily>
</Window.Resources>
<TextBlock Name="textblock2" Text="{Binding Source={StaticResource CustomFont}, Path=Source}" Margin="3"></TextBlock>
(2)RelativeSource:从上下文找到符合绑定要求的元素进行绑定。包括在当前元素的所有属性进行搜索,和在其父节点、祖父节点一直往上的上下文搜索。
例如:绑定当前控件自己的Margin的值,如下:
<TextBlock Name="textblock3" Margin="4,4,4,4" Text="{Binding RelativeSource={RelativeSource Self},Path=Margin}"></TextBlock>
例如:绑定当前控件祖先节点Window窗口的名字,如下:
<TextBlock Name="textblock3" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=Title}"></TextBlock>
(3)DataContext:如果有大量元素绑定同一对象,代码标记会有很长的重复。使用DataContext绑定源可以简化代码。例如下面的代码就很臃肿:
<StackPanel>
<TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily},Path=Source}">
</TextBlock>
<TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily},Path=LineSpacing}"> </TextBlock>
<TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=FamilyTypefaces[0].Style}"> </TextBlock>
</StackPanel>
代码中所有TextBlock绑定相同的数据SystemFonts.IconFontFamily,但是它们绑定的属性不同。我们使用DataContext对代码进行简化如下:
<StackPanel DataContext="{x:Static SystemFonts.IconFontFamily}">
<TextBlock Text="{Binding Path=Source}">
</TextBlock>
<TextBlock Text="{Binding Path=LineSpacing}"> </TextBlock>
<TextBlock Text="{Binding Path=FamilyTypefaces[0].Style}"> </TextBlock>
</StackPanel>