【WPF】ContentControl Style定义与使用出现问题后 -- 引发的思考
一、背景
使用WPF的朋友,大家都很喜欢采用定义控件的公共样式,以便整个框架对该资源的使用,好处就是可以达到代码复用、系统风格统一等;
1. 定义资源
<Style TargetType="Button" x:Key="ButtonStyle">
<Setter Property="Content">
<Setter.Value>
<Image Source="Imgs\btn.jpg"/>
</Setter.Value>
</Setter>
<Setter Property="Width" Value="60"/>
</Style>
2. 其他地方对该资源的引用:
<StackPanel>
<Button Style="{DynamicResource ButtonStyle}"/>
<Button Style="{DynamicResource ButtonStyle}"/>
<Button Style="{DynamicResource ButtonStyle}"/>
</StackPanel>
该写法会出现一种情况,如下图所示(除了最后一个按钮能显示图片,而其他按钮却是“空白”,这让我的抑制不住我的好奇心,决定对该问题进行分析和思考:

二、代码实现 :分别采用三种写法来尝试
1. 采用原来Content的代码;(代码及图请参考上面)
2. 采用String来代替Content中的Image控件,具体代码和结果如下:
<Style TargetType="Button" x:Key="ButtonWithOutStringStyle">
<Setter Property="Content" Value="Button"/>
<Setter Property="Width" Value="60"/>
</Style>

3. 采用ContentTemplate来代替Content属性,具体代码和结果如下:
<Style TargetType="Button" x:Key="ButtonContentTemplateStyle">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="Imgs\btn.jpg"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Width" Value="60"/>
</Style>

结果出来了,这时能满足我的需求,但是我的好奇心更加强烈了,我非常想知道“为什么”?
三、分析
我对以上3种情况,出现不同的结果非常好奇,我很想知道背后的运作原理;根据第一种情况,只有一个Button有图片,所以我进行了以下大胆的假设:
1. 假设出现该情况的原因是因为引用该资源的所有按钮使用了相同的一个Image控件,违背了“一个控件不能同存在可视树上两个不同节点上”;
2. 为什么String却可以正常显示;
3. ContentTemplate是渲染(呈现时)再进行实例化,所以显示的每个Image控件是不同的控件;
我带着这3个疑问进行了一步步的验证:
1. 验证:引用了ButtonStyle后是否使用了同一个对象;
<StackPanel>
<Button x:Name="G_1_A" Style="{DynamicResource ButtonStyle}"/>
<Button x:Name="G_1_B" Style="{DynamicResource ButtonStyle}"/>
<Button x:Name="G_1_C" Style="{DynamicResource ButtonStyle}"/>
</StackPanel> Console.WriteLine(string.Format("{0}:\t{1}(HashCode)", G_1_A.Name, G_1_A.Content.GetHashCode()));
Console.WriteLine(string.Format("{0}:\t{1}(HashCode)", G_1_B.Name, G_1_B.Content.GetHashCode()));
Console.WriteLine(string.Format("{0}:\t{1}(HashCode)", G_1_C.Name, G_1_C.Content.GetHashCode()));
结果如下:

2. 疑问:为什么采用String却可以,为了验证该问题,用了以前我写的一个工具“逻辑树与可视化树工具”;

错误图(之前验证方法有误,误导了大家),以下进行纠正

正确图(进行了树控件的优化)
错误结论:以上3个Button的Content(String)的HashCode都不一样,所以他们能正常显示出来;我开始还以为String是同一个,因为String在编译的时候,如果值是相同时会放入到驻留池中(这个出乎我的意料)
正确结论:3个Button的Content(String)的HashCode是一样的,因为string重写了GetHashCode方法 ,需要进一步确认是否是相同对象;
验证是否是同个对象:
Console.WriteLine(string.Format("{0} is {1}:{2}", G_2_A.Name, G_2_B.Name, object.ReferenceEquals(G_2_A.Content, G_2_B.Content)));
输出结果:证明string是同个对象,验证了string驻留池的说法是正确的;
G_2_A is G_2_B:True
3. 疑问:ContentTemplate是渲染(呈现时)再进行实例化,所以显示的每个Image控件是不同的控件;
3.1 我需要对G_3_A Button的Content和ContentTemplate进行分析
var content = G_3_A.Content;
var template = G_3_A.ContentTemplate;
结果如下:

3.2 我得到了一个结论是:ContentTemplate很有可能在渲染的时候才对Button的Content进行实例化,接下来的难题就是如何证明该观点:
我需要验证ContentTemplate里面的Content是否是同一个对象,结果如下:
Console.WriteLine(string.Format("{0}:\t{1}(HashCode)", G_3_A.Name, G_3_A.ContentTemplate.LoadContent().GetHashCode()));
Console.WriteLine(string.Format("{0}:\t{1}(HashCode)", G_3_B.Name, G_3_B.ContentTemplate.LoadContent().GetHashCode()));
Console.WriteLine(string.Format("{0}:\t{1}(HashCode)", G_3_C.Name, G_3_C.ContentTemplate.LoadContent().GetHashCode()));
结果如下:

3.3 证明ContentTemplate里面是不同的对象;我写下了如下代码:
var style = this.FindResource("ButtonContentTemplateStyle") as Style;
var setter = style.Setters[0] as Setter;
var template = setter.Value as DataTemplate;
Console.WriteLine(string.Format("第{0}次加载ButtonContentTemplateStyle:\tContent的HashCode({1})", count, template.LoadContent().GetHashCode()));
count++;
结果如下:

四、总结
WPF的树上是不允许有同一个控件存在两个不同的节点上;如果想要实现该功能,需要实例化两个对象然后存放到WPF的树上(包括逻辑树或可视化树);Silverlight也应该是一样的道理;WPF中的ContentControl定义样式时都不能采用Content来定义;
五、代码下载
【WPF】ContentControl Style定义与使用出现问题后 -- 引发的思考的更多相关文章
- 【Asp.net之旅】--因自己定义控件注冊而引发的思考
前言 近期在开发远洋的SOA系统平台,开发使用的是.NET平台.对于Asp.net并不困难,但该系统的开发并非全然依靠Asp.net.而是自身封装好的框架.这套框架是远洋地产购买的微软的开发平台,项目 ...
- WPF 之 style文件的引用
总结一下WPF中Style样式的引用方法. 一.内联样式: 直接设置控件的Height.Width.Foreground.HorizontalAlignment.VerticalAlignment等属 ...
- WPF(ContentControl和ItemsControl)
WPF(ContentControl和ItemsControl) 2013-04-01 16:25 2188人阅读 评论(0) 收藏 举报 分类: .Net(C#)(31) WPF(25) 版权 ...
- WPF 样式(定义样式、引用样式、样式作用域、Trigger触发器)
1.定义 资源字典 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presenta ...
- WPF的Style的TargetType不同写法的异同
原文:WPF的Style的TargetType不同写法的异同 <Style TargetType="TextBlock"> <Setter Property=&q ...
- WPF中Style文件的引用——使用xaml代码或者C#代码动态加载
原文:WPF中Style文件的引用--使用xaml代码或者C#代码动态加载 WPF中控件拥有很多依赖属性(Dependency Property),我们可以通过编写自定义Style文件来控制控件的外观 ...
- WPF 中style文件的引用
原文:WPF 中style文件的引用 总结一下WPF中Style样式的引用方法: 一,内联样式: 直接设置控件的Height.Width.Foreground.HorizontalAlignment. ...
- Google C++ Style Guide在C++11普及后的变化
转 http://www.cnblogs.com/chen3feng/p/5972967.html?from=timeline&isappinstalled=0&lwfrom=user ...
- [WPF自定义控件库] 让Form在加载后自动获得焦点
原文:[WPF自定义控件库] 让Form在加载后自动获得焦点 1. 需求 加载后让第一个输入框或者焦点是个很基本的功能,典型的如"登录"对话框.一般来说"登录" ...
随机推荐
- Myeclipse的web项目移植到Eclipse中需要添加的包
3.jstl.jar 4.standard-1.1.2.jar 把Myeclipse的web项目一直到Eclipse当中需要添加的包主要有一下4个: 1.servlet-api.jar 2.jsp-a ...
- hihoCoder 1051补提交卡(贪心 枚举)
http://hihocoder.com/problemset/problem/1051 既然要选择最长连续提交天数,那么提交卡必须连续使用才有可能得到最优解,这样贪心,然后从头到尾扫一遍求出最大值. ...
- NDK(9)Application.mk各属性介绍
本文参考 : http://blog.csdn.net/grimraider/article/details/7587816 在NDK中编写的是本地程序,这个程序的源码在 jni 下,这个本地项目的配 ...
- XML Schema使用技巧——unique
XML Schema使用技巧——unique XML Scheam允许指定某个元素或属性的值在一定得范围内是唯一的.为了指定元素或属性值的唯一性,可以使用<xs:unqiue>元素,使 ...
- hdu1050(贪心)
囧 . 想了好久,一开始想的是一个连通图怎样用黑白两色染色,想了各种算法发现都不好做,然后灵机一动这不是网路流吗,然后想怎么建图,如果转换成网络流这题就好做了,建图加个二分应该就可以解决了,最后又发现 ...
- CSS处理溢出
如果内容超过的区块的大小,内容就会溢出,可以使用下面的方法控制溢出 overflow visible 不剪切内容也不添加滚动条 auto 在必需时对象内容才会被裁切或显示滚动条 hidden 不显示超 ...
- UVa 247 Calling Circles【传递闭包】
题意:给出n个人的m次电话,问最后构成多少个环,找出所有的环 自己想的是:用map来储存人名,每个人名映射成一个数字编号,再用并查集,求出有多少块连通块,输出 可是map不熟,写不出来,而且用并查集输 ...
- BZOJ 4690 Never Wait for Weights
带权并查集23333333 注意dis[x]+=dis[fath[x]. #include<iostream> #include<cstdio> #include<cst ...
- UESTC 1854
题目意思 就是说 有一个起点一个终点 和一些虫洞,从一个虫洞进去然后出来,是不需要消耗时间的,注意点就是,虫洞是一条线段,你可以从线段的任意位置进去,从任意位置出来: 所以从一个虫洞到另一个虫洞的 ...
- java 构造函数内部的多态方法 完全剖析
我们先来看一个例子,如果你读过<java编程思想>的话 应该会有印象 package com.test.zj; public class PolyConstructors { public ...