Learning WPFUnleashedBook chapter 3Important new concepts in WPF

最近读了WPFUnleashed这本书的第三章,主要介绍了一下几个方面:

1. Logical and Visual Trees(逻辑树和视觉树)
2. Dependency Properties(依赖属性)
3. Routed Events
4. Commands
5. WPF 类的层次概述

下面分别是我觉得比较重要的地方的笔记。
1. 逻辑树和视觉树
逻辑树就是我们实际看到的节点间的树的结构。视觉树可以看作是对逻辑树的扩展,它把每一个元素还原
成它的真实面目,我们可以看到每一个元素由那些visual components组成的。例如一个ListBox,在逻辑
树中它就是一个元素,但在视觉树中我们可以看到它包含一个Border,两个Scrollbar,等等组成的。

注意在XamlPad中,只能显示Page的Visual Tree,对于Window,我们可以把根节点改成Page来查看它的
Visual Tree。

逻辑树在构造函数中就可以打印出来查看了,但是视觉树要等到整个元素(例如Window)被layout至少一次
以后才可以形成的。所以我们可以在Override void OnContentRendered(EventArgs e)中查看视觉树。

2. Dependency Properties
WPF用Dependency Properties来实现styling,动态data binding,animation,change notification等等功能。
为什么叫Dependency Properties呢,因为一个Dependency property在任何时刻都能及时的根据许多的
Providers来决定它的值,例如这个providers可能是从父元素那里继承来的属性值、由动画连续改变它的值,
自己定义的缺省值等等。

Property value inheritance(属性值的继承机制)

注意:不是所有的dependency property都能被继承的,在Dependency.Register中我们通过
FrameworkPropertyMetadataOptions.Inherits来指定这个属性可以被继承。

因为有多个Providers,它们是通过下面的顺序来决定dependency property的最终值的:

Determine Base Value -----> Evaluate -----> Apply animations -----> Coerce -----> Validate

Step(1) Determine Base Value

Base value 有以下八个提供者,其中1为最高的优先级:

1. Local value
2. Style triggers
3. Template triggers
4. Style setters
5. Theme Style triggers
6. Theme Style setters
7. Property value inheritance
8. Default value
其中Local value是指直接对某个元素设置的值,它会调用或者我们直接调用DependencyObject.SetValue
方法。Default value是Register是设置的值。

Step(2) Evaluate

如果第一步得到的值是一个expression(derives from System.Windows.Expression),WPF则根据这个
expression来计算值,这个expression可能来自于使用了DynamicResource或者是data binding。

Step(3) Apply animations

如果有动画,动画会改变上面得到的值。

Step(4) Coerce

将上面得到的值给CoerceValueCallback代理,判断是否需要返回一个强制的value。

Step(5) Validate

最后,将上面得到的值给ValidateValueCallback代理,这个代理会返回true如果上面的值是合法的,
false说明不合法,会取消整个过程,或者抛一个异常。

Tooltip:在debug的时候,如果不能确定我们最终得到的值来自哪里,我们可以用如下的方法,
例如有一个StatusBar,名称是statusBar:
ValueSource vs = DependencyPropertyHelper.GetValueSource(statusBar, FontSizeProperty);
vs 的第一个属性值是BaseValueSource,它是一个枚举,对应了Step(1)中的base value
的来源,其他的值是IsAnimated,IsCoerced,IsExpression等,对应其它几步的来源。
假设有下面的xaml:

<StackPanel TextElement.FontSize="20">
      <Button>click</Button>
      <StatusBar>You have successfully registered this product.</StatusBar>
</StackPanel>

我们会发现Button的字体继承了父亲的FontSize属性,大小是20。但StatusBar的字体很小,明显不是
20,为什么呢?
如果我们用上面的方法得到的BaseValueSource的值是DefaultStyle,DefaultStyle表示这个base value
来自当前系统的theme style setter的设置,排在base value的第6位,而property inheritance value排在
第7位,所以优先用系统的字体设置了。类似的还有Menu、ToolTip等等。

Clearing a local value(清除一个本地设置的值)

例如有一个Button名称为b,我们在MouseEnter trigger中设置了Foreground属性为Red,但是当Mouse
Leave的时候如何回到最初的default值呢,这时只要调用b.ClearValue(b, ForegroundProperty)就可以了。

如果我们想要清除所有的已经设定了的LocalValue,可以调用DependencyObject的
GetLocalValueEnumerator()方法来获得所有已经设定了的LocalValue的枚举:

LocalValueEnumerator locallySetProperties = uie.GetLocalValueEnumerator();
while (locallySetProperties.MoveNext())
{
DependencyProperty propertyToClear = (DependencyProperty)locallySetProperties.Current.Property;

if (!propertyToClear.ReadOnly) { uie.ClearValue(propertyToClear); }

}

Attached Properties(附加属性)

Attached Property是一种特殊的Dependency property,可以看作是在子元素中设置父元素的某些属性。
注意在上面的xaml片段中,因为StackPanel本身是没有FontSize属性的,这里是用到了TextElement的
FontSize这个Attached property,为什么能够这样做呢?
我们看这个属性的注册方式:

TextElement.FontSizeProperty = DependencyProperty.RegisterAttached(
“FontSize”, typeof(double), typeof(TextElement), new FrameworkPropertyMetadata(
SystemFonts.MessageFontSize, FrameworkPropertyMetadataOptions.Inherits |
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsMeasure),
new ValidateValueCallback(TextElement.IsValidFontSize));

然后在Control类里用AddOwner方法把Control类也当作是这个已经注册了的Attached property的Owner:

Control.FontSizeProperty = TextElement.FontSizeProperty.AddOwner(
typeof(Control), new FrameworkPropertyMetadata(SystemFonts.MessageFontSize,FrameworkPropertyMetadataOptions.Inherits));

这样就相当于Control类借用了这个属性了。而Control类又是Window类的基类,所以就实现了。
当这个根元素是Page的时候,也可以这样做,是因为在Page类中

FontSizeProperty = TextElement.FontSizeProperty.AddOwner(typeof(Page));

Attached Properties的扩展用法

例如GeometryModel3D类本身没有Tag属性,但是我们可以借用FrameworkElement的Tag属性,达到
类似于该类有这个属性的效果:

GeometryModel3D model = new GeometryModel3D();
model.SetValue(FrameworkElement.TagProperty, my custom data);

这样接着我们就可以通过GetValue得到这个customized data了:
object obj = model.GetValue(FrameworkElement.TagProperty);







 











原文地址:https://www.cnblogs.com/bear831204/p/1317619.html