原文:【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() 函数中进行限制的。 以下先给出链接中的例子:

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的才是我们要找的):

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

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

  1. 【msdn wpf forum翻译】获取当前窗口焦点所在的元素

    原文:[msdn wpf forum翻译]获取当前窗口焦点所在的元素 原文地址: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/6b ...

  2. 《Programming WPF》翻译 第3章 2.处理输入

    原文:<Programming WPF>翻译 第3章 2.处理输入 在Windows应用程序中,又3种基本的用户输入形式:鼠标.键盘和手写板.同时,还有一种更高级输入方式,其可能来自快捷键 ...

  3. 《Programming WPF》翻译 第7章 4.转换

    <Programming WPF>翻译 第7章 4.转换 原文:<Programming WPF>翻译 第7章 4.转换 支持高分辨率显示是WPF中的重要样式.这是被部分地支持 ...

  4. 《Programming WPF》翻译 第7章 3.笔刷和钢笔

    原文:<Programming WPF>翻译 第7章 3.笔刷和钢笔 为了在屏幕上绘制一个图形,WPF需要知道你想要为图形填充什么颜色以及如何绘制它的边框.WPF提供了一些Brush类型支 ...

  5. 《Programming WPF》翻译 目录

    原文:<Programming WPF>翻译 目录 注:第1.2章我只做了笔记,没有翻译,请大家阅读时注意. 还有就是,这本书的英文版本下载:[O'Reilly] Programming ...

  6. 《Programming WPF》翻译 第8章 2.Timeline

    <Programming WPF>翻译 第8章 2.Timeline 原文:<Programming WPF>翻译 第8章 2.Timeline Timeline代表了时间的延 ...

  7. WPF编程,通过KeyFrame 类型制作控件线性动画的一种方法。

    WPF编程,通过KeyFrame 类型制作控件线性动画的一种方法. 原文: WPF编程,通过KeyFrame 类型制作控件线性动画的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https ...

  8. 《Programming WPF》翻译 第8章 6.我们进行到哪里了?

    <Programming WPF>翻译 第8章 6.我们进行到哪里了? 原文:<Programming WPF>翻译 第8章 6.我们进行到哪里了? 动画可以增强应用程序的交互 ...

  9. WPF编程,TextBlock中的文字修饰线(上划线,中划线,基线与下划线)的使用方法。...

    WPF编程,TextBlock中的文字修饰线(上划线,中划线,基线与下划线)的使用方法. 原文:WPF编程,TextBlock中的文字修饰线(上划线,中划线,基线与下划线)的使用方法. 版权声明:我不 ...

最新文章

  1. C/C++版数据结构之链表三
  2. python3 出现错误:TypeError: must be str, not list
  3. 在Spring Boot中使用内存数据库
  4. 去掉WinLicense文件效验的方法
  5. SAP ERP里如何创建一个新的material类型
  6. Oracle数据库案例整理-Oracle系统执行时故障-Shared Pool内存不足导致数据库响应缓慢...
  7. Java方法中的参数太多,第1部分:自定义类型
  8. @清晰掉 C++ 中的 enum 结构在内存中是怎么存储的?
  9. git reflog
  10. oform java_客户端表单通用验证checkForm(oForm)(1)
  11. 推荐系统实践:基于数据集MovieLens构造简单推荐系统
  12. 怎么使用计算机英语段落,怎么在电脑word文档中添加英文朗读功能
  13. 视频编解码学习之四:视频处理及编码标准
  14. 荣耀MagicBook 2019 Intel版发布:性能新升级 续航长达15小时!
  15. 【unity】遇到Multiple precompiled assemblies with the same name的解决方案
  16. 给计算机主机吹灰,电脑主机多久清理一次灰合理一些?一点小建议给你
  17. pssh, pscp的用法
  18. Linux下访问处理器硬件信息原理:图形化工具RWLinux的诞生
  19. jdk版本8u201 8u202 区别
  20. 短信被微信困兽:运营商战或和

热门文章

  1. 3. pima-indians-diabetes.csv 数据文件
  2. ajax操作oracle,AJAX操作流程
  3. 看华为eLTE-IoT如何联接高效工业物联网
  4. 小雅深访 | 谢治宇最完整投资框架(2020年4月)
  5. 诺基亚被台湾之星电信选为5G网络供应商
  6. R语言如何绘制散点图和相关图,并且使散点颜色渐变
  7. 设计灵感|惊艳!酷炫十足!3D平面版式设计案例
  8. 3.2 埃尔米特转置
  9. 永磁同步电机相序测量中遇到的问题及解决
  10. div属性与span属性的区别