如何创建接受在 XAML 中添加 child 元素的 UserControl

How to create a UserControl which accepts adding child elements in XAML

目标:

我想实现类似于GroupBox控件的东西。

我想要围绕我在 XAML 中指定的 child 元素进行一些设计和控制:

当前代码:

<GroupBox Header="Filter">
    <local:Filterview></local:Filterview>
</GroupBox>

目标:

<objectViews:ContractableGroupBox Header="TEST">
    <local:Filterview></local:Filterview>
</objectViews:ContractableGroupBox>

现状/问题:

我的自定义“groupbox”可以将其添加到表单并设置 header,但在添加 child 元素 Filterview 时无法正常工作。

正在工作(但没有内容):

<objectViews:ContractableGroupBox Header="TEST">
</objectViews:ContractableGroupBox>

出现错误(内容存在但没有换行):

<objectViews:ContractableGroupBox Header="TEST">
    <local:Filterview></local:Filterview>
</objectViews:ContractableGroupBox>

隐藏代码:

这是ContractableGroupBox的XAML:

<UserControl x:Class="SoundStudio.Views.ObjectViews.ContractableGroupBox"
             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:local="clr-namespace:SoundStudio.Views.ObjectViews"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Border BorderBrush="#FF303030" Background="#FF646464" CornerRadius="8,8,3,3" >
            <Expander x:Name="ExpanderContent" Header="{Binding Header}" IsExpanded="True">
                
            </Expander>
        </Border>  
    </Grid>
</UserControl>

注意,我想在parentUserControl中指定child元素,但是应该像在Expander节点中一样显示如:

<Expander x:Name="ExpanderContent" Header="{Binding Header}" IsExpanded="True">
    <local:Filterview></local:Filterview>    
</Expander>

这是当前的ContractableGroupBox.cs

using System.Windows.Controls;

namespace SoundStudio.Views.ObjectViews
{
    /// <summary>
    /// Interaction logic for ContractableGroupBox.xaml
    /// </summary>
    public partial class ContractableGroupBox : UserControl
    {
        public ContractableGroupBox()
        {
            InitializeComponent();
            this.DataContext = this;
        }
        public string Header { get; set;}
    }
}

您看到的是以下 XAML 覆盖了 您的 UserControl 的内容,其中包括 GridExpander 本身,这就是 header 似乎丢失的原因。

<objectViews:ContractableGroupBox Header="TEST">
    <local:Filterview></local:Filterview>
</objectViews:ContractableGroupBox>

作为一般建议,永远不要UserControlDataContext 设置为自身,这会破坏数据上下文继承并且是不好的做法.关于您的问题,您应该使 Header 成为依赖项 属性 以启用数据绑定,并为扩展器的内容添加另一个依赖项 属性,例如ExpanderContentContent 已经存在于 UserControl 上)。

[ContentProperty(nameof(ExpanderContent))]
public partial class ContractableGroupBox : UserControl
{
   public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
      nameof(Header), typeof(string), typeof(ContractableGroupBox));


   public static readonly DependencyProperty ExpanderContentProperty = DependencyProperty.Register(
      nameof(ExpanderContent), typeof(object), typeof(ContractableGroupBox));


   public ContractableGroupBox()
   {
      InitializeComponent();
   }

   public string Header
   {
      get => (string)GetValue(HeaderProperty);
      set => SetValue(HeaderProperty, value);
   }

   public object ExpanderContent
   {
      get => GetValue(ExpanderContentProperty);
      set => SetValue(ExpanderContentProperty, value);
   }
}

顶部的 ContentProperty 属性将确保您在 XAML 中放入此用户控件中的任何内容(如下所示)都将分配给 ExpanderContent 属性 UserControl 类型已经提供的 Content 属性。如果不这样做,则必须手动将内容分配给 ExpanderContent,否则 UserControl 本身的实际内容(您的 GridExpander 等将被覆盖。

<objectViews:ContractableGroupBox Header="TEST">
    <local:Filterview></local:Filterview>
</objectViews:ContractableGroupBox>

您必须使用 RelativeSourceAncestorType 更改用户控件 XAML 绑定,以便它们解析依赖属性 HeaderExpanderContent你的控制。请注意,我将 Expander 重命名为 Expander 以避免与依赖项 属性 ExpanderContent 发生命名冲突。现在绑定使用依赖属性,甚至不需要设置 DataContext.

<UserControl x:Class="SoundStudio.Views.ObjectViews.ContractableGroupBox"
             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:local="clr-namespace:SoundStudio.Views.ObjectViews"
             mc:Ignorable="d" >
   <Grid>
      <Border BorderBrush="#FF303030" Background="#FF646464" CornerRadius="8,8,3,3" >
         <Expander x:Name="Expander"
                   IsExpanded="True"
                   Header="{Binding Header, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                   Content="{Binding ExpanderContent, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>
      </Border>  
   </Grid>
</UserControl>

但是,如果您想要添加到 Expander 的唯一内容是围绕它的 Border,那么您不必创建单独的 UserControl。您可以通过复制其默认模板并在其中添加 Border 来为 Expander 创建一个自定义控件模板。