将子项添加到用户控件
本文关键字:用户 控件 添加 | 更新日期: 2023-09-27 18:22:19
我的任务
创建一个应该能够包含 WPF 中可用的任何可视子项的UserControl
,这些子项显示在作为UserControl
子项的容器中。
我的问题
我无法设法在我的容器中正确显示孩子,我尝试了薮猫方式,但没有找到在设计器中工作的方法。我也尝试使用ContentControl
但没有任何显示。
我的方法
首先,我找到了这个链接,并尝试了一些变化。我设法在正确的容器中显示内容,但它在设计器中不起作用,因为内容属性是设置私有的,并且设计器想要覆盖它。将所有内容放在 XAML 中是有效的,但在与设计器合作时,这并不好。这可能是最喜欢的方式。
在此之后,我尝试通过将Content
属性绑定到UIElementCollection
类型的可绑定属性来使用ContentControl
。这个 aproach 不会在设计器中抛出任何错误,但我不得不承认我从未在我的容器中看到任何控件(例如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>
泛型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>
通常不能绑定类型 UIElementCollection
的依赖属性。 尝试这样的事情:
MultiChildDemo.xaml
这里没什么可看的。 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>
MultiChildDemo.xaml.cs
重要提示:
ContentPropertyAttribute
属性设置将由此类型的父元素包含的任何元素设置的属性。 因此,<MultiChildDemo></MultiChildDemo>
中的任何元素都将添加到Children
属性中。- 我们正在扩展
UserControl
. 这不需要完全自定义的控件。 - 在 WPF 中,最好使用
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;
}
}
}
MultiChildDemoWindow.xaml
请注意标签是如何<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>
只需删除 UserControl 标记并替换为网格
首先尝试了解用户控件和自定义控件(控件/内容控件(之间的区别
为简单起见:
"标准 WPF 控件提供了大量内置控件 功能性。如果其中一个预设控件的功能, 例如进度条或滑块,与您匹配的功能 想要合并,那么您应该为此创建一个新模板 预先存在的控件以实现所需的外观。创建一个新的 模板是创建自定义元素的最简单解决方案,因此 应该首先考虑该选项。
如果要合并到应用程序中的功能可以 通过预先存在的控件和代码的组合来实现, 请考虑创建用户控件。用户控件使您能够绑定 在单个界面中将多个预先存在的控件放在一起并添加 确定其行为方式的代码。
如果没有预先存在的控件或控件组合可以实现 所需的功能,创建自定义控件。自定义控件 使您能够创建定义视觉对象的全新模板 控件的表示形式,并添加确定的自定义代码 控件的功能。
亚当·内森 - WPF 释放 4
现在,如果您只需要一个 ContentControl:
- 创建一个派生 ContentControl 的新自定义控件
- 在主题中找到 generic.xaml,并将内容演示器添加到控件模板。如上所述,自定义控件逻辑与其视觉呈现是分开的
- 将该控件用作常规内容控件。
对于作为内容的多个项目,请查看项目控件
上述步骤修改为:
派生项控件
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 并为其定义一个模板。派生项控制,当计划通过添加功能进行扩展时
编辑:
边框(参见xaml(在设计器中不显示,并且在应用程序运行时不显示,甚至没有边距
应在控件本身上设置 Border 属性:
<ctrl:MyCtrl BorderBrush="Red" BorderThickness="3" Background="Green" >