zoukankan      html  css  js  c++  java
  • [WPF]在Style中设置ToolTip的问题分析

    刚才开到智者千虑发的【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>

    To be the apostrophe which changed “Impossible” into “I’m possible”
    ----------------------------------------------------
    WinkingZhang's Blog (http://winkingzhang.cnblogs.com)
    GCDN(http://gcdn.grapecity.com/cs)
  • 相关阅读:
    程序自动更新版本
    [.NET] Rough Dependency Injection
    Python标准库存储对象(pickle包,cPickle包)
    发送邮件,支持群发
    css3传送带示例
    “计算机之子”的MVVM框架源码学习笔记
    Windows 8 应用商店正式面向全部开发者开放
    MVVM框架 v1发布
    Python学习索引
    注册 windows 8 开发者账号
  • 原文地址:https://www.cnblogs.com/winkingzhang/p/1376479.html
Copyright © 2011-2022 走看看