1、背景 框架
使用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. 其餘地方對該資源的引用: this
<StackPanel>
<Button Style=
"
{DynamicResource ButtonStyle}
"/>
<Button Style=
"
{DynamicResource ButtonStyle}
"/>
<Button Style=
"
{DynamicResource ButtonStyle}
"/>
</StackPanel>
該寫法會出現一種狀況,以下圖所示(除了最後一個按鈕能顯示圖片,而其餘按鈕倒是「空白」,這讓個人抑制不住個人好奇心,決定對該問題進行分析和思考: spa

2、代碼實現 :分別採用三種寫法來嘗試 3d
1. 採用原來Content的代碼;(代碼及圖請參考上面)code
2. 採用String來代替Content中的Image控件,具體代碼和結果以下: orm
<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>
結果出來了,這時能知足個人需求,可是個人好奇心更增強烈了,我很是想知道「爲何」?blog
3、分析
我對以上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駐留池的說法是正確的;
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++;
結果以下:

4、總結
WPF的樹上是不容許有同一個控件存在兩個不一樣的節點上;若是想要實現該功能,須要實例化兩個對象而後存放到WPF的樹上(包括邏輯樹或可視化樹);Silverlight也應該是同樣的道理;WPF中的ContentControl定義樣式時都不能採用Content來定義;
5、代碼下載
點擊下載代碼
點擊下載「邏輯樹與可視化樹工具」