【WPF】ContentControl Style定义与使用出现问题后 -- 引发的思考
2013-12-12 00:38
656 查看
[b]一、背景 [/b]
使用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> 该写法会出现一种情况,如下图所示(除了最后一个按钮能显示图片,而其他按钮却是“空白”,这让我的抑制不住我的好奇心,决定对该问题进行分析和思考:
[b]二、代码实现 :[/b]分别采用三种写法来尝试
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>
结果出来了,这时能满足我的需求,但是我的好奇心更加强烈了,我非常想知道“为什么”?
[b]三、分析 [/b]
我对以上3种情况,出现不同的结果非常好奇,我很想知道背后的运作原理;根据第一种情况,只有一个Button有图片,所以我进行了以下大胆的假设:
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++; 结果如下:
[b]四、总结[/b]
WPF的树上是不允许有同一个控件存在两个不同的节点上;如果想要实现该功能,需要实例化两个对象然后存放到WPF的树上(包括逻辑树或可视化树);Silverlight也应该是一样的道理;WPF中的ContentControl定义样式时都不能采用Content来定义;
[b]五、代码下载[/b]
点击下载代码
点击下载“逻辑树与可视化树工具”
使用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> 该写法会出现一种情况,如下图所示(除了最后一个按钮能显示图片,而其他按钮却是“空白”,这让我的抑制不住我的好奇心,决定对该问题进行分析和思考:
[b]二、代码实现 :[/b]分别采用三种写法来尝试
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>
结果出来了,这时能满足我的需求,但是我的好奇心更加强烈了,我非常想知道“为什么”?
[b]三、分析 [/b]
我对以上3种情况,出现不同的结果非常好奇,我很想知道背后的运作原理;根据第一种情况,只有一个Button有图片,所以我进行了以下大胆的假设:
1. 假设出现该情况的原因是因为引用该资源的所有按钮使用了相同的一个Image控件,违背了“一个控件不能同存在可视树上两个不同节点上”;
2. 为什么String却可以正常显示;
3. ContentTemplate是渲染(呈现时)再进行实例化,所以显示的每个Image控件是不同的控件;
我带着这3个疑问进行了一步步的验证:2. 为什么String却可以正常显示;
3. ContentTemplate是渲染(呈现时)再进行实例化,所以显示的每个Image控件是不同的控件;
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++; 结果如下:
[b]四、总结[/b]
WPF的树上是不允许有同一个控件存在两个不同的节点上;如果想要实现该功能,需要实例化两个对象然后存放到WPF的树上(包括逻辑树或可视化树);Silverlight也应该是一样的道理;WPF中的ContentControl定义样式时都不能采用Content来定义;
[b]五、代码下载[/b]
点击下载代码
点击下载“逻辑树与可视化树工具”
相关文章推荐
- WPF Style-ControlTemplate 使用
- 关于“JAVA中为什么没有了多继承并出现了接口”这一问题引发的一些思考
- jmeter 压测时nginx,使用nginx -s reload出现tcp reset 问题的思考
- 【WPF】对Frame控件的Content属性做绑定时出现的一个小问题
- 解决使用Struts2的s:textfield标签出现Unable to find setter method for attribute: style的问题
- Wpf UserControl使用 KeyBinding,失效问题
- WPF 布局心得 一个像素引发的问题和思考
- 使用spring和jdk1.7开发web项目时出现的注解定义pointcut的问题
- WPF开发学习:资源在内外部定义的不同方式和怎么引用,Style样式的四种使用(对比CSS)
- 使用 ajax toolkit 中的 tabcontrol 时出现的一个小问题
- WPF下 使用它tabcontrol控件 时遇到的指定控件获得焦点的问题
- 使用额外资源定义的文件出现的问题
- WPF中的数据模板使用方式之一:ContentControl、ContentTemplate和TemplateSelector的使用
- 关于使用AjaxControlToolKit进度条的一些问题和思考
- WPF中的数据模板使用方式之一:ContentControl、ContentTemplate和TemplateSelector的使用
- 在使用ajaxcontroltoolkit时出现 'SYS' 未定义
- 使用WITH 定义的“临时表(视图)”,出现问题!
- [置顶] 微信充值取消出现闪屏问题引发的思考
- 使用Ajax.dll实现ajax功能时出现'某某'未定义的解决方案
- 由简单小问题引发的思考和推理