Aaginor
Aaginor

Reputation: 4782

Creating a WPF user control with a child element

I try to create a "container" user control that can display UIElement objects as child in WPF. The usage of the control should look like this:

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

where the TextBlock is just an example. So far I created the UserControl and bind the Title property like this:

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

The Codebehind file looks like this:

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));
}

Now, how can I modify my control in a way, that i accepts a child element in the XAML-file, when I use the control? The child should be displayed through the Border_Main.Child Property.

Thanks in advance,
Frank

Upvotes: 2

Views: 3472

Answers (3)

J.H.
J.H.

Reputation: 4322

Or, yet another way.

  • Create a DependencyProperty 'Child' of type UIElement in DisplayContainer
  • Add a ContentPresenter to Border_Main that has it's content bound to the Child DependencyProperty.
  • Mark DisplayContainer with the ContentProperty attribute (value of "Child")

You can add many DPs if you needed to have different sections. Just add more ContentPresenters bound to the different DPs (Child, Header, Footer, etc..).

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>

Upvotes: 1

mm8
mm8

Reputation: 169190

Or you could define a DisplayContainer as a ContentControl without a .xaml file but with a ControlTemplate:

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>

Upvotes: 1

Clemens
Clemens

Reputation: 128061

Just set the UserControl's Template property

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

and put the displayed element in the UserControl's Content:

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

Upvotes: 4

Related Questions