如何创建包含占位符供以后使用的 WPF 用户控件

How to create WPF usercontrol which contains placeholders for later usage

提问人:greenoldman 提问时间:4/23/2011 最后编辑:greenoldman 更新时间:12/29/2022 访问量:36345

问:

我最好举例问这个问题。假设我有使用此控件的 UserControl 和 Window。

我想以这种方式设计这个控件(名为 MyControl)(这是科幻语法!

<Grid>
  <Button>Just a button</Button>
  <PlaceHolder Name="place_holder/>
</Grid> 

并在设计我的窗口时以这种方式使用:

<MyControl/>

<MyControl>
  <place_holder>
    <Button>Button 1</Button>
  </place_holder>
</MyControl> 

<MyControl>
  <place_holder>
    <Button>Button 1</Button>
    <Button>Button 2</Button>
  </place_holder>
</MyControl> 

当然,我希望能够在 Windows 中向 MyControl 添加更多元素。因此,在某种程度上,它应该作为容器工作(如 Grid、StackPanel 等)。位置将在 UserControl 中定义(在此示例中,在按钮“只是一个按钮”之后),但要添加的内容(哪些元素)将在 Window(其中使用 UserControl -- MyControl)中定义。

我希望这很清楚我想要实现的目标。关键点是在设计 Window 时使用 XAML,所以我的类应该不比其他控件差。

现在,最大的问题是——如何去做?

备注:样式超出范围。我所要做的就是在设计 Window 时(而不是在设计 MyControl 时)将我想要的任何控件添加到 MyControl。

WPF 用户控件 容器 占位符

评论


答:

23赞 Scott 4/23/2011 #1

我认为您希望使用位于内部的 ContentPresenter 来设置 UserControl 的 ControlTemplate(以便您可以定义内容的呈现位置)。

您的自定义 UserControl:

<UserControl x:Class="TestApp11.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Template>
        <ControlTemplate>
            <StackPanel>
                <TextBlock Text="Custom Control Text Area 1" />
                <ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
                <TextBlock Text="Custom Control Text Area 2" />
            </StackPanel>
        </ControlTemplate>
    </UserControl.Template>
</UserControl>

用法:

<Window x:Class="TestApp11.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:l="clr-namespace:TestApp11"
    Title="Window1" Height="250" Width="200">
    <StackPanel>
        <l:UserControl1>
            <Button Content="My Control's Content" />
        </l:UserControl1>
    </StackPanel>
</Window>

enter image description here

如果你的内容部分需要多个项目,只需将它们放在一个容器中,如网格或堆栈面板:

<l:UserControl1>
    <StackPanel>
        <Button Content="Button 1" />
        <Button Content="Button 2" />
    </StackPanel>
</l:UserControl1>

评论

0赞 greenoldman 4/23/2011
嗯,谢谢你的帮助和链接,但这篇文章正在做一些我已经知道的事情——它定义了一些用户控件,然后使用它们。就是这样 -- 用户控件不充当容器,请注意,它们的内容是在控件级别定义的,而不是在窗口级别定义的 -- 这就是我问题的重点。
0赞 Scott 4/23/2011
@macias,该链接只包含我所了解的几个概念......但没有按照我的想法把它放在一起。我附上了我的意思的样本。
0赞 greenoldman 4/23/2011
谢谢你的澄清,我对WPF不太熟悉,无法通过样式“雾”来查看容器;-)我将 H.B. 回复标记为答案,因为尽管您的解决方案是无代码的,但我已经设计了用户控件,并且 H.B. 方法需要对现有代码进行较少的更改。尽管如此,非常感谢!
0赞 Scott 4/23/2011
@macias......不用担心...只要您找到适合您需求的解决方案,这才是最重要的!
3赞 Sriwantha Attanayake 9/10/2012
存在一个问题,无论您在用户控件中放置的内容(例如 <l:UserControl1> x </l:UserControl>都不能有命名内容。如何添加带有命名元素的内容?编译错误为“无法在元素 'x' 上设置名称属性值 x。x 位于元素 y 的作用域内,该元素在另一个作用域中定义时已注册了名称。
50赞 brunnerh 4/23/2011 #2

ContentControls 和 ItemsControls 适用于此目的,您可以将它们绑定到 UserControl 的属性或公开它们。

使用 ContentControl(用于多个断开连接位置的占位符):

<UserControl x:Class="Test.UserControls.MyUserControl2"
             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" 
             Name="control">
    <Grid>
        <Button>Just a button</Button>
        <ContentControl Content="{Binding PlaceHolder1, ElementName=control}"/>
    </Grid>
</UserControl>
public partial class MyUserControl2 : UserControl
{
    public static readonly DependencyProperty PlaceHolder1Property =
        DependencyProperty.Register("PlaceHolder1", typeof(object), typeof(MyUserControl2), new UIPropertyMetadata(null));
    public object PlaceHolder1
    {
        get { return (object)GetValue(PlaceHolder1Property); }
        set { SetValue(PlaceHolder1Property, value); }
    }

    public MyUserControl2()
    {
        InitializeComponent();
    }
}
<uc:MyUserControl2>
    <uc:MyUserControl2.PlaceHolder1>
        <TextBlock Text="Test"/>
    </uc:MyUserControl2.PlaceHolder1>
</uc:MyUserControl2>

ItemsControl-Version(用于一个位置的集合)

<UserControl x:Class="Test.UserControls.MyUserControl2"
             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"
             Name="control">
    <Grid>
        <Button>Just a button</Button>
        <ItemsControl Name="_itemsControl" ItemsSource="{Binding ItemsSource, ElementName=control}"/>
    </Grid>
</UserControl>
[ContentProperty("Items")]
public partial class MyUserControl2 : UserControl
{
    public static readonly DependencyProperty ItemsSourceProperty = 
        ItemsControl.ItemsSourceProperty.AddOwner(typeof(MyUserControl2));
    public IEnumerable ItemsSource
    {
        get { return (IEnumerable)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }

    public ItemCollection Items
    {
        get { return _itemsControl.Items; }
    }

    public MyUserControl2()
    {
        InitializeComponent();
    }
}
<uc:MyUserControl2>
    <TextBlock Text="Test"/>
    <TextBlock Text="Test"/>
</uc:MyUserControl2>

使用 UserControls,您可以决定公开内部控件的某些属性;此外,可能还想公开像 这样的属性,但这完全取决于你想如何使用它,如果你只是设置 那么你不一定需要任何这些。ItemsSourceItemsControl.ItemTemplateItems

评论

3赞 Thomas Levesque 4/23/2011
DataContext="{Binding RelativeSource={RelativeSource Self}}不好,因为它会阻止绑定到实际数据。UserControl 将无法从其父级继承 DataContext,如果在使用控件时显式定义它,则对 PlaceHolder1 的绑定将不起作用。需要改用 RelativeSource 或 ElementName 绑定。
0赞 greenoldman 4/23/2011
谢谢。在使用 DataContext 修复后,它就像一个魅力。
0赞 brunnerh 4/23/2011
我只是倾向于这样设置 DataContext,因为在 Windows 中这并不重要,因为它们无论如何都没有父级。
1赞 greenoldman 4/23/2011
@H.B.,我也是(在 Windows 中),但这里确实最好不要。顺便说一句,奇怪的结果是,使用这种方法,您不能在没有特殊代码的情况下命名嵌套控件(在您的示例中为 TextBlock)。参见:stackoverflow.com/questions/751325/...当然没有抱怨,只是说。也许将来有人会从这里收集的所有智慧中受益:-)
0赞 brunnerh 4/23/2011
哦,是的,我知道这一点,这可能确实有点问题。