简体   繁体   中英

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

Goal:

I want to achieve something similar to the GroupBox control.

I want to have some design and controls wrapping around a child element which I specify in XAML:

Current code:

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

Goal:

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

Current Situation / Issue:

My custom "groupbox" works as far as adding it to the Form and setting the header but it does not work properly when adding the child element Filterview .

Working (But no content):

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

没有内容的可收缩组框显示标题。

Bugs out (content is there but wrapping not):

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

包含内容的可收缩组框不显示标题。

Code Behind:

This is the XAML of ContractableGroupBox :

<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>

Note, I want to specify the child element in the parent UserControl , but it should be displayed as if in the Expander node such as:

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

This is the current 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;}
    }
}

What you see ist that the following XAML overrides the content of your UserControl , which includes the Grid and the Expander itself and that is why the header is seemingly lost.

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

As a general advise, do not ever set the DataContext of a UserControl to itself, this will break data context inheritance and is bad practice. Regarding your issue, you should make Header a dependency property to enable data binding and add another dependency property for the content of the expander, eg ExpanderContent ( Content already exists on 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);
   }
}

The ContentProperty attribute at the top will make sure that anything you put inside this user control in XAML like below will be assigned to the ExpanderContent property instead of the Content property that the UserControl type already provides. If you do not do this, you have to assign your content manually to ExpanderContent , otherwise the actual content of the UserControl itself (your Grid , Expander , etc. will be overridden.

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

You have to change your user control XAML bindings using RelativeSource and AncestorType , so that they resolve the dependency properties Header and ExpanderContent on your control. Notice, that I renamed the Expander to Expander to avoid a naming collision with the dependency property ExpanderContent . Now that the bindings use the dependency properties, there is even no need for setting the 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>

However, if the only thing that you want to add to the Expander is a Border around it, then you do not have to create a separate UserControl . You could just create a custom control template for Expander by copying its default template and add a Border there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM