'How to replicate behavior of standard list elements to instantiate child items in WPF?

I'm creating a Wizard control based on Selector. My plan is that it will behave in the similar way to TabControl, ListBox, etc. - by having its own child items.

What I already did is:

public class Wizard : Selector
{
    // ...

    protected override DependencyObject GetContainerForItemOverride() => new WizardPage();

    protected override bool IsItemItsOwnContainerOverride(object item) => item is WizardPage;

    // ...

The relevant part ControlTemplate for my control looks like this:

<Style TargetType="{x:Type c:Wizard}">
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <Grid />
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type c:Wizard}">
                <DockPanel Background="{TemplateBinding Background}"
                    Margin="{TemplateBinding Padding}">
                    <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right">
                        <Button Name="BackButton" MinWidth="75" Command="{x:Static c:WizardCommands.PreviousPage}"
                        Content="{TemplateBinding BackButtonContent}" Style="{StaticResource WizardButton}">
                            <Button.Visibility>
                                <MultiBinding Converter="{StaticResource WizardPageButtonVisibilityConverter}">
                                    <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="BackButtonVisibility" />
                                    <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="SelectedItem.BackButtonVisibility" />
                                </MultiBinding>
                            </Button.Visibility>
                        </Button>
                        <Button Name="NextButton" Command="{x:Static c:WizardCommands.NextPage}"
                        Content="{TemplateBinding NextButtonContent}" Style="{StaticResource WizardButton}">
                            <Button.Visibility>
                                <MultiBinding Converter="{StaticResource WizardPageButtonVisibilityConverter}">
                                    <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="NextButtonVisibility" />
                                    <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="SelectedItem.NextButtonVisibility" />
                                </MultiBinding>
                            </Button.Visibility>
                        </Button>
                        <Button Name="FinishButton" Command="{x:Static c:WizardCommands.Finish}" Content="{TemplateBinding FinishButtonContent}"
                        Style="{StaticResource WizardButton}">
                            <Button.Visibility>
                                <MultiBinding Converter="{StaticResource WizardPageButtonVisibilityConverter}">
                                    <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="FinishButtonVisibility" />
                                    <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="SelectedItem.FinishButtonVisibility" />
                                </MultiBinding>
                            </Button.Visibility>
                        </Button>
                        <Button Name="CancelButton" Command="{x:Static c:WizardCommands.Cancel}" Content="{TemplateBinding CancelButtonContent}"
                        Style="{StaticResource WizardButton}">
                            <Button.Visibility>
                                <MultiBinding Converter="{StaticResource WizardPageButtonVisibilityConverter}">
                                    <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="CancelButtonVisibility" />
                                    <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="SelectedItem.CancelButtonVisibility" />
                                </MultiBinding>
                            </Button.Visibility>
                        </Button>
                    </StackPanel>

                    <ContentPresenter Content="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}}" />
                </DockPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

However, when I assign an ObservableCollection<BaseViewModel> along with specific DataTemplates...

<c:Wizard Grid.Column="1" ItemsSource="{Binding WizardSteps}">
    <c:Wizard.Resources>
        <DataTemplate DataType="{x:Type vm:WizardStep1ViewModel}">
            <Border Background="Red" />
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:WizardStep2ViewModel}">
            <Border Background="Green" />
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:WizardStep3ViewModel}">
            <Border Background="Blue" />
        </DataTemplate>
    </c:Wizard.Resources>
</c:Wizard>

...then the DataTemplates are instantiated and shown properly, but no WizardPages are being created in the meantime.

How can I fix it?



Solution 1:[1]

Your ControlTemplate should include an ItemsPresenter for a WizardPage to get created for each item in the source collection, e.g.:

<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="c:Wizard">
            <ItemsPresenter />
        </ControlTemplate>
    </Setter.Value>
</Setter>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 mm8