将子项添加到UserControl

Fel*_* K. 23 c# wpf user-controls wpf-controls

我的任务

创建一个UserControl应该能够包含WPF中可用的任何可视子项的子项,子项显示在一个容器中,该容器是一个子容器UserControl.

我的问题

我无法设法让我的容器中的孩子正确显示,我尝试了serval方式,并没有找到一种适用于设计师的方法.我也尝试使用ContentControl但没有显示任何内容.

我的方法

首先,我找到了这个链接,我尝试了一些变化.我设法在正确的容器中显示内容,但它在设计器中不起作用,因为content-property是set-private而设计者想要覆盖它.将所有内容放在XAML中都有效,但在与设计师合作时这并不好.这可能是最喜欢的方式.

在此之后我试图ContentControl通过将它的Content-property绑定到UIElementCollection-type 的可绑定属性来使用.这种方法不会给设计师带来任何错误,但我必须承认我从未Button在容器中看到任何控件(例如a ).它保持空白但添加了孩子.

结论

经过一段时间寻找一个简单快捷的解决方案后,我决定在这里寻求解决方案.我有点失望.如果微软可以将样本带入MSDN,那将非常有用.

我确信必须有一种简单的方法来存档.

现在的情况

感谢Andrei Gavrilajberger,我将其存档以创建一个显示内容的节点(请参阅下面的代码),但仍有两个问题: - 没有设计师支持 - 边框(请参阅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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

节点的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>
Run Code Online (Sandbox Code Playgroud)

仿制的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>
Run Code Online (Sandbox Code Playgroud)

Zen*_*xer 44

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>
Run Code Online (Sandbox Code Playgroud)

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

MultiChildDemoWindow.xaml

请注意标签是<demo:MultiChildDemo>元素的直接后代.您也可以将它们包含在<demo:MultiChildDemo.Children>元素中.但是ContentPropertyAttribute,我们添加到MultiChild类的属性允许我们省略此步骤.

码:

<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>
Run Code Online (Sandbox Code Playgroud)

  • 这是有效的,但是当你在设计师中获得"属性'内容'时,设置的次数会超过一次." 它仍然可以构建并运行良好. (2认同)
  • 感谢您提供出色的解决方案。我知道我已经进行这个讨论好几年了,但是...如果我采用您的解决方案并更改一件事,则绑定标签内容将不起作用(即 &lt;Label Content = "{Binding myDataContextProperty}" /&gt; )。我已经尝试了绑定属性的所有组合,但没有成功。将内容硬编码为“Hello”,就像魅力一样。有任何想法吗? (2认同)