用户控件中的 ContentPresenter

10

我是WPF的新手,正在尝试创建一个包含一些嵌套内容的用户控件。

<my:InformationBox Header="General Information" Width="280">
    <StackPanel>
        <Label>Label1</Label>
        <Label>Label2</Label>
    </StackPanel>
</my:InformationBox>

如您所见,我想将一个StackPanel放入其中。根据我阅读的一些文章,我应该向我的UserControl添加ContentPresenter,因此我已经这样做了,但我找不到应该绑定到它的Content属性的内容。

以下是我的UserControl代码

<UserControl x:Class="ITMAN.InformationBox"
             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="200" d:DesignWidth="280" Name="infoBox" Loaded="infoBox_Loaded">
    <StackPanel Width="{Binding ElementName=infoBox, Path=Width}" HorizontalAlignment="Stretch">
        <Label Content="{Binding ElementName=infoBox, Path=Header}" />
        <Border BorderThickness="0,1,0,0" Padding="10 5" Margin="5 0 5 10" BorderBrush="#B4CEDE">
            <StackPanel>
                <ContentPresenter Content="{Binding Content}" />
                <Label Content="End" />
            </StackPanel>
        </Border>
    </StackPanel>
</UserControl>

我尝试了来自各种文章的许多组合,但是我找不到任何可行的示例来实现我想要的。

之前有另一个用户提出了类似的问题,但是给出的答案对我没有帮助:有没有人有一个简单的UserControl和单个ContentPresenter的示例?


1
据我所知,用户控件没有“Content”属性。另外,“ContentPresenter”通常只用于模板中。我认为你需要做的是创建一个带有依赖属性“Content”和默认模板的自定义控件,就像你使用用户控件一样设置控件。 - 也可能将DP添加到用户控件的类中;不过那时你必须使用“ContentControl”。 - poke
是的,您需要向您的用户控件添加依赖属性。请查看此链接:http://www.codeproject.com/Articles/224230/Exploring-the-use-of-Dependency-Properties-in-User。 - Klaus78
@poke 用户控件是 ContentControl 类的一种,因此 ContentPresenter 是可用的。 - dowhilefor
@dowhilefor 但它不是一个内容控件吗?因此,您可以使用内容构建自定义用户控件。从中创建的控件是否也是内容控件? - poke
1
@poke是一个从ContentControl派生的UserControl。xaml部分是它的ControlTemplate。因此,您可以构建不使用Content的UserControls,但如果要使用它,就需要使用ContentPresenter。 - dowhilefor
@poke 我必须道歉,看起来我错了。它不是你在XAML中定义的模板,而是它的内容。请参考user1537441的回答。这也是我根本不使用UserControls的原因之一。 - dowhilefor
3个回答

11

ContentPresenter是一种神奇的控件。如果你不向它提供任何内容,它将自动使用TemplateBinding到TemplatedParent设置ContentContentTemplateContentTemplateSelector属性。这意味着,你不需要向它提供任何内容,只需

<ContentPresenter/>

在您的UserControl中,应该自动使用在UserControl中找到的相应属性。还要记住,像{Binding Content}这样的绑定总是引用您的DataContext,我想这不是您想要的。


我一开始也考虑过这个,但它也不起作用。我会尝试将其制作成模板。 - krzychu
1
我试过了,它完美地运行了,也许你有一些其他的需求在你的问题中没有清楚表达出来。为了确保,你可以给你的XAML用户控件一个名称,比如x:Name="myControl",并将你的内容绑定更改为Content="{Binding ElementName=myControl, Path=Content}",看看是否有效。但是在我的测试用例中,一个空的ContentPresenter是有效的。 - dowhilefor
有趣的是,每次我写下 Content={Binding ElementName=infoBox, Path=Content} 时,我的 Visual Studio 2010 都会崩溃。 - krzychu
在一个非常简单的应用程序中尝试它。创建一个新的WPF项目,添加一个用户控件并在那里尝试它。也许你的应用程序还有其他问题。 - dowhilefor

11

我通过对GroupBox应用自定义样式来解决了这个问题。我创建了一个在ResourceDictionary中的样式,它如下所示:

<Style x:Key="InformationBoxStyle" TargetType="GroupBox">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="GroupBox">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>
                    <Label>
                        <ContentPresenter Margin="4" ContentSource="Header"
                               RecognizesAccessKey="True" />
                    </Label>
                    <Border Grid.Row="1" BorderThickness="0,1,0,0" Padding="10 5"
                               Margin="5 0 5 10" BorderBrush="#B4CEDE">
                        <StackPanel>
                            <ContentPresenter />
                        </StackPanel>
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

并将这个样式应用到 GroupBox

<GroupBox Header="General Information" Width="280" Style="{StaticResource InformationBoxStyle}">
    <StackPanel>
        <Label>Label1</Label>
        <Label>Label2</Label>
    </StackPanel>
</GroupBox>

这段代码按预期工作。

您还可以参考这篇优秀的文章,其中展示了不同的选项来实现它: http://www.codeproject.com/Articles/82464/How-to-Embed-Arbitrary-Content-in-a-WPF-Control 该文章还说明了为什么在我的代码中ContentPresenter无法工作。


那个CodeProject的链接非常好地解释了使用ContentControl与模板静态资源而不是样式的用法。 - John Leidegren

2

您需要在UserControl的代码后台创建一个依赖属性,例如InnerContent。

    public object InnerContent
    {
        get { return GetValue(InnerContentProperty); }
        set { SetValue(InnerContentProperty, value); }
    }

    public static readonly DependencyProperty InnerContentProperty =
        DependencyProperty.Register("InnerContent", typeof(object), typeof(ConfirmationControl), new PropertyMetadata(null));

然后您需要在UserControl的XAML代码中绑定到那个InnerContent。

 <ContentControl Content="{Binding InnerContent, ElementName=userControl}" />

那么当您使用它时,不要直接放置内容到UserControl中并覆盖现有内容,而是添加到InnerContent部分。

    <UserControls:InformationBox>
        <UserControls:InformationBox.InnerContent>
            <TextBlock Text="I'm in the InnerContent" />
        </UserControls:InformationBox.InnerContent>
    </UserControls:InformationBox>

否则,使用模板或样式同样有效,但如果您希望打包一个UserControl供使用,而不强制任何人引用样式或模板,那么这可能是您更好的选择之一。

我收到了这个错误信息:无法在元素“Button”上设置Name属性值“btMain”。 “Button”位于元素“MyCustomControl”的范围内,在另一个范围中定义时已经注册了名称。 - Luca Ziegler
@LucaZiegler 我认为这应该算作一个新的问题/问题,而不是评论。请在问题中发布它并添加代码,以便有人或我自己可以帮助您解决它。 - Michael Puckett II

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接