原文:【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 #endif11                 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 at19                 // 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 reach29                 // 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 = contentPresenter37                 // 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.OnVisualParentChanged45                 // 1.3.1 Set accessText.ShouldLookupImplicitStyle46                 // 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 acceptable52                 // 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 #endif64         }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翻译】TextBox中文本 中对齐 的方法

    原文:[msdn wpf forum翻译]TextBox中文本 中对齐 的方法 原文链接:http://social.msdn.microsoft.com/Forums/en-US/wpf/threa ...

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

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

  3. 【msdn wpf forum翻译】如何在wpf程序(程序激活时)中捕获所有的键盘输入,而不管哪个元素获得焦点?

    原文:[msdn wpf forum翻译]如何在wpf程序(程序激活时)中捕获所有的键盘输入,而不管哪个元素获得焦点? 原文链接:http://social.msdn.microsoft.com/Fo ...

  4. 《Programming WPF》翻译 第9章 5.默认可视化

    原文:<Programming WPF>翻译 第9章 5.默认可视化 虽然为控件提供一个自定义外观的能力是有用的,开发者应该能够使用一个控件而不用必须提供自定义可视化.这个控件应该正好工作 ...

  5. 《Programming WPF》翻译 第5章 4.元素类型样式

    原文:<Programming WPF>翻译 第5章 4.元素类型样式 命名样式非常有用,当你得到一组属性并应用到特点的元素上.然而,如果你想要应用一个统一的样式到所有确定元素类型的实例, ...

  6. 《Programming WPF》翻译 第9章 2.选择一个基类

    原文:<Programming WPF>翻译 第9章 2.选择一个基类 WPF提供了很多类,当创建一个自定义元素时,你可以从这些类中派生.图9-1显示了一组可能作为类--可能是合适的基类, ...

  7. 《Programming WPF》翻译 第8章 3.Storyboard

    原文:<Programming WPF>翻译 第8章 3.Storyboard Storyboard是动画的集合.如果你使用了标记,所有的动画必须要被定义在一个Storyboard中.(在 ...

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

    原文:<Programming WPF>翻译 第7章 4.转换 支持高分辨率显示是WPF中的重要样式.这是被部分地支持--强调了可伸缩的向量图,而不是图像.但是,正如使用GDI+和GDI3 ...

  9. 《Programming WPF》翻译 第7章 1.图形基础

    原文:<Programming WPF>翻译 第7章 1.图形基础 WPF使得在你的应用程序中使用图形很容易,以及更容易开发你的显卡的能力.这有很多图形构架的方面来达到这个目标.其中最重要 ...

随机推荐

  1. 【codeforces 742C】Arpa's loud Owf and Mehrdad's evil plan

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  2. Swift基础1.1——基本的语法—变量和常量

    前些日子.第一届Swift开发人员大会开了之后.身边非常多搞OC的朋友就按捺不住了. 都认为是时候学一下Swift了,毕竟Swift已是趋势. 也是应他们再三要求,让我整理一下Swift的学习心得.今 ...

  3. jquery中的this与$(this)的区别总结(this:html元素)($(this):JQuery对象)

    jquery中的this与$(this)的区别总结(this:html元素)($(this):JQuery对象) 一.总结 1.this所指的是html 元素,有html的属性,可用 this.属性  ...

  4. Linux网络编程——原始套接字实例:MAC 头部报文分析

    通过<Linux网络编程——原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 M ...

  5. ios UI控件的简单整理

    把该文件拷贝到.m文件中就能够方便的查找 /** 匿名类目:能够声明方法和变量,属性为private(不同意在外部调用,且不能被继承 */ /** 发送数据的托付方,接收数据的时代理发(即代理的反向传 ...

  6. Android View框架的measure机制

    概述 Android中View框架的工作机制中,主要有三个过程: 1.View树的測量(measure)Android View框架的measure机制 2.View树的布局(layout) Andr ...

  7. 子查询及exists

    子查询分为无关子查询和相关子查询 无关子查询,只查询一次,与外查询无关,可作为查询条件: select * from student where sno in (select sno from stu ...

  8. python 爬取36kr 7x24h快讯

    url为https://36kr.com/newsflashes,抓包后发现第一次的新闻内容就是包含在<script>var props={}></script>标签中, ...

  9. Windows完成端口与Linux epoll技术简介(能看懂)

    WINDOWS完成端口编程1.基本概念2.WINDOWS完成端口的特点3.完成端口(Completion Ports )相关数据结构和创建4.完成端口线程的工作原理5.Windows完成端口的实例代码 ...

  10. 一起学Python:列表介绍

    列表介绍 想一想: 前面学习的字符串可以用来存储一串信息,那么想一想,怎样存储咱们班所有同学的名字呢? 定义100个变量,每个变量存放一个学生的姓名可行吗?有更好的办法吗? 答: 列表 <1&g ...