TemplatedParent、TemplateBinding、ContentPresenter使用说明

1.TemplatedParent:获取一个对此元素的模板父级的引用。 如果此元素不是通过模板创建而成,则此属性并不相关。

啥意思呢?

说再多不如实例形象,来看下面示例代码:

这是一个简单的ContentControl,它的Content是一个按钮,然后定义了控件模板和数据模板,代码中一些关键元素有Name属性,我们在后续讨论就以Name属性的值来引用这些元素。

<Window x:Class="Windows.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
  
        <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" Click="Button_Click">按钮</Button>
        </ContentControl>
  
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace Windows
{
    /// <summary>
    /// Window1.xaml 的交互逻辑
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        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);


        }
    }
}

运行结果:

 

 

 结论:(1)控件模板中元素的TemplatedParent 就是这个控件,就像b1的templatedParent就是ContentControl ;

(2)数据模板中元素的TemplatedParent就是ContentPresenter,b2、cp2的templatedParent都是ContentPresenter;

2.TenplateBinding:
 <Border Background="{TemplateBinding Background}"></Border>
 <Border  Background="{Binding Path=Background,Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
上面两种写法效果是一样的,分析下面的xaml代码,Binding形式写法的数据源就是Border元素的TemplatedParent,也就是Button
 <Button Content="btn1"  Height="30" >
            <Button.Template>
                <ControlTemplate>
                    <Border  Background="{Binding Path=Background,Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" >
                        <ContentPresenter></ContentPresenter>
                    </Border>
                </ControlTemplate>
            </Button.Template>
        </Button>
3.ContentPresenter:转载自https://www.cnblogs.com/YangMark/p/3172725.html

我們先由下面來看類層次,可知ContentControl繼承Control,ContentPresenter繼承FrameworkElement(Control也繼承FrameworkElement);


同樣的,ItemsControl繼承Control,ItemsPresenter繼承FrameworkElement.


ScreenClip


在Control類並沒有Content屬性, 所以在這之上再寫了一個ContentControl, 使控件有Content屬性可以顯示內容, 而


ContentPresenter就是負責將Content屬性顯示出來.


 


接著來我們看一下實例,


 


實例1:不使用ContentPresenter


使用ContentPresenter


            <ContentControl Content="YangMark">
                <ContentControl.Template>
                    <ControlTemplate TargetType="ContentControl">
                        <ContentPresenter></ContentPresenter>
                    </ControlTemplate>
                </ContentControl.Template>
            </ContentControl>


輸出結果: YangMark

正確顯示Content!!

 

不使用ContentPresenter

            <ContentControl Content="YangMark">
                <ContentControl.Template>
                    <ControlTemplate TargetType="ContentControl">

                    </ControlTemplate>
                </ContentControl.Template>
            </ContentControl>

 

輸出結果:      

無法顯示出Content!!

 

結論1:ContentPresenter通常出現在ControlTemplate內,且若不使用ContentPresenter則
Content屬性就無法正常顯示。

 

 

實例2:ContentPresenter中的ContentSource屬性

為什麼只為了顯示出Content屬性要大費周張弄出ContentPresenter呢??

我們可以先比較以下兩種代碼不同之類,

            <ContentControl Content="YangMark" ContentStringFormat="Hello!! {0}">
                <ContentControl.Template>
                    <ControlTemplate TargetType="ContentControl">
                        <ContentPresenter ContentSource="Content"></ContentPresenter>
                    </ControlTemplate>
                </ContentControl.Template>
            </ContentControl>


輸出結果:Hello!! YangMark

 

            <ContentControl Content="YangMark" ContentStringFormat="Hello!! {0}">
                <ContentControl.Template>
                    <ControlTemplate TargetType="ContentControl">
                        <ContentPresenter Content="{TemplateBinding Content}"></ContentPresenter>
                    </ControlTemplate>
                </ContentControl.Template>
            </ContentControl>

 


輸出結果:YangMark

僅出現Content屬性的內容!!

 

結論2:<ContentPresenter/>與<ContentPresenter ContentSource="Content"/> 意義上是相同的。

它們同時綁定了Content, ContentStringFormat, ContentTemplate和ContentTemplateSelector等內容

屬性。


若僅用Content="{TemplateBinding Content}"代表只綁定Content屬性而已,且ContentSource會失效。

 

 

實例3:ContentSource的應用


 以HeaderContentControl為例,使用ContentPresenter綁定內容屬性。

            <HeaderedContentControl Header="Header" HeaderStringFormat="I'm {0}"
                                    Content="Content" ContentStringFormat="I'm {0}">
                <HeaderedContentControl.Template>
                    <ControlTemplate TargetType="HeaderedContentControl">
                        <DockPanel>
                            
                            <ContentPresenter ContentSource="Header" DockPanel.Dock="Top"></ContentPresenter>
                            
                            <!--等同於<ContentPresenter ContentSource="Content"/>-->
                            <ContentPresenter></ContentPresenter>
                            
                        </DockPanel>
                    </ControlTemplate>
                </HeaderedContentControl.Template>
            </HeaderedContentControl>


輸出結果:


I'm Header


I’m Content


 


結論3:ContentSource若指定對象為Content是可以省略的,若不為Content(如:Header)則不能省略。


 


 


總結:


Content, ContentStringFormat, ContentTemplate和ContentTemplateSelector等屬性, 我將它們稱為內容屬性.


1. ContentPresenter的作用就是用來顯示內容屬性


2.ContentSource若指定對象為Content,則等同於<ContentPresenter/>; 若指定對象不為Content,


   則必須使用ContentSource聲明指定的對象.


 

 
posted @ 2020-07-28 16:35  _MrZhu  阅读(660)  评论(0)    收藏  举报