繁体   English   中英

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

[英]In a WPF control that acts as a container, how do I place the content?

我正在编写一个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。

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

编辑:

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

尝试替换此行:

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

有了这个

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

在现有的代码,你使这种ContentPresenter显示的生成内容EllipsesButtonControl ,其中包括ContentPresenter必须渲染生成的内容ElipsesButtonControl其中包括ContentPresenter .....无限递归。

您的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>
<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>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM