如何创建一个接受在 XAML 中添加子元素的 UserControl
Posted
技术标签:
【中文标题】如何创建一个接受在 XAML 中添加子元素的 UserControl【英文标题】:How to create a UserControl which accepts adding child elements in XAML 【发布时间】:2021-07-25 18:19:13 【问题描述】:目标:
我想实现类似于GroupBox
控件的功能。
我想要围绕我在 XAML 中指定的子元素进行一些设计和控件:
当前代码:
<GroupBox Header="Filter">
<local:Filterview></local:Filterview>
</GroupBox>
目标:
<objectViews:ContractableGroupBox Header="TEST">
<local:Filterview></local:Filterview>
</objectViews:ContractableGroupBox>
现状/问题:
我的自定义“组框”可以将其添加到表单并设置标题,但在添加子元素 Filterview
时无法正常工作。
工作(但没有内容):
<objectViews:ContractableGroupBox Header="TEST">
</objectViews:ContractableGroupBox>
Bugs out(内容存在但没有包装):
<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>
注意,我想在父UserControl
中指定子元素,但是应该像在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;
【问题讨论】:
【参考方案1】:您看到的是,以下 XAML 覆盖您的 UserControl
的内容,其中包括 Grid
和 Expander
本身,这就是标题似乎丢失的原因。
<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
属性,而不是Content
类型已经提供的UserControl
属性.如果您不这样做,则必须手动将您的内容分配给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
创建一个自定义控件模板。
【讨论】:
感谢您的广泛解释。这是我收到的最好的答案之一!还要感谢您在 datacontext 上设置 self 的建议。我对来自 winforms 的 wpf 数据绑定很陌生,在这种情况下,我主要依靠阅读其他地方的问题和教程。以上是关于如何创建一个接受在 XAML 中添加子元素的 UserControl的主要内容,如果未能解决你的问题,请参考以下文章