原文链接:http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/148e95c6-6fb5-4399-8a56-41d0e0a72f1b
疑问:
以下代码定义了一个TextBlock的默认样式:
< Style.Triggers >
< Trigger Property ="Text" Value ="" >
< Setter Property ="Visibility" Value ="Collapsed" />
</ Trigger >
</ Style.Triggers >
</ Style >
< Grid x:Uid ="Grid_7" Width ="Auto" Height ="Auto" >
< Grid.ColumnDefinitions >
< ColumnDefinition />
< ColumnDefinition />
< ColumnDefinition />
< ColumnDefinition />
</ Grid.ColumnDefinitions >
< Grid.RowDefinitions >
< RowDefinition />
</ Grid.RowDefinitions >
< Image

< TextBlock

< TextBlock

< TextBlock

</ Grid >
</ HierarchicalDataTemplate >
Johnny Q. 回答:
这个行为“by design”,简而言之,当一个直接继承自 FrameworkElement 的对象在一个 FrameworkTemplate 之中时,默认样式不起作用(当该样式定义在 FrameworkTemplate 之外的逻辑树的祖先节点时)。
This is by design, as far as I know (if it's good or bad, that can be debatable; maybe things will change in future releases). For short, default styles do not work with objects directly derived from FrameworkElement, when placed inside FrameworkTemplate. See also http://shevaspace.blogspot.com/2007/03/wtf-of-wpf-part-one-templating-styling.html
注:
http://shevaspace.blogspot.com/2007/03/wtf-of-wpf-part-one-templating-styling.html (需FQ)中更为详细的描述了这个问题,给出了一个可以在 xamlpad 中演示的例子,并指出了这个行为是在 FrameworkElement.FindImplicitStyleResource() 函数中进行限制的。 以下先给出链接中的例子:



























































之后,我们打开 MS 发布的 wpf 的源代码(.net 3.0版)找到 FrameworkElement.FindImplicitStyleResource() 函数(注意参数重载,第一个参数为FrameworkElement的才是我们要找的):


1 // FindImplicitSytle(fe) : Default: unlinkedParent, deferReference
2 internal static object FindImplicitStyleResource(FrameworkElement fe, object resourceKey, out object source)
3 {
4 // Do a FindResource call only if someone in the ancestry has
5 // implicit styles. This is a performance optimization.
6
7 #if !DEBUG
8 if (fe.ShouldLookupImplicitStyles)
9 {
10 #endif
11 object unlinkedParent = null;
12 bool allowDeferredResourceReference = false;
13 bool mustReturnDeferredResourceReference = false;
14
15 // Implicit style lookup must stop at the app.
16 bool isImplicitStyleLookup = true;
17
18 // For non-controls the implicit StyleResource lookup must stop at
19 // the templated parent. Look at task 25606 for further details.
20 DependencyObject boundaryElement = null;
21 if (!(fe is Control))
22 {
23 boundaryElement = fe.TemplatedParent;
24 }
25
26 object implicitStyle = FindResourceInternal(fe, null, FrameworkElement.StyleProperty, resourceKey, unlinkedParent, allowDeferredResourceReference, mustReturnDeferredResourceReference, boundaryElement, isImplicitStyleLookup, out source);
27
28 // The reason this assert is commented is because there are specific scenarios when we can reach
29 // here even before the ShouldLookupImplicitStyles flag is updated. But this is still acceptable
30 // because the flag does get updated and the style property gets re-fetched soon after.
31
32 // Look at AccessText.GetVisualChild implementation for example and
33 // consider the following sequence of operations.
34
35 // 1. contentPresenter.AddVisualChild(accessText)
36 // 1.1. accessText._parent = contentPresenter
37 // 1.2. accessText.GetVisualChild(

38 // 1.2.1 accessText.AddVisualChild(textBlock)
39 // 1.2.1.1 textBlock.OnVisualParentChanged()
40 // 1.2.1.1.1 FindImplicitStyleResource(textBlock)
41 // .
42 // .
43 // .
44 // 1.3 accessText.OnVisualParentChanged
45 // 1.3.1 Set accessText.ShouldLookupImplicitStyle
46 // 1.3.2 FindImplicitStyleResource(accessText)
47 // 1.3.3 Set textBlock.ShouldLookupImplicitStyle
48 // 1.3.4 FindImplicitStyleResource(textBlock)
49
50 // Notice how we end up calling FindImplicitStyleResource on the textBlock before we have set the
51 // ShouldLookupImplicitStyle flag on either accessText or textBlock. However this is still acceptable
52 // because we eventually going to synchronize the flag and the style property value on both these objects.
53
54 // Debug.Assert(!(implicitStyle != DependencyProperty.UnsetValue && fe.ShouldLookupImplicitStyles == false),
55 // "ShouldLookupImplicitStyles is false even while there exists an implicit style in the lookup path. To be precise at source " + source);
56
57 return implicitStyle;
58 #if !DEBUG
59 }
60
61 source = null;
62 return DependencyProperty.UnsetValue;
63 #endif
64 }
65
在这里我们只需要关注两个地方:
1. 第4、5行的注释。
2. 第18-24行的注释及代码。
可知,如果FrameworkElement的 非Control子类 的对象,其默认样式的搜索边界是其TemplateParent,而MS是出于性能优化的角度,进行这样的设计的。
为什么值得这样设计呢?以下是我的分析、推测:
我们知道,Control 类有 Template 属性,依照上面的结论, ControlTemplate 中 FrameworkElement 的非Control子类 的对象,其默认样式的搜索边界就是 其 TemplateParent,这样设计之后,搜索默认样式的速度就会被加快(可通过使用Reflector+Reflector's baml viewer add-in 查看 PresentationFramework.Aero.dll 中的 themes/aero.normalcolor.baml 来查看 Aero 主题的控件的默认Template)。
回到篇首提到的那个问题,我们可知,可通过在HierarchicalDataTemplate 的 Grid.Resource 中定义 TextBlock 的默认样式来实现提问者想要的功能。