在充当容器的WPF控件中,如何放置内容?

时间:2017-04-26 01:39:38

标签: c# wpf

我正在编写一个WPF控件,它以与BorderScrollViewer为容器相同的方式构成容器。它被称为EllipsisButtonControl,它应该在其内容的右侧放置一个省略号按钮。这是我打算如何使用它的一个例子:

<local:EllipsisButtonControl>
    <TextBlock Text="Testing" />
</local:EllipsisButtonControl>

以下是EllipsisButtonControl的XAML:

<ContentControl
    x:Class="WpfApplication1.EllipsisButtonControl"
    x:Name="ContentControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    mc:Ignorable="d"
    d:DesignHeight="30" d:DesignWidth="300">

    <Grid>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>

        <ContentPresenter Grid.Column="0" Content="{Binding ElementName=ContentControl, Path=Content}" />

        <Button Grid.Column="1" Command="{Binding  ElementName=ContentControl, Path=Command}" Margin="3,0" Width="30" Height="24" MaxHeight="24" VerticalAlignment="Stretch" Content="..." />

    </Grid>

</ContentControl>

这是背后的代码:

using System.Windows;
using System.Windows.Input;

namespace WpfApplication1
{
    public partial class EllipsisButtonControl
    {
        public EllipsisButtonControl()
        {
            InitializeComponent();
        }

        public static string GetCommand(DependencyObject obj)
        {
            return (string)obj.GetValue(CommandProperty);
        }

        public static void SetCommand(DependencyObject obj, string value)
        {
            obj.SetValue(CommandProperty, value);
        }

        public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
                name: "Command",
                propertyType: typeof(ICommand),
                ownerType: typeof(EllipsisButtonControl),
                defaultMetadata: new UIPropertyMetadata());
    }
}

这不起作用。它使用System.Runtime.Remoting.RemotingException来崩溃Designer。

我认为对ContentPresenter XAML的EllipsisButtonControl的绑定是错误的,但我不知道如何使它正确。使该行引用控件内容的适当语法是什么? (例如,使用示例中定义的TextBlock

修改

poke在下面提供了一个全面的答案(包括工作代码),但为了其他可能分享我最初误解的人的利益,让我总结一下这里的关键概念:容器控件本身不能“放置内容”。它通过定义修改调用XAML呈现内容的方式的模板来实现预期效果。其余的解决方案都是从这个前提出发的。

3 个答案:

答案 0 :(得分:1)

尝试替换此行:

<ContentPresenter Grid.Column="0" Content="{Binding ElementName=ContentControl, Path=Content}" />

有了这个

<ContentPresenter Grid.Column="0" Content={Binding Content} />

在现有代码中,您ContentPresenter显示生成的EllipsesButtonControl内容,其中包含ContentPresenterElipsesButtonControl必须呈现生成的ContentPresenter内容,其中包含{{1}} .....无限递归。

答案 1 :(得分:1)

您的EllipsisButtonControl的XAML已将其内容设置为顶级网格。你可能想要的是创建一个ControlTemplate,例如像这样:

<ContentControl x:Class="WpfApplication1.EllipsisButtonControl"
                x:Name="ContentControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                mc:Ignorable="d" d:DesignHeight="30" d:DesignWidth="300">
    <ContentControl.Template>
        <ControlTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>

                <ContentPresenter Grid.Column="0"
                    Content="{Binding ElementName=ContentControl, Path=Content}"/>

                <Button Grid.Column="1"
                    Command="{Binding ElementName=ContentControl, Path=Command}"
                    Margin="3,0" Width="30" Height="24" MaxHeight="24"
                    VerticalAlignment="Stretch" Content="..." />
            </Grid>
        </ControlTemplate>
    </ContentControl.Template>
</ContentControl>

答案 2 :(得分:1)

<local:EllipsisButtonControl>
    <TextBlock Text="Testing" />
</local:EllipsisButtonControl>

这会设置用户控件的Content。但是用户控件的XAML中的以下内容也是如此:

<ContentControl …>
    <Grid>
        …
    </Grid>
</ContentControl>

调用XAML在这里有优先权,所以无论你在用户控件的XAML中做什么都被忽略了。

此处的解决方案是设置用户控件的模板。模板(在本例中为控件的控件模板)确定控件本身的呈现方式。用户控件的最简单模板(以及它的默认模板)只是在那里使用ContentPresenter,但当然,你想要添加一些东西,所以我们必须覆盖模板。这通常看起来像这样:

<ContentControl …>
    <!-- We are setting the `Template` property -->
    <ContentControl.Template>
        <!-- The template value is of type `ControlTemplate` and we should
             also set the target type properly so binding paths can be resolved -->
        <ControlTemplate>

            <!-- This is where your control code actually goes -->

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

现在,这是您完成此项工作所需的框架。但是,一旦进入控件模板,就需要使用正确的绑定类型。由于我们正在编写模板并希望绑定到父控件的属性,因此我们需要将父控件指定为绑定中的相对源。但最简单的方法是使用TemplateBinding标记扩展名。使用它,可以在ContentPresenter上面ControlTemplate放置<ContentPresenter Content="{TemplateBinding Content}" />

Command

这应该是您需要的所有内容,以使内容演示者工作。

但是,现在您使用控件模板,当然还需要调整其他绑定。特别是绑定到自定义依赖项属性Content。这通常看起来与绑定到ContentControl的模板相同,但由于我们的控件模板定位类型ContentControl<Button Command="{TemplateBinding local:EllipsisButtonControl.Command}" … /> 没有自定义属性,因此我们需要显式引用您的自定义依赖项属性:

<ContentControl
        x:Class="WpfApplication1.EllipsisButtonControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:WpfApplication1"
        d:DesignHeight="30" d:DesignWidth="300" mc:Ignorable="d">
    <ContentControl.Template>
        <ControlTemplate TargetType="ContentControl">

            <Grid>
                <ContentPresenter Grid.Column="0"
                        Content="{TemplateBinding Content}" />

                <Button Grid.Column="1" Content="…"
                        Command="{TemplateBinding local:EllipsisButtonControl.Command}" />
            </Grid>

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

一旦我们拥有了,所有绑定都应该正常工作。 (如果你现在想知道:是的,绑定总是以类型上的静态依赖属性为目标)

因此,总而言之,您的自定义内容控件应如下所示:

SSLHandshakeException