创建一个UserControl
,它应该能够包含WPF中可用的任何可视子项,子项显示在UserControl
的子容器中。
我无法让我的容器中的孩子正确显示,我尝试了serval方式,并没有找到适合设计师的方法。我也尝试使用ContentControl
,但没有显示任何内容。
首先我找到this链接,我尝试了一些变化。我设法在正确的容器中显示内容,但它在设计器中不起作用,因为content-property是set-private而设计者想要覆盖它。将所有内容放在XAML中都有效,但在与设计师合作时这并不好。这可能是最喜欢的方式。
在此之后,我试图通过将ContentControl
- 属性绑定到Content
- 类型的可绑定属性来使用UIElementCollection
。这种方法不会给设计师带来任何错误,但我不得不承认我的容器中从未见过任何控制(例如Button
)。它保持空白但是已经添加了孩子。
在寻找简单快捷的解决方案后,我决定在这里寻求解决方案。我有点失望。如果微软可以将样本带入MSDN,那将非常有用。
我确信必须有一种简单的方法来存档。
感谢 Andrei Gavrila 和 jberger 我已归档创建一个显示内容的节点(请参阅下面的代码),但仍有两个问题: - 没有设计师支持 - 边框(参见xaml)未在设计器中显示,并且在应用程序运行时未显示,甚至没有边距
public class NodeContent : ContentControl
{
static NodeContent()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NodeContent), new FrameworkPropertyMetadata(typeof(NodeContent)));
}
}
public partial class Node : UserControl, INotifyPropertyChanged
{
UIElementCollection _Elements;
public event PropertyChangedEventHandler PropertyChanged;
public UIElementCollection NodeContent
{
get { return _Elements; }
set
{
_Elements = value;
OnPropertyChanged("NodeContent");
}
}
public Node()
{
InitializeComponent();
NodeContent = new UIElementCollection(NodeContentContainer, this);
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
节点的Xaml:
<UserControl x:Class="Pipedream.Nodes.Node"
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="216" d:DesignWidth="174" Background="Transparent" Name="NodeControl" xmlns:my="clr-namespace:Pipedream.Nodes">
<Border BorderThickness="1" CornerRadius="20" BorderBrush="Black" Background="White">
<Grid>
<my:NodeContent x:Name="NodeContentContainer" Margin="20" Content="{Binding Source=NodeControl, Path=NodeContent}" />
</Grid>
</Border>
</UserControl>
通用-的Xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Pipedream.Nodes">
<Style TargetType="{x:Type local:NodeContent}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Node}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
答案 0 :(得分:42)
通常,您无法绑定类型UIElementCollection
的依赖项属性。尝试这样的事情:
这里没什么可看的。 StackPanel将保存我们的子元素。你显然可以做得更多。
代码:
<UserControl x:Class="Demo.MultiChildDemo"
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:demo="clr-namespace:Demo"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel x:Name="PART_Host" />
</UserControl>
重要提示:
ContentPropertyAttribute
属性设置将由此类型的父元素包围的任何元素设置的属性。因此,<MultiChildDemo></MultiChildDemo>
中的所有元素都将添加到Children
属性中。UserControl
。这不需要完全自定义控制。DependencyProperty.Register()
和变体创建属性是一种很好的做法。您会注意到Children
属性没有后备变量:DependencyProperty
负责为我们存储数据。如果我们不创建只读属性,这将允许使用绑定和其他很酷的WPF功能。因此,养成使用依赖项属性的习惯很重要,而不是像在Internet上的示例中经常看到的普通属性一样。UIElementCollection.Add
。有更复杂的例子,特别是在MSDN上。代码:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
namespace Demo
{
[ContentProperty(nameof(Children))] // Prior to C# 6.0, replace nameof(Children) with "Children"
public partial class MultiChildDemo : UserControl
{
public static readonly DependencyPropertyKey ChildrenProperty = DependencyProperty.RegisterReadOnly(
nameof(Children), // Prior to C# 6.0, replace nameof(Children) with "Children"
typeof(UIElementCollection),
typeof(MultiChildDemo),
new PropertyMetadata());
public UIElementCollection Children
{
get { return (UIElementCollection)GetValue(ChildrenProperty.DependencyProperty); }
private set { SetValue(ChildrenProperty, value); }
}
public MultiChildDemo()
{
InitializeComponent();
Children = PART_Host.Children;
}
}
}
请注意标签是<demo:MultiChildDemo>
元素的直接后代。您也可以将它们包含在<demo:MultiChildDemo.Children>
元素中。我们添加到MultiChild类的ContentPropertyAttribute
属性允许我们省略此步骤。
代码:
<Window x:Class="Demo.MultiChildDemoWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:demo="clr-namespace:Demo"
Title="MultiChildDemoWindow" Height="300" Width="300">
<demo:MultiChildDemo>
<Label>Test 1</Label>
<Label>Test 2</Label>
<Label>Test 3</Label>
</demo:MultiChildDemo>
</Window>
答案 1 :(得分:4)
首先尝试了解User Control和自定义控件(Control / Content Control)
之间的区别为了简单起见:
“标准的WPF控件提供了大量的内置功能 功能。如果其中一个预置控件的功能, 例如进度条或滑块,匹配您的功能 想要合并,那么你应该为此创建一个新模板 预先存在的控制,以达到你想要的外观。创建一个新的 模板是创建自定义元素的最简单的解决方案,所以你 应首先考虑该选项。
如果要包含在应用程序中的功能可以 通过预先存在的控件和代码的组合来实现, 考虑创建用户控件。用户控件使您可以绑定 将多个预先存在的控件放在一个界面中并添加 确定其行为方式的代码。
如果没有先前存在的控制或控制组合可以实现 您想要的功能,创建自定义控件。自定义控件 使您能够创建一个定义视觉的全新模板 控件的表示和添加确定的自定义代码 控件的功能。“
Adam Nathan - WPF释放4
现在,如果你想要的只是一个ContentControl:
对于多个项目,内容请查看ItemsControl
以上步骤修改为:
派生物品控制
public class MyCtrl : ItemsControl
{
static MyCtrl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCtrl), new FrameworkPropertyMetadata(typeof(MyCtrl)));
}
}
修改Generic.xaml以包含ItemsPresenter
<Style TargetType="{x:Type local:MyCtrl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCtrl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ItemsPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
使用控件
<StackPanel>
<ctrl:MyCtrl>
<Button Width="100" Height="50">Click</Button>
<Button Width="100" Height="50">Click</Button>
<Button Width="100" Height="50">Click</Button>
</ctrl:MyCtrl>
</StackPanel>
如上所述,对于这个简单的情况,没有必要派生ItemsControl,只需使用ItemsControl并为它定义一个模板。计划通过添加功能
进行扩展时派生ItemsControl编辑:
边框(参见xaml)未在设计器中显示,并且在应用程序运行时未显示,甚至没有边距
您应该在控件上设置Border属性:
<ctrl:MyCtrl BorderBrush="Red" BorderThickness="3" Background="Green" >
答案 2 :(得分:4)
只需删除UserControl标记并替换为Grid
即可