将子项添加到 UserControl
我的任务
创建一个 UserControl
,它应该能够包含 WPF 中可用的任何可视子项,这些子项显示在作为 UserControl
的子项的容器中。
我的问题
我无法让子项在我的容器中正确显示,我尝试了几种方法,但没有找到在设计器中有效的方法。我还尝试使用 ContentControl
但没有显示任何内容。
我的方法
首先我找到这个链接我尝试了一些变化。我设法在正确的容器中显示内容,但它在设计器中不起作用,因为内容属性设置为私有,并且设计器想要覆盖它。将所有内容都放在 XAML 中是可行的,但在与设计师合作时这并不好。这是可能最喜欢的方式。
之后,我尝试通过将其 Content
属性绑定到 UIElementCollection
类型的可绑定属性来使用 ContentControl
。这种方法不会在设计器中引发任何错误,但我必须承认我在容器中从未看到任何控件(例如 Button
)。它仍然是空的,但添加了孩子。
结论
经过几个小时的寻找简单快捷的解决方案后,我决定在这里寻求解决方案。我有点失望。如果 Microsoft 能够在 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));
}
}
}
Node-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>
Generic-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>
My task
Create a UserControl
which should be able to contain any visual child which is available in WPF, the children are displayed in a container which is a child of the UserControl
.
My Problem
I can't manage to get the children displayed correctly in my container, i tried serval ways and did not find a way which works in the designer. I also tried to use ContentControl
but nothing gets displayed.
My approaches
First i found this link and i tried it with some variations. I managed to display the content in the right container but it does not work in the designer because the content-property is set-private and the designer want to override it. Placing everything in XAML works but this is not good when working with designers. This is may favorite way.
After this i tried to use ContentControl
by binding it's Content
-property to a bindable property of the UIElementCollection
-type. This aproach is not throwing any errors in the designer, but i have to admit that i never see any control ( e.g. a Button
) in my container. It stays empty but has the children added.
Conclusion
After serval hours of searching for a easy and quick solution i decided to ask for solutions here. I'm a little disappointed. It would be really helpful if Microsoft could get a sample into MSDN.
I'm sure there must be a easy way to archive this.
Current situation
Thanks to Andrei Gavrila and jberger i archived to create a node which displays the content ( see code below ) but there are still two issues:
- No designer support
- The border ( see xaml ) is not shown in designer and not shown when the app is running there is even no margin
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));
}
}
}
Node-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>
Generic-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>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
通常,您无法绑定
UIElementCollection
类型的依赖属性。尝试这样的事情:MultiChildDemo.xaml
这里没什么可看的。 StackPanel 将保存我们的子元素。显然你可以做得更多。
代码:
MultiChildDemo.xaml.cs
需要注意的重要事项:
ContentPropertyAttribute
特性设置将由该类型的父元素所包含的任何元素设置的属性。因此,
中的任何元素都将添加到Children
属性中。UserControl
。这并不需要完全自定义的控件。Children
属性没有支持变量:DependencyProperty
负责为我们存储数据。如果我们不创建只读属性,这将允许使用绑定和其他很酷的 WPF 功能。因此,养成使用依赖属性的习惯非常重要,而不是像您在 Internet 上的示例中经常看到的普通属性。UIElementCollection.Add
。还有更复杂的示例,尤其是在 MSDN 上。代码:
MultiChildDemoWindow.xaml
请注意标签如何成为
元素的直接后代。您还可以将它们包含在
元素中。不过,我们添加到 MultiChild 类中的ContentPropertyAttribute
属性允许我们省略此步骤。代码:
You cannot bind dependency properties of type
UIElementCollection
, generally. Try something like this:MultiChildDemo.xaml
Nothing much to see here. The StackPanel will hold our child elements. You could obviously do quite a bit more.
Code:
MultiChildDemo.xaml.cs
Important to note:
ContentPropertyAttribute
attribute sets the property that will be set by any elements enclosed by the parent element of this type. Thus, any elements within<MultiChildDemo></MultiChildDemo>
will be added to theChildren
property.UserControl
. This does not necessitate a completely custom control.DependencyProperty.Register()
and variants. You will notice that there is no backing variable for theChildren
property:DependencyProperty
takes care of storing the data for us. Were we not creating a read-only property, this would enable the use of bindings and other cool WPF features. Thus, it is important to get into the habit of using dependency properties, rather than plain properties as you often see in examples around the Internet.UIElementCollection.Add
. Much more complex examples are out there, especially on MSDN.Code:
MultiChildDemoWindow.xaml
Note how the labels are direct descendants of the
<demo:MultiChildDemo>
element. You could also enclose them in a<demo:MultiChildDemo.Children>
element. TheContentPropertyAttribute
attribute that we added to the MultiChild class allows us to omit this step, though.Code:
只需删除 UserControl 标签并替换为 Grid
Just remove the UserControl tag and replace with Grid
首先尝试了解 用户控件之间的区别 和自定义控件 (控制/内容控制< /a>)
为了简单起见:
Adam Nathan - WPF Unleashed 4
现在,如果您想要的只是一个 ContentControl:
对于作为内容的多个项目,请查看 ItemsControl
上述步骤修改为:
Derive Items控件
修改 Generic.xaml 以包含 ItemsPresenter
使用控件
如上所述,对于这个简单的情况,不需要派生 ItemsControl,而只需使用 ItemsControl 并为其定义一个模板。计划通过添加功能进行扩展时派生 ItemsControl
编辑:
您应该在控件本身上设置 Border 属性:
First of all try to understand the difference between an User Control and a Custom Control (Control/Content Control)
To keep it simple:
Adam Nathan - WPF Unleashed 4
Now if all you want is a ContentControl:
For multiple items as Content take a look at ItemsControl
The steps above are modified as:
Derive Items Control
Modify Generic.xaml to include ItemsPresenter
Use the control
As said above, for this simple case it would not be necessary to derive ItemsControl but to simply use the ItemsControl and define a Template for it. Derive ItemsControl when planning to extend by by adding functionality
EDIT:
You should set the Border properties on your control itself: