1. 程式人生 > >正確理解WPF中的TemplatedParent

正確理解WPF中的TemplatedParent

ted 不存在 htm each fin sent 直接 .com dpa

http://www.cnblogs.com/mgen/archive/2011/08/31/2160581.html

(註:Logical Tree中文稱為邏輯樹,Visual Tree中文稱為可視化樹或者視覺樹,由於名稱不是很統一,文中統一用英文名稱代表兩個概念,況且VisualTreeHelper和LogicalTreeHelper也是WPF中提供的類名稱)

眾所周知WPF中的Logical Tree是邏輯上定義的元素層次樹,而實際上顯示在屏幕上的元素層次樹是Visual Tree,Visual Tree是Logical Tree節點擴充後的的產物。因此從Visual Tree的角度上看(Visual Tree當然是完整的一個),Logical Tree被分割成一段一段的,而這些段與段的連接點,就是和TemplatedParent有關。

這個概念在WPF類模型中是FrameworkElement.TemplatedParent屬性。WPF中的模板(數據模板和控件模板)都可以擴展Logical Tree,那麽模板所修飾的對象就是模板中元素的TemplatedParent,此時模板元素和修飾對象都會出現在Visual Tree中,但模板元素肯定不屬於被修飾元素的Logical Tree,但是模板有自己的Logical Tree,兩個Logical Tree是分開的,但是通過TemplatedParent,兩者之間又有聯系。

說再多不如實例形象,來看下面示例代碼:

這是一個簡單的ContentControl,它的Content是一個按鈕,然後定義了控件模板和數據模板,代碼中一些關鍵元素有Name屬性,我們在後續討論就以Name屬性的值來引用這些元素。

<ContentControl Name="contentControl">

<!-- 控件模板 -->

<ContentControl.Template>

<ControlTemplate TargetType="ContentControl">

<Border Name="bd1">

<ContentPresenter Name="cp1" ContentSource="Content"/>

</Border>

</ControlTemplate>

</ContentControl.Template>

<!-- 數據模板 -->

<ContentControl.ContentTemplate>

<DataTemplate>

<Border Name="bd2">

<ContentPresenter Name="cp2" Content="{Binding}" />

</Border>

</DataTemplate>

</ContentControl.ContentTemplate>

<!-- 邏輯孩子 -->

<Button Name="btn">按鈕</Button>

</ContentControl>

這個ContentControl的Visual Tree如下圖:

技術分享

圖中相同顏色的節點代表它們屬於同一個Logical Tree,可以看出來,整個Visual Tree分成多個Logical Tree,而這些Logical Tree是分開的,比如上面代碼中的兩個Border(名稱是bd1和bd2),它們的Parent屬性的值都是null,即沒有邏輯父節點。但是這些邏輯樹通過TemplatedParent是互相有聯系的。比如控件模板中的元素的TemplatedParent指代最上方的ContentControl,而數據模板元素的TemplatedParent則是控件模板內的ContentPresenter元素。

通過代碼也可以驗證這些:(bd1, bd2, cp1, cp2分別代表控件模板和數據模板中的Border和ContentPresenter)

private void Button_Click(object sender, RoutedEventArgs e)

{

var bd1 = (Border)contentControl.Template.FindName("bd1", contentControl);

var cp1 = (ContentPresenter)contentControl.Template.FindName("cp1", contentControl);

var bd2 = (Border)contentControl.ContentTemplate.FindName("bd2", cp1);

var cp2 = (ContentPresenter)contentControl.ContentTemplate.FindName("cp2", cp1);

PrintInfo(bd1, cp1, bd2, cp2, btn);

}

void PrintInfo(params FrameworkElement[] eles)

{

string s = "";

foreach (var ele in eles)

s += String.Format("{2}\r\nParent: {0}\r\nTemplatedParent: {1}\r\n\r\n", ele.Parent, ele.TemplatedParent, ele.Name);

MessageBox.Show(s);

}

輸出信息:(冒號後沒有值則代表null)

bd1

Parent:

TemplatedParent: System.Windows.Controls.ContentControl: 按鈕

cp1

Parent: System.Windows.Controls.Border

TemplatedParent: System.Windows.Controls.ContentControl: 按鈕

bd2

Parent:

TemplatedParent: System.Windows.Controls.ContentPresenter

cp2

Parent: System.Windows.Controls.Border

TemplatedParent: System.Windows.Controls.ContentPresenter

btn

Parent: System.Windows.Controls.ContentControl: 按鈕

TemplatedParent:

最後還有一個btn,指代ContentControl中的內容按鈕,它屬於主幹邏輯樹,因此Parent是ContentControl,同時它也不屬於任何模板,不存在修飾對象,因此TemplatedParent為null

另外WPF數據綁定Binding類還支持RelativeSource對象,這個RelativeSource類的Mode屬性有一個TemplatedParent值,這個值就是代表數據綁定會將數據源作為,同時WPF中的TemplateBinding標記擴展可以方便定義此類綁定,另外TemplateBinding的綁定模式是OneWay。

了解了TemplatedParent,使用TemplateBinding也就非常靈活了,一般情況下TemplateBinding使用在定義控件模板下,但是在數據模板中也可以使用,比如下面這個例子:

<ContentControl>

<Button>Content</Button>

<ContentControl.ContentTemplate>

<DataTemplate>

<ContentPresenter Content="{TemplateBinding Content}" />

</DataTemplate>

</ContentControl.ContentTemplate>

</ContentControl>

這個TemplateBinding的數據源在哪裏?答案就是ContentControl中默認控件模板裏的ContentPresenter,所以這裏數據模板內的ContentPresenter的Content直接綁定到控件模板中的ContentPresenter的Content屬性,當然這個僅僅為了做示例,實際上用Content=”{Binding}”也可以。

正確理解WPF中的TemplatedParent