如何创建接受在 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
的内容,其中包括 Grid
和 Expander
本身,这就是 header 似乎丢失的原因。
<objectViews:ContractableGroupBox Header="TEST">
<local:Filterview></local:Filterview>
</objectViews:ContractableGroupBox>
作为一般建议,永远不要 将 UserControl
的 DataContext
设置为自身,这会破坏数据上下文继承并且是不好的做法.关于您的问题,您应该使 Header
成为依赖项 属性 以启用数据绑定,并为扩展器的内容添加另一个依赖项 属性,例如ExpanderContent
(Content
已经存在于 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
本身的实际内容(您的 Grid
、Expander
等将被覆盖。
<objectViews:ContractableGroupBox Header="TEST">
<local:Filterview></local:Filterview>
</objectViews:ContractableGroupBox>
您必须使用 RelativeSource
和 AncestorType
更改用户控件 XAML 绑定,以便它们解析依赖属性 Header
和 ExpanderContent
你的控制。请注意,我将 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
创建一个自定义控件模板。
目标:
我想实现类似于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
的内容,其中包括 Grid
和 Expander
本身,这就是 header 似乎丢失的原因。
<objectViews:ContractableGroupBox Header="TEST">
<local:Filterview></local:Filterview>
</objectViews:ContractableGroupBox>
作为一般建议,永远不要 将 UserControl
的 DataContext
设置为自身,这会破坏数据上下文继承并且是不好的做法.关于您的问题,您应该使 Header
成为依赖项 属性 以启用数据绑定,并为扩展器的内容添加另一个依赖项 属性,例如ExpanderContent
(Content
已经存在于 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
本身的实际内容(您的 Grid
、Expander
等将被覆盖。
<objectViews:ContractableGroupBox Header="TEST">
<local:Filterview></local:Filterview>
</objectViews:ContractableGroupBox>
您必须使用 RelativeSource
和 AncestorType
更改用户控件 XAML 绑定,以便它们解析依赖属性 Header
和 ExpanderContent
你的控制。请注意,我将 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
创建一个自定义控件模板。