如何创建包含占位符以供以后使用的 WPF 用户控件
Posted
技术标签:
【中文标题】如何创建包含占位符以供以后使用的 WPF 用户控件【英文标题】:How to create WPF usercontrol which contains placeholders for later usage 【发布时间】:2011-08-11 03:05:52 【问题描述】:我最好通过例子来提问。假设我有使用此控件的 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>
当然,我希望能够在 Window 中向 MyControl 添加更多元素。因此,在某种程度上,它应该作为容器工作(如 Grid、StackPanel 等)。放置将在 UserControl 中定义(在此示例中,在按钮“只是一个按钮”之后),但要添加的内容(哪些元素)将在 Window 中定义(使用 UserControl -- MyControl --)。
我希望这很清楚我想要实现的目标。重点是在设计Window的时候使用XAML,所以我的类应该不会比其他控件差。
现在,最大的问题是——怎么做?
备注:样式超出范围。我要做的就是在设计 Window 时(而不是在设计 MyControl 时)将我想要的任何控件添加到 MyControl。
【问题讨论】:
【参考方案1】: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,您可以决定公开内部控件的某些属性;除了ItemsSource
之外,可能还想公开ItemsControl.ItemTemplate
之类的属性,但这完全取决于您想如何使用它,如果您只是设置Items
,那么您不一定需要任何这些。
【讨论】:
DataContext="Binding RelativeSource=RelativeSource Self
是坏,因为它会阻止绑定到实际数据。 UserControl 将无法从其父级继承 DataContext,如果您在使用控件时显式定义它,则 PlaceHolder1 上的绑定将不起作用。您需要改用 RelativeSource 或 ElementName 绑定。
非常感谢。使用 DataContext 修复后,它就像一个魅力。
我只是倾向于这样设置 DataContext,因为在 Windows 中这并不重要,因为它们没有父级。
@H.B.,我也是(在 Windows 中),但在这里确实最好不要。顺便提一句。奇怪的结果,使用这种方法,如果没有特殊的代码圈,您无法命名嵌套控件(示例中为 TextBlock)。见:***.com/questions/751325/… 当然没有抱怨,只是说。也许将来有人会从这里收集的所有智慧中受益:-)
哦,是的,我知道,这确实可能有点问题。【参考方案2】:
我认为您希望使用位于内部的 ContentPresenter 来设置 UserControl 的 ControlTemplate(这样您就可以定义内容的呈现位置)。
您的自定义用户控件:
<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>
如果您的内容部分需要多个项目,只需将它们放在网格或堆栈面板等容器中:
<l:UserControl1>
<StackPanel>
<Button Content="Button 1" />
<Button Content="Button 2" />
</StackPanel>
</l:UserControl1>
【讨论】:
嗯,感谢您的帮助和链接,但这篇文章正在做一些我已经知道的事情——它定义了一些用户控件,然后使用它们。就是这样——用户控件不充当容器,请注意它们的内容是在控件级别定义的,而不是窗口——这就是我的问题的重点。 @macias,该链接仅包含我所了解的几个概念......但并没有按照我的想法将它们放在一起。我附上了我的意思的示例。 感谢您的澄清,我对 WPF 不太熟悉通过样式“雾”来查看容器 ;-) 我正在标记 H.B.回复为答案,因为尽管您的解决方案是无代码的,但我已经设计了用户控件,并且 H.B.方法需要对现有代码进行较少的更改。不过还是非常感谢! @macias...不用担心...只要您找到了适合您需求的解决方案,这才是最重要的! 有一个问题,无论您在用户控件中放置什么内容,例如以上是关于如何创建包含占位符以供以后使用的 WPF 用户控件的主要内容,如果未能解决你的问题,请参考以下文章