[笔记/简译]WPF的新特性——依赖属性(4)

来源:互联网 发布:mac系统清理工具 编辑:程序博客网 时间:2024/05/01 07:12

对多种提供器的支持

 

       WPF包含许多强大的机制,这些机制尝试独立地设置依赖属性值。WPF需要五个步骤来计算依赖属性的最终值:确定基本值—>计算表达式(如果有)—>应用动画—>强制—>验证。

 

第一步    确定基本值

       基本值的计算被分解成许多属性值的提供器,下列提供器可以设置大部分依赖属性,按从高到低的优先级排列:

1)局部值(local value)提供器;

2)样式触发(style trigger)提供器;

3)模板触发(template trigger)提供器;

4)样式设定(style setter)提供器;

5)主题样式触发(theme style trigger)提供器;

6)主题样式设定(theme style setter)提供器;

7)属性值继承(property value inheritance)提供器;

8)默认值(default value)提供器

       我们已经见过一些属性值提供器了,比如属性值继承提供器。从技术上说,局部值提供器是任何调用了DependencyObject.SetValue的对象,但是它通常可以被看作一种在XAML或程序代码中给普通属性赋值的操作。默认值提供器设定的值是依赖属性在注册时设定的那个值,因此其优先级最低。(详情参见依赖属性(1)对依赖属性实现的介绍)

       这个优先级顺序解释了为何StatusBarFontSizeFontStyle属性没有受属性值继承影响,这是因为StatusBar已经被主题样式提供器(第6级)设定为匹配系统的值。尽管它的优先级超过了属性值继承提供器(第7级),但是我们仍旧可以通过较高优先级的提供器来设定字体属性值。

 

第二步    计算表达式

       如果来自第一步的值是一个表达式(一种派生自System.Windows.Expression的对象),则WPF负责执行一个将表达式转换为具体值的特殊计算步骤。在WPF 3.0中,表达式仅可是动态资源或数据绑定,未来版本的WPF可能会允许其它种类的表达式。

 

第三步    应用动画

       在一个或多个动画正在运行的时候,它们拥有更改当前属性值(来自第二步的值)的权利。因此,动画比任何其它属性值提供器的优先级都高,这对WPF初学者来说有些难度。

 

第四步    强制(Coerce

       在所有的属性值提供器完成设定后,WPF将结果属性值传递到为依赖属性注册的CoerceValueCallback委托中。我们可以在这个委托内编写自己的代码,用以重新计算依赖属性值或执行强制转换。例如,WPF内建控件ProgressBar使用这个回调来约束其Value依赖属性。这个属性的值应在MinimumMaximum之间,但如果输入值小于Minimum,则其返回Minimum值,如果输入值大于Maximum,则其返回Maximum值。

       (这个CoerceValueCallbackMSDN中的说法是“为只要重新计算依赖项属性值或专门请求强制转换时就调用的方法提供一个模板,此回调的实现应检查 baseValue 中的值,并根据该值或其类型确定该值是否需要进一步进行强制转换”。按我自己的理解,这个委托的作用就是为上一步计算好的属性值添加使其合理化的逻辑。

       MSDN为这个回调提供了一个示例(其中d是该属性所在的对象,value是该属性在尝试执行任何强制转换之前的新值):

private static object CoerceButtonColor(DependencyObject d, object value)

{

ShirtTypes newShirtType = (d as Shirt).ShirtType;

if (newShirtType == ShirtTypes.Dress || newShirtType == ShirtTypes.Bowling)

{

return ButtonColors.Black;

}

return ButtonColors.None;

}

从中可以看出,依赖属性ButtonColor的值依赖于对象dShirtType,但并没有使用上一步计算好的值value。)

 

第五步    验证

       最终,经过强制转换的值被传递到为依赖属性注册的ValidateValueCallback委托中。如果输入值是合法的,则此委托必须返回true;否则,必须返回false。返回false将导致一个取消整个过程的异常。

MSDN中的示例:

private static bool ShirtValidateCallback(object value)

{

ShirtTypes sh = (ShirtTypes)value;

return (sh == ShirtTypes.None || sh == ShirtTypes.Bowling || sh == ShirtTypes.Dress || sh == ShirtTypes.Rugby || sh == ShirtTypes.Tee);

}

有此可见,在这个回调的实现中,应当加入验证属性值的范围的逻辑。同理,在ProgressBar为其double类型的ValueProperty依赖属性实现的验证回调中,判断了属性值是否是合法的double数据。)

 

       如果我们想要查看依赖属性值是由那一个提供器设置的,可以使用静态的DependencyPropertyHelper.GetValueSource方法来调试。该方法返回一个ValueSoruce结构,它包含一个暴露基本值来自于何方的BaseValueSource枚举,以及对应2~4步的IsExpressionIsAnimatedIsCoerced属性。当我们在前面的StatusBar实例上为FontSizeFontStyle调用这个方法后,BaseValueSource的返回值为DefaultStyle,这说明它的值来自于主题风格设定提供器(主题风格有时也称作默认风格,对应主题风格触发器就是DefaultStyleTrigger)。此外切记不要在产品代码中使用这个方法。

 

       在前面通过事件处理器设置按钮文本颜色的示例中,有一个问题值得注意:在MouseLeave事件处理器中,按钮的Foreground被赋予了局部值Brushes.Black(局部值提供器)。这与按钮的初始状态不同,其初始状态是由主题风格设定提供器设置的。如果主题变换了,且新主题(或其它优先级较高的提供器)尝试改变Foreground的默认值,那么局部值提供器会胜出(因为它的优先级高于主题风格提供器),并将Foreground设置为黑色。对于这种情况,我们可能会希望清除这个局部值,以便可以使用主题的默认设定。DependencyObject提供了一个ClearValue方法,可以清除局部值:

 

b.ClearValue(Button.ForegroundProperty);

 

       值得注意的是,在IsMouseOver上的触发器与此不同,当其处于“未触发”状态时,将会忽略对属性值的计算。

 

原创粉丝点击