Search code examples
c#wpftemplatesitemscontrol

Adding multiple children to a property of a custom control


I am trying to create a base view that knows where to place some buttons (Actions) that I am defining in my actual views. I have ViewA which derives from BaseView. BaseView is a custom control with some properties and a generic template. ViewA derives from BaseView and defines some buttons that BaseView should display in a StackPanel.

This is how ViewA should look:

<BaseView x:Class="ViewA"
             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" 
             mc:Ignorable="d">
    <Grid>
        <!-- This will be the main content for the view -->
    </Grid>
    <BaseView.Actions>
        <!-- Here I want these buttons to be displayed in a StackPanel where the template is defined by BaseView -->
        <Button>Button1</Button>
        <Button>Button2</Button>
    </BaseView.Actions>
</BaseView>

This is my BaseView template with where I would like the buttons to be displayed:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Style TargetType="{x:Type BaseView}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type BaseView}">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*"></RowDefinition>
                            <RowDefinition Height="Auto"></RowDefinition>
                        </Grid.RowDefinitions>
                        <ContentPresenter Grid.Row="0" />
                        <!-- I would like the buttons defined in Actions to display here in a StackPanel -->
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

How can I accomplish this? I was trying to use an ItemsControl but I am unsure what type the "Actions" property should be and how to bind that to an ItemsControl?


Solution

  • I ended up implementing this with the below dependency property and an ItemsControl in my template:

    public static readonly DependencyProperty ActionsProperty =
        DependencyProperty.Register("Actions", typeof(ObservableCollection<UIElement>), typeof(ModalWindow), new UIPropertyMetadata(new ObservableCollection<UIElement>()));
    
    public ObservableCollection<UIElement> Actions
    {
        get => (ObservableCollection<UIElement>)GetValue(ActionsProperty);
        set => SetValue(ActionsProperty, value);
    }
    

    <ItemsControl Grid.Row="1" ItemsSource="{TemplateBinding Actions}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
    

    Usage:

    <BaseView.Actions>
        <Button>Action 1</Button>
        <Button>Action 2</Button>
    </BaseView.Actions>
    

    I think a UIElementCollection would have been better suited for the type of the property but I am unsure how to instantiate that collection with the required parameters.