WPF中的Dependency Property(4)
在Dependency Property(3)——属性值继承中,我们提到一些具有一定优先权的源,也就是属性值提供程序,它们到底有哪些呢?这就是我们在这个Post中要说的Dependency Property支持多个属性值提供程序。
WPF提供了强大的机制来保证多个提供程序设置Dependency Property的值,而这些提供 程序按照一定的流程、一定的优先权来进行设置。如果没有设计良好的机制来处理这些完全不同的属性值提供程序,这个系统就会变得混乱,属性值也会变得不稳定。
下图显示了设置Dependency Property的流程,WPF中的每一个Dependency Property也是按照这一流程最终计算出来它的值的。多亏了Dependency Property中的变化通知能力,这个流程才得以自动发生。
1. 第一步:判断基础值
大多数属性提供程序会影响属性的基础值。下面以优先权从高到低的顺序列出了可以设置大多数Dependency Property的8个提供程序:
(1) 本地值 (2) 样式触发器
(3) 模板触发器 (4) 样式设置程序
(5) 主题样式触发器 (6) 主题样式设置程序
(7) 属性值继承 (8) 默认值
本地值,技术上指任何对DependencyObject.SetValue()的调用,常见的形式是在XAML或在过程式代码中的属性赋值。默认值指的是Dependency Property注册时使用的初始值,优先权最低。
这个优先顺序解释了Dependency Property(3)中StatusBar控件的“特殊”。同样,如果我们设置Dependency Property (3)中Label的FontSize和FontStyle,那么,它将不再从Window那里继承属性值,这是因为本地值比属性值继承的优先权高,本地值说了算,J。
2. 第二步:计算
如果第一步中的值是表达式,那么WPF会执行一种特殊的演算步骤——把表达式转换为具体的结果。目前,表达式仅在使用动态资源或数据绑定时起作用。
3. 第三步:应用动画
对属性应用的动画胜过其他任何属性值提供程序——就连本地值也不是它的“对手”!
4. 第四步:限制
在所有属性值提供程序处理过之后,WPF将拿到一个几乎是终值的属性值。如果Dependency Property已经注册了CoerceValueCallback,还会把这个属性值传递给CoerceValueCallback委托,该回调函数依据自己的逻辑返回一个新的值。比如ProgressBar,会使用回调来限制叫作Value的Dependency Property,保证其在最大值最小值范围内。
5. 第五步:验证
最后,如果Dependency Property已经注册了ValidateValueCallback,第四步中获得的值将被传入ValidateValueCallback委托。如果输入值有效,该回调函数必须返回true;否则返回false。返回false将会导致抛出一个异常,并使整个流程被取消。
走完这五步,Dependency Property的最终值也被确定下来。
提示:清除本地值
如果某个控件中的某个属性的值来自于样式(或者主题样式)的提供程序,在代码中一旦人为设定了值,那么它将不再从样式(或者主题样式)处获得值。如何才能让属性的值重新来自于样式(或者主题样式)呢?DependencyObject提供了这种机制,可以通过调用ClearValue()方法来实现。调用之后,会把本地值删除,重新计算基础值。