将子项添加到UserControl
我的任务
创建一个UserControl
,它应该能够包含WPF中可用的任何可视子对象,子对象显示在一个容器中,该容器是UserControl
的子对象。
我的问题
我无法设法让孩子们在我的容器中正确显示,我尝试了serval方式,并没有找到适合设计师的方法。 我也尝试使用ContentControl
但没有显示任何内容。
我的方法
首先,我找到了这个链接,我尝试了一些变化。 我设法在正确的容器中显示内容,但它在设计器中不起作用,因为content-property是set-private而设计者想要覆盖它。 将所有内容放在XAML中都有效但与设计人员合作时这并不好。 这可能是最喜欢的方式。
在此之后,我试图通过将Content
-property绑定到UIElementCollection
-type的可绑定属性来使用ContentControl
。 这种方法不会给设计师带来任何错误,但我不得不承认我从未在容器中看到任何控件(例如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:
仿制的XAML:
通常,您不能绑定UIElementCollection
类型的依赖项属性。 尝试这样的事情:
MultiChildDemo.xaml
没什么可看的。 StackPanel将保存我们的子元素。 你显然可以做得更多。
码:
MultiChildDemo.xaml.cs
重要提示:
-
ContentPropertyAttribute
属性设置将由此类型的父元素包含的任何元素设置的属性。 因此,
任何元素都将添加到Children
属性中。 - 我们正在扩展
UserControl
。 这不需要完全自定义控件。 - 在WPF中使用
DependencyProperty.Register()
和变体创建属性是一种很好的做法。 您会注意到Children
属性没有后备变量:DependencyProperty
负责为我们存储数据。 如果我们不创建只读属性,这将允许使用绑定和其他很酷的WPFfunction。 因此,养成使用依赖项属性的习惯很重要,而不是像在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
请注意标签是
元素的直接后代。 您也可以将它们包含在
元素中。 但是,我们添加到MultiChild类的ContentPropertyAttribute
属性允许我们省略此步骤。
码:
只需删除UserControl标记并替换为Grid
首先尝试理解用户控件和自定义控件( 控件 / 内容控件 )之间的区别
为了简单起见:
“标准的WPF控件提供了大量的内置function。如果其中一个预设控件(如进度条或滑块)的function与您要合并的function相匹配,那么您应该创建一个新模板对于那个预先存在的控件来实现你想要的外观。创建一个新模板是创建自定义元素的最简单的解决方案,所以你应该首先考虑该选项。
如果要将要包含在应用程序中的function通过预先存在的控件和代码的组合来实现,请考虑创建用户控件。 用户控件使您可以在单个界面中将多个预先存在的控件绑定在一起,并添加确定其行为方式的代码。
如果没有预先存在的控件或控件组合可以实现所需的function,请创建自定义控件。 通过自定义控件,您可以创建一个全新的模板,用于定义控件的可视化表示,并添加用于确定控件function的自定义代码。“
Adam Nathan – WPF Unleashed 4
现在,如果你想要的只是一个ContentControl:
- 创建一个新的CustomControl来派生ContentControl
- 在主题中找到generic.xaml,并将Content Presenter添加到控件模板中。 如上所述,自定义控制逻辑与其视觉呈现分开
- 将控件用作常规ContentControl。
对于多个项目作为内容,请查看ItemsControl
上述步骤修改为:
派生物品控制
public class MyCtrl : ItemsControl { static MyCtrl() { DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCtrl), new FrameworkPropertyMetadata(typeof(MyCtrl))); } }
修改Generic.xaml以包含ItemsPresenter
使用控件
如上所述,对于这个简单的情况,没有必要派生ItemsControl,只需使用ItemsControl并为它定义一个模板。 计划通过添加function进行扩展时派生ItemsControl
编辑:
边框(参见xaml)未在设计器中显示,并且在应用程序运行时未显示,甚至没有边距
您应该在控件本身上设置Border属性: