将子级添加到 UserControl

Adding children to UserControl(将子级添加到 UserControl)

本文介绍了将子级添加到 UserControl的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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.

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

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.

首先我发现这个链接,我尝试了一些变化.我设法在正确的容器中显示内容,但它在设计器中不起作用,因为内容属性是设置私有的并且设计器想要覆盖它.将所有内容都放在 XAML 中是可行的,但这在与设计人员合作时并不好.这可能是最喜欢的方式.

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.

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

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.

经过几个小时寻找简单快捷的解决方案后,我决定在这里寻求解决方案.我有点失望.如果 Microsoft 可以将示例放入 MSDN,那将非常有帮助.

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.

感谢 Andrei Gavrilajberger 我存档创建了一个显示内容的节点(参见下面的代码),但仍然存在两个问题:- 没有设计师支持- 边框(见 xaml)不显示在设计器中,并且在应用程序运行时不显示,甚至没有边距

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));
        }
    }
}

节点 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 类型的依赖属性.试试这样的:

You cannot bind dependency properties of type UIElementCollection, generally. Try something like this:

这里没什么可看的.StackPanel 将保存我们的子元素.你显然可以做得更多.

Nothing much to see here. The StackPanel will hold our child elements. You could obviously do quite a bit more.

代码:

<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 上.
  • The 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 the Children property.
  • We are extending a UserControl. This does not necessitate a completely custom control.
  • It is good practice in WPF to make properties using DependencyProperty.Register() and variants. You will notice that there is no backing variable for the Children 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.
  • This is a relatively simple dependency property example. All we do is copy the reference to a child's dependency property, thereby forwarding calls to UIElementCollection.Add. Much more complex examples are out there, especially on 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 属性允许我们省略这一步.

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. The ContentPropertyAttribute attribute that we added to the MultiChild class allows us to omit this step, though.

代码:

<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="TXVsdGlDaGlsZERlbW9XaW5kb3c=" Height="300" Width="300">
    <demo:MultiChildDemo>
        <Label>Test 1</Label>
        <Label>Test 2</Label>
        <Label>Test 3</Label>
    </demo:MultiChildDemo>
</Window>

这篇关于将子级添加到 UserControl的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:将子级添加到 UserControl