使用 child 元素创建 WPF 用户控件

Creating a WPF user control with a child element

我尝试创建一个 "container" 用户控件,它可以在 WPF 中将 UIElement objects 显示为 child。控件的用法应如下所示:

<general:DisplayContainer Title="Hello">
  <TextBlock x:Name="AnyUIElement" />
</general:DisplayContainer>

其中 TextBlock 只是一个示例。到目前为止,我创建了 UserControl 并像这样绑定标题 属性:

<Grid x:Name="Grid_Main">
  <Border x:Name="Border_Main" BorderThickness="2" Margin="10" CornerRadius="5" Padding="7" />

  <TextBlock x:Name="TextBlock_Title" Background="{Binding Background, ElementName=Grid_Main}" HorizontalAlignment="Left" VerticalAlignment="Top"  Text="{Binding Path=Title, FallbackValue=Title}" Margin="20,2,0,0" Padding="3,0" />
</Grid>

代码隐藏文件如下所示:

public partial class DisplayContainer : UserControl
{
  public DisplayContainer()
  {
    InitializeComponent();
    this.DataContext = this;
  }

  public string Title
  {
    get { return (string)GetValue(TitleProperty); }
    set { SetValue(TitleProperty, value); }
  }

  public static readonly DependencyProperty TitleProperty =
      DependencyProperty.Register("Title",typeof(string), typeof(DisplayContainer));
}

现在,我如何修改我的控件,使我在使用控件时接受 XAML-file 中的 child 元素? child 应通过 Border_Main.Child 属性 显示。

提前致谢,
弗兰克

只需设置用户控件的模板属性

<UserControl ...>
    <UserControl.Template>
        <ControlTemplate TargetType="UserControl">
            <StackPanel>
                <TextBlock Text="{Binding Title,
                           RelativeSource={RelativeSource AncestorType=UserControl}}"/>
                <ContentPresenter />
            </StackPanel>
        </ControlTemplate>
    </UserControl.Template>
</UserControl>

并将显示的元素放在用户控件的内容中:

<local:DisplayContainer Title="A Title">
    <TextBlock Text="Hello"/>
</local:DisplayContainer>

或者您可以将 DisplayContainer 定义为没有 .xaml 文件但带有 ControlTemplateContentControl:

public partial class DisplayContainer : ContentControl
{
    public DisplayContainer()
    {
        this.DataContext = this;
    }

    public string Title
    {
        get { return (string)GetValue(TitleProperty); }
        set { SetValue(TitleProperty, value); }
    }

    public static readonly DependencyProperty TitleProperty =
        DependencyProperty.Register("Title", typeof(string), typeof(DisplayContainer));
}

XAML:

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="300" Width="300">
    <Window.Resources>
        <Style TargetType="local:DisplayContainer">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="local:DisplayContainer">
                        <Grid x:Name="Grid_Main">
                            <Border x:Name="Border_Main" BorderThickness="2" Margin="10" CornerRadius="5" Padding="7">
                                <ContentPresenter />
                            </Border>
                            <TextBlock x:Name="TextBlock_Title" Background="{Binding Background, ElementName=Grid_Main}"
                                       HorizontalAlignment="Left" VerticalAlignment="Top"
                                       Text="{Binding Path=Title, RelativeSource={RelativeSource AncestorType=local:DisplayContainer}, FallbackValue=Title}" Margin="20,2,0,0" Padding="3,0" />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <StackPanel>

        <local:DisplayContainer Title="Hello">
            <TextBlock Text="AnyUIElement..." />
        </local:DisplayContainer>

    </StackPanel>
</Window>

或者,另一种方式。

  • 在 DisplayContainer
  • 中创建 UIElement 类型的 DependencyProperty 'Child'
  • 将 ContentPresenter 添加到 Border_Main,其内容绑定到 Child DependencyProperty。
  • 使用 ContentProperty 属性("Child" 的值)标记 DisplayContainer

如果您需要有不同的部分,您可以添加多个 DP。只需添加更多绑定到不同 DP(子、页眉、页脚等)的 ContentPresenters。

DisplayContainer.cs

[System.Windows.Markup.ContentProperty("Child")]
public partial class DisplayContainer : UserControl
{
    public DisplayContainer()
    {
        InitializeComponent();
    }
    public string Title
    {
        get { return (string)GetValue(TitleProperty); }
        set { SetValue(TitleProperty, value); }
    }
    public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(DisplayContainer));



    public UIElement Child
    {
        get { return (UIElement)GetValue(ChildProperty); }
        set { SetValue(ChildProperty, value); }
    }
    public static readonly DependencyProperty ChildProperty = DependencyProperty.Register("Child", typeof(UIElement), typeof(DisplayContainer));
}

DisplayContainer.xaml

    <UserControl x:Class="WpfApp19.DisplayContainer"
                 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:WpfApp19"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
        <Grid x:Name="Grid_Main">
            <Border x:Name="Border_Main" BorderThickness="2" Margin="10" CornerRadius="5" Padding="7">
                <ContentPresenter Content="{Binding Child, RelativeSource={RelativeSource AncestorType=UserControl}}" />
            </Border>

            <TextBlock x:Name="TextBlock_Title" Background="{Binding Background, ElementName=Grid_Main}" HorizontalAlignment="Left" VerticalAlignment="Top"  Text="{Binding Path=Title, FallbackValue=Title, RelativeSource={RelativeSource AncestorType=UserControl}}" Margin="20,2,0,0" Padding="3,0" />
        </Grid>
    </UserControl>

MainWindow.xaml

<Window x:Class="WpfApp19.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:general="clr-namespace:WpfApp19"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <general:DisplayContainer Title="Hello">
            <StackPanel>
                <TextBlock Text="Test1" />
                <TextBlock Text="Test2" />
            </StackPanel>
            <!-- Alternative way of setting Child - if you had more DPs (Header, Footer, etc..) you would have to set their content this way
            <general:DisplayContainer.Child>
                <TextBlock Text="AnyUIElement" />
            </general:DisplayContainer.Child>
            -->
        </general:DisplayContainer>
    </Grid>
</Window>