【msdn wpf forum翻译】TextBlock等类型的默认样式(implicit style)为何有时不起作用?

简介: 原文:【msdn wpf forum翻译】TextBlock等类型的默认样式(implicit style)为何有时不起作用?原文链接:http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/148e95c6-6fb5-4399-8a56-4...
原文: 【msdn wpf forum翻译】TextBlock等类型的默认样式(implicit style)为何有时不起作用?

原文链接:http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/148e95c6-6fb5-4399-8a56-41d0e0a72f1b


疑问:
以下代码定义了一个TextBlock的默认样式:
         < Style  TargetType =" {x:Type TextBlock} " >
            
< Style.Triggers >
                
< Trigger  Property ="Text"  Value ="" >
                    
< Setter  Property ="Visibility"  Value ="Collapsed" />
                
</ Trigger >
            
</ Style.Triggers >
        
</ Style >
以下是TreeView的一个 HierarchicalDataTemplate 的定义:
             < HierarchicalDataTemplate  DataType ="XYZ"  ItemsSource  =" {Binding} " >
                
< Grid  x:Uid ="Grid_7"  Width ="Auto"  Height ="Auto" >
                    
< Grid.ColumnDefinitions >
                        
< ColumnDefinition />
                        
< ColumnDefinition />
                        
< ColumnDefinition />
                        
< ColumnDefinition />
                    
</ Grid.ColumnDefinitions >
                    
< Grid.RowDefinitions >
                        
< RowDefinition />
                    
</ Grid.RowDefinitions >
                    
                    
< Image   Grid.Column ="0"  Grid.Row ="0"  Margin ="5,0,0,0" />
                    
< TextBlock   Grid.Column ="1"  Grid.Row ="0"  Padding ="5,0,0,0" />
                    
< TextBlock   Grid.Column ="2"  Grid.Row ="0"  Foreground ="Blue" Padding ="5,0,0,0" />
                    
< TextBlock   Grid.Column ="3"  Grid.Row ="0"  Foreground ="Red" Padding ="5,0,0,0" />                    
                
</ Grid >
            
</ HierarchicalDataTemplate >
之后,就算我们把 TextBlock的默认样式加到<TreeView.Resources> 之中,TextBlock也不会像预期的那样(预期:如果为空串时 Visibility == Collapsed)。
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() 函数中进行限制的。 以下先给出链接中的例子:

 

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif Code
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
         xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"> 
    
<Page.Resources> 
        
<Style TargetType="{x:Type TextBlock}" x:Key="textBlock"> 
            
<Setter Property="TextElement.Foreground" Value="Cyan"/> 
        
</Style> 
        
<Style TargetType="{x:Type ContentPresenter}"> 
            
<Setter Property="TextElement.Foreground" Value="Cyan"/> 
        
</Style> 
        
<Style TargetType="{x:Type  AccessText}"> 
            
<Setter Property="TextElement.Foreground" Value="Cyan"/> 
        
</Style> 
        
<Style TargetType="{x:Type Rectangle}"> 
            
<Setter Property="Fill" Value="Cyan"/> 
        
</Style> 
        
<Style TargetType="{x:Type InkCanvas}"> 
            
<Setter Property="Background" Value="Cyan"/> 
        
</Style> 
        
<Style TargetType="{x:Type Border}"> 
            
<Setter Property="BorderThickness" Value="10"/> 
            
<Setter Property="BorderBrush" Value="Cyan"/> 
        
</Style> 
        
<Style TargetType="{x:Type StackPanel}"> 
            
<Setter Property="Background" Value="Green"/> 
        
</Style> 
        
<Style TargetType="{x:Type Control}"> 
            
<Setter Property="Template"> 
                
<Setter.Value> 
                    
<ControlTemplate> 
                        
<Label>Inside Control</Label> 
                    
</ControlTemplate> 
                
</Setter.Value> 
            
</Setter> 
        
</Style> 
        
<ControlTemplate x:Key="template"> 
            
<!--WTF, StackPanel, TextBlock, AccessText, ContentPresenter, InkCanvas,  Image, Rectangle, 
              MediaElement etc cannot pick up their respective implicit styles here, 
              one takeway from this empircal discovery is that Elements directly derived from FramworkElement cannot work in this scenario.
--> 
            
<StackPanel> 
                
<TextBlock Name="tb">inside TextBlock</TextBlock> 
                
<AccessText>inside AccessText</AccessText> 
                
<ContentPresenter> 
                    
<ContentPresenter.Content> 
                    inside ContentPresenter 
                    
</ContentPresenter.Content> 
                
</ContentPresenter> 
                
<InkCanvas/> 
                
<Rectangle Width="40" Height="40"/> 
                
<!--WTF, Border can pick up implicit style here.--> 
                
<Border Width="200" Height="20"><TextBlock Name="tb2">bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb</TextBlock> </Border>
                
<!--WTF, Control can pick up implicit style here, since Border and Control can work here, our previous hypothesis breaks.--> 
                
<Control/> 
            
</StackPanel> 
        
</ControlTemplate> 
    
</Page.Resources> 
    
<Control Template="{StaticResource template}"/> 
</Page> 


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

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif Code
 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 的默认样式来实现提问者想要的功能。

目录
相关文章
WPF疑难问题之Treeview中HierarchicalDataTemplate多级样式
WPF疑难问题之Treeview中HierarchicalDataTemplate多级样式
354 0
|
3月前
|
开发者 C# 存储
WPF开发者必读:资源字典应用秘籍,轻松实现样式与模板共享,让你的WPF应用更上一层楼!
【8月更文挑战第31天】在WPF开发中,资源字典是一种强大的工具,用于共享样式、模板、图像等资源,提高了应用的可维护性和可扩展性。本文介绍了资源字典的基础知识、创建方法及最佳实践,并通过示例展示了如何在项目中有效利用资源字典,实现资源的重用和动态绑定。
92 0
|
3月前
|
开发者 C# 存储
WPF开发者必读:样式与模板的艺术,轻松定制UI外观,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用开发中,样式与模板是实现美观界面与一致性的关键工具。样式定义了控件如字体、颜色等属性,而模板则允许自定义控件布局与子控件,两者均可存储于`.xaml`文件中。本文介绍了样式与模板的基础知识,通过示例展示了如何创建并应用它们来改变按钮的外观,从而提升用户体验。
87 0
|
3月前
|
存储 前端开发 C#
WPF/C#:更改界面的样式
WPF/C#:更改界面的样式
43 0
WPF-布局样式练习-Day02-聊天气泡
WPF-布局样式练习-Day02-聊天气泡
242 1
|
6月前
|
文字识别 C# 开发者
WPF开源的一款免费、开箱即用的翻译、OCR工具
WPF开源的一款免费、开箱即用的翻译、OCR工具
115 0
|
6月前
|
C#
浅谈WPF之样式与资源
WPF通过样式,不仅可以方便的设置控件元素的展示方式,给用户呈现多样化的体验,还简化配置,避免重复设置元素的属性,以达到节约成本,提高工作效率的目的,样式也是资源的一种表现形式。本文以一个简单的小例子,简述如何设置WPF的样式以及资源的应用,仅供学习分享使用,如有不足之处,还请指正。
124 0
|
C# Windows
WPF技术之TextBlock控件
WPF(Windows Presentation Foundation)的TextBlock控件是用于显示文本的控件。与Label控件相比,TextBlock提供了更多的灵活性和格式化选项。
497 1
WPF-Binding问题-模板样式使用Binding TemplatedParent与TemplateBinding区别
WPF-Binding问题-模板样式使用Binding TemplatedParent与TemplateBinding区别
228 0
WPF-样式问题-处理ListBox、ListView子项内容全填充问题
WPF-样式问题-处理ListBox、ListView子项内容全填充问题
234 0