WPF中的Dependency Property(2)
在前一个Post中,我们举例说明了如何实现一个Dependency Property,而在这一个Post中,我们将重点关注Dependency Property的变化通知能力。
我们知道,无论何时,只要Dependency Property的值改变了,WPF就会自动依据属性元数据的设定触发一系列的动作。而这些设定,就是我们事先在定义Dependency Property时已经定义过的,如前一个Post中的FontSize property:
metadata.AffectsMeasure = true;
metadata.Inherits = true;
metadata.IsNotDataBindable = false;
metadata.DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
AffectsMeasure标志表明属性的改变是否会造成控件尺寸的重新计算(这也需要控件进行重绘);Inherits标志表明属性值是否允许被继承;IsNotDataBindable标志表明属性是否允许进行数据绑定;DefaultUpdateSourceTrigger标志表明更新数据绑定的时机。
因此,上面提到的,Dependency Property的改变会触发的一系列动作包括:重新呈现适当的元素、更新当前布局、刷新数据绑定等。
内建的变化通知最有趣的特征之一是属性触发器,它可以在属性值改变时执行自定义的动作,而不用添加任何过程式代码。
假设我们要实现这样的功能:当鼠标移动到Button上时,将文本的颜色变为Blue;而离开时恢复。如果没有属性触发器,我们可能这样来实现:
前台代码:
<Button Content="Click" Width="100" Height="50"
VerticalAlignment="Bottom" HorizontalAlignment="Center"
MouseEnter="ButtonOnMouseEnter" MouseLeave="ButtonOnMouseLeave"/>
后台代码:
private void ButtonOnMouseEnter(object sender, MouseEventArgs e)
{
Button btn = sender as Button;
if (btn != null)
{
btn.Foreground = Brushes.Blue;
}
}
private void ButtonOnMouseLeave(object sender, MouseEventArgs e)
{
Button btn = sender as Button;
if (btn != null)
{
btn.Foreground = Brushes.Black;
}
}
然而,有了属性触发器,我们完全可以在前台XAML中完成相同的行为,而且代码相当少:
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Blue"/>
</Trigger>
它的功能是:当IsMouseOver属性变成True时,改变Foreground属性为Brushes.Blue;而当IsMouseOver属性变成False时,设置Foreground属性为原来的值(此处不需要写代码,WPF自动完成)。IsMouseOver属性的改变能让属性触发器得到通知,这就是变化通知能力的一种体现。
当我们把Trigger赋给Button时,遇到了一点困难,不能把属性触发器直接应用到控件上。在WPF 3.0中,由于人为限制,Trigger只能在Style对象内部应用。因此,可以在<Window.Resources>中添加或者在<Button.Style>中添加。
我们在<Window.Resources>中添加作示例:
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Blue"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
WPF除了支持属性触发器外,还支持另外两种触发器——数据触发器、事件触发器。这些将会在其他的Post中详细说明。
注意:不要被元素的Triggers集合所愚弄!
FrameworkElement的Triggers属性是一个可读写的TriggerBase项(三个触发器类型的通用基类)集合,因此,它是把属性触发器添加给元素最简单的方式。不幸的是,这个集合只能包括事件触发器,因为WPF团队没有时间来实现这种支持。如果你尝试添加一个属性触发器到Triggers属性中,将导致运行时抛出一个异常。