来自:http://www.cnblogs.com/Clingingboy/archive/2010/12/20/1911388.html

 

Displays the content of a ContentControl

似乎其是为ContentControl定身量做的.

为了理解这一点,首先我们要对WPF内容模型有所了解,上面这篇文章有提到过ContentControl继承自Control,多了Content属性,继承自ContentControl的均可以称之为内容模型的控件.如下

image

这里似乎看不到ContentPresenter的影子.下面来举一些例子

一个ContentPresenter的例子

image

ContentPresenter可以直接在xaml中输出,其视觉树中包含一个TextBlock,在默认的WPF样式定义中找不到任何关于ContentPresenter的样式,说明了ContentPresenter并非是真正代码逻辑与样式分离的,而是在内部代码中提供了一个默认的模板即呈现了TextBlock,如果在内部创建模板的话,一般均会采用FrameworkElementFactory创建根元素,但这种方式太过于复杂,适用于一些简单默认操作,就如ContentPresenter内部的TextBlock.

那么问题出来了,为什么不直接用TextBlock呢,还要包装一个ContentPresenter?

ContentPresenter与TextBlock

如要回答上面的问题,那么就犹如来讨论两者的区别,我们来看下TextBlock

<TextBlock Text="Hello"/>

 

 

 

TextBlock是一个真正以文字为主题的元素,而ContentPresenter的功能就不只呈现文字了。

image

只不过默认是呈现文字而已,但两者概念完全不同,Content属性是object类型,而非string,可以自己以Content为数据源而重新定义模板(ContentTemplate),如下示例

image

这样的话ContentPresenter将不再局限于文字的呈现.

下面来看看ContentControl与ContentPresenter的关系

ContentControl与ContentPresenter

先看一个被重新定义的的Button样式

image

上面可以看到,使用ContentPresenter非常的方便,只要将ContentPresenter放在模板中即可,也不需要做任何的额外的绑定(难道不需要做吗?只不过ContentPresenter内部帮我们做了默认的绑定),但如果使用TextBlock呢?如下还是需要做绑定的

        <Style TargetType="Button">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <TextBlock Text="{TemplateBinding Content}"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

WPF中不同的Binding类型,在http://blog.csdn.net/xxchen2/article/details/4552554中看到下面的片段,得出结论//WPF中({Binding RelativeSource={RelativeSource TemplatedParnet}}”等价于“{TemplateBinding}”)?

 <Style TargetType="{x:Type local:TemplatedParent}">

        <Setter Property="Background" Value="Green"/>

        <Setter Property="Template">

            <Setter.Value>

                <ControlTemplate TargetType="{x:Type local:TemplatedParent}">

                    <Grid>

                        <Ellipse>

                            <Ellipse.Fill>

                                <SolidColorBrush Color="{Binding Path=Background.Color,RelativeSource={RelativeSource TemplatedParent}}"/>

                            </Ellipse.Fill>

                        </Ellipse>

                    </Grid>

                </ControlTemplate>

            </Setter.Value>

        </Setter>

    </Style>

 

在上例中是这样使用TemplatedParent的:

 

<SolidColorBrush Color="{Binding Path=Background.Color,RelativeSource={RelativeSource TemplatedParent}}"/>

 

 

 

 

如上看来我们还不如说ContentControl是ContentPresenter的一个特例,而ContentPresenter

ContentPresenter则是ContentControl的基础.为了适配ContentPresenter,ContentControl提供了内容模型的相关属性,本质上ContentPresenter并非仅仅只是用到ContentControl而已,ContentPresenter可以通过指定ContentSource来绑定指定的源属性

image

记住内容模型不仅仅只是呈现文字而已,如果只是为了呈现文字的话,是不需要ContentPresenter的

父子元素之间的关系(ItemsPresenter)

有时候控件并非维护本身逻辑,而是依赖于父子元素的,如了上诉的ContentPresenter,我们还有一个非常常用的ListBox控件,因为继承自ItemsControl,所以有一个ItemsPanel属性作为集合元素承载容器,但集合控件本身却不负责呈现控件,那么这个任务就留给了子元素ItemsPresenter,其实用也很简单,只要把ItemsPresenter放在内部模板中,那么ItemsPresenter则会去检测父元素是否为集合控件,然后将ItemsPanel添加到其内部视觉树当中

        <Style x:Key="{x:Type ItemsControl}"
           TargetType="{x:Type ItemsControl}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ItemsControl}">
                        <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            Padding="{TemplateBinding Padding}"
                            SnapsToDevicePixels="true">
                            <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

如下视觉树,StackPanel作为ItemsControl的默认容器

image

先到这里吧

posted on 2012-08-28 09:10  醉意人间  阅读(5955)  评论(0编辑  收藏  举报