惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google Online Security Blog
Google Online Security Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
C
CERT Recently Published Vulnerability Notes
C
Cybersecurity and Infrastructure Security Agency CISA
Cisco Talos Blog
Cisco Talos Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
Scott Helme
Scott Helme
Project Zero
Project Zero
E
Exploit-DB.com RSS Feed
S
Secure Thoughts
K
Kaspersky official blog
L
Lohrmann on Cybersecurity
NISL@THU
NISL@THU
WordPress大学
WordPress大学
N
News and Events Feed by Topic
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LINUX DO - 热门话题
小众软件
小众软件
P
Privacy & Cybersecurity Law Blog
博客园 - 聂微东
Google DeepMind News
Google DeepMind News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
A
About on SuperTechFans
Hacker News: Ask HN
Hacker News: Ask HN
AWS News Blog
AWS News Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
H
Hacker News: Front Page
F
Full Disclosure
Latest news
Latest news
Schneier on Security
Schneier on Security
The Hacker News
The Hacker News
T
Troy Hunt's Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Jina AI
Jina AI
Martin Fowler
Martin Fowler
P
Proofpoint News Feed
TaoSecurity Blog
TaoSecurity Blog
G
GRAHAM CLULEY
Forbes - Security
Forbes - Security
V
V2EX - 技术
酷 壳 – CoolShell
酷 壳 – CoolShell
V
Vulnerabilities – Threatpost
C
Cyber Attacks, Cyber Crime and Cyber Security
MongoDB | Blog
MongoDB | Blog
博客园 - 三生石上(FineUI控件)
S
SegmentFault 最新的问题
Hugging Face - Blog
Hugging Face - Blog
P
Privacy International News Feed
C
Check Point Blog
N
News and Events Feed by Topic

博客园 - winkingzhang

Build 2016概览 WinRT开发系列之编程语言:功能和效率 WinRT开发系列之基础概念:WinRT不是…… VS2010 Extension实践(3)——实现自定义配置 [VS2010 Extension]PowerExtension.GoToDefinition VS2010 Extension实践(2) VS2010 Extension实践 如何通过反射调用带有ref或者out的参数的方法[迁移] Win7硬盘安装和移动硬盘访问出错的修复办法[迁移] zt. Windows Mobile开发文章收藏 [WPF]RadioButton在Group的Header区部分不响应鼠标选择的bug分析 WPF模式思考 (zt) How to Get IIS Web Sites Information Programmatically Visual Studio 调试器 Application Request Routing and the IIS 7.0 Web Management Service Reference Resources for MOSS and WSS FileSystemWatcher事件多次触发的解决方法 ASP.NET Application Life Cycle CAS and Native Code
[WPF]在Style中设置ToolTip的问题分析
winkingzhang · 2009-01-15 · via 博客园 - winkingzhang

刚才开到智者千虑发的【WPF】在Style中设置ToolTip的问题的博文,虽然最终给了一个暂时解决问题的方案,但是没有分析和解释其中的问题,正与他所说:但至于为什么不能直接在Setter.Value中放置TextBlock还是一个未解之谜。

趁着中午间隙,跟踪了一下,这里我将带给你完整的分析。

为了描述问题,首先,给出问题的xaml,当然,你也可以去智者千虑的blog查看详细描述。

<TextBlock x:Name="textBlockContainer" Text="ABC" Margin="10">
    
<!--如下的写法没有问题-->
    
<!--<ToolTipService.ToolTip>
        <TextBlock 
                        Text="// 通过绑定等方式从某地方获取文本"
                        TextWrapping="Wrap"
                        Width="70" />
    </ToolTipService.ToolTip>
--><!--使用Style为ToolTip赋值,出错!将会抛出exception-->
    
<TextBlock.Style>
        
<Style TargetType="TextBlock">
            
<Setter Property="ToolTipService.ToolTip">
                
<Setter.Value>
                    
<TextBlock x:Name="tooltipBlock"
                        Text
="// 通过绑定等方式从某地方获取文本"
                        TextWrapping
="Wrap"
                        Width
="70" />
                
</Setter.Value>
            
</Setter>
        
</Style>  
    
</TextBlock.Style>
</TextBlock>

其中异常的信息为:

Exception
System.Windows.Markup.XamlParseException occurred
  Message
="Cannot add content of type 'System.Windows.Controls.TextBlock' to an object of type 'System.Object'.  Error at object 'System.Windows.Controls.TextBlock' in markup file 'WpfApplication1;component/window1.xaml' Line 17 Position 30."
  Source
="PresentationFramework"
  LineNumber
=17
  LinePosition
=30
  NameContext
="Value"
  StackTrace:
       at System.Windows.Markup.XamlParseException.ThrowException(String message, Exception innerException, Int32 lineNumber, Int32 linePosition, Uri baseUri, XamlObjectIds currentXamlObjectIds, XamlObjectIds contextXamlObjectIds, Type objectType)
  InnerException: 

  从异常信息来看,似乎是要将TextBlock设置为某个类型为Object的对象的Content,而对于WPF程序来说,外面开到的Exception是重新throw出来的,其实内部应该有另外的exception,所以这里需要打开IDE的Exceptions设置(通常快捷键为Ctrl+D,E),选中CLR Exception。然后再次调试,这次可以看到在上面的Exception之前,IDE捕捉到了另一个Exception:

Exception

通过这个Exception信息,可以知道是在BamlRecordReader的SetPropertyValueToParent方法里出现了问题,这里在尝试转换某个object对象成MemberInfo。那么这个SetPropertyToParent在做什么呢?从方法名称来看,是为当前节点的父节点的某个属性设值;那这里为什么又要把对象cast成MemberInfo呢?

要回答这些问题,我们得要去看看SetPropertyToParent的实现,在没有Microsoft Symbol Server的时候,最好的方式就是Reflector;当然如果能使用Symbol Server,那就可以直接设置断点,进入调试了。在我分析的时候,我是使用Reflector查看代码的,这个方法很长,但是里面总共只有5处会扔出exception,从每个exception的key来看这一段嫌疑很大:

System.Windows.Markup.BamlRecordReader。SetPropertyValueToParent

由此结合Exception的描述分析,肯定是TextBlock在xaml的解析时,它的父节点是Object,这样,问题又来了,为什么呢?于是我们不得不回到我们写的xaml上,显然外面的TextBlock(名称为textBlockContainer)的肯定不会出问题,因为我们注释掉Style程序就正常了,问题肯定在Style。

我想学习过WPF之后都会知道XAML在解析过程是自顶向下,有外向内的解析的,在解析这个Style的时候,首先会创建一个Style对象,然后添加Setter,于是就解析到Setter了,也就要创建Setter对象,并为Setter对象的Property属性赋值为"ToolTipService.ToolTip";下面就解析到Setter的Value属性了,此时解析器需要创建对象TextBlock(名称为toolTipBlock),创建好了以后就把它设置到到父上的某个属性,通常是ContentProperty,如果没有就按照上面代码的顺序搜索,直到什么都没找到,扔个exception通知一下。这里TextBlock在Xaml中的父是谁?从XAML可以看到是“Setter.Value”,而这个Setter.Value在没有赋值的时候,取它返回的是一个DependencyProperty.UnsetValue,就是一个Object,显然,不可能为Object添加子,于是WPF系统认为异常。

结论:

至此,我们终于找到了问题根源,那就是在WPF的XAML节点的处理方式是实例化当前节点,然后将其赋值到它的父节点的某个属性,如果此时父节点是一个Object类型的属性时,就会出现exception。

解决方案

知道了为什么,下一步就会想到该如何解决。当然,智者千虑提供的方法是可行的,代码如下,这样就可以避过为TextBlock的父,即Setter.Value赋值了。

Code
<TextBlock x:Name="textBlockContainer" Text="ABC" Margin="10">
    
<TextBlock.Resources>
        
<TextBlock x:Key="toolTipBlock"
                   Text
="// 通过绑定等方式从某地方获取文本"
                   TextWrapping
="Wrap"
                   Width
="70" />
    
</TextBlock.Resources>
    
<TextBlock.Style>
        
<Style TargetType="TextBlock">
            
<Setter
                
Property="ToolTipService.ToolTip"
                Value
="{StaticResource toolTipBlock}"/>
        
</Style>  
    
</TextBlock.Style>
</TextBlock>