Reputation: 2251
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>
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>
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;}
}
}
Upvotes: 0
Views: 883
Reputation: 22119
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, e.g. 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.
Upvotes: 1