正確理解WPF中的TemplatedParent


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如下圖:

a_thumb5

 

圖中相同顏色的節點代表它們屬於同一個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}”也可以。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM