Home > Software engineering >  Combine multiple CustomUserControl templates into a single template
Combine multiple CustomUserControl templates into a single template

Time:11-03

I have a custom user control with two templates that I want to use together for each item in a Tab Control. I am only able to use one at a time.

CustomUserControl.xaml.cs

public partial class CustomUserControl : UserControl
{
    public CustomUserControl ()
    {
        InitializeComponent();
        var style = (Style)FindResource("Styling");
        Style = style;
    }

    public static readonly DependencyProperty ItemHeaderTemplateProperty = DependencyProperty.Register(
        nameof(ItemHeaderTemplate), typeof(DataTemplate),
        typeof(ConfigurableCollectionControl), new PropertyMetadata(default(DataTemplate)));

    public DataTemplate ItemHeaderTemplate
    {
        get => (DataTemplate) GetValue(ItemHeaderTemplateProperty);
        set => SetValue(ItemHeaderTemplateProperty, value);
    }

    public static readonly DependencyProperty ItemContentTemplateProperty = DependencyProperty.Register(
        nameof(ItemContentTemplate), typeof(DataTemplate),
        typeof(ConfigurableCollectionControl), new PropertyMetadata(default(DataTemplate)));

    public DataTemplate ItemContentTemplate
    {
        get => (DataTemplate) GetValue(ItemContentTemplateProperty);
        set => SetValue(ItemContentTemplateProperty, value);
    }
}

I can reference one of them in a tab control like this:

CustomUserControl.xaml

<Style x:Key="Styling" TargetType="{x:Type local:CustomUserControl}">
    <Setter Property="Template">
        <Setter.Value>          
            <ControlTemplate TargetType="{x:Type local:CustomUserControl}">
                
                <TabControl Style="{Static CustomStyle}"
                            ItemsSource="{Binding Items}"
                            ContentTemplate="{TemplateBinding ItemContentTemplate}"/>                    
            
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Note: I am referencing it from the main page like so: MainPage.xaml

<test:CustomUserControl Content="{Binding}"
                        ItemHeaderTemplate="{StaticResource TestHeaderTemplate}"
                        ItemContentTemplate="{StaticResource TestContentTemplate}"/>

I have tried referencing them both like this, with a DataTemplate, but the project doesn't compile:

CustomUserControl.xaml

<DataTemplate x:Key="TestTemplate" DataType="{x:Type local:CustomUserControl}">
    <StackPanel>
        <ContentControl Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ItemHeaderTemplate}"/>
        <ContentControl Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ItemContentTemplate}"/>
    </StackPanel>
</DataTemplate>

<Style x:Key="Styling" TargetType="{x:Type local:CustomUserControl}">
    <Setter Property="Template">
        <Setter.Value>          
            <ControlTemplate TargetType="{x:Type local:CustomUserControl}">
                
                <TabControl Style="{Static CustomStyle}"
                            ItemsSource="{Binding Items}"
                            ContentTemplate="{StaticResource TestTemplate}"/>                    
            
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

If I do the same thing, but with a ControlTemplate, it will compile, but then crash when I navigate to the page:

CustomUserControl.xaml

<ControlTemplate x:Key="TestTemplate" DataType="{x:Type local:CustomUserControl}">
    <StackPanel>
        <ContentControl Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ItemHeaderTemplate}"/>
        <ContentControl Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ItemContentTemplate}"/>
    </StackPanel>
</ControlTemplate>

<Style x:Key="Styling" TargetType="{x:Type local:CustomUserControl}">
    <Setter Property="Template">
        <Setter.Value>          
            <ControlTemplate TargetType="{x:Type local:CustomUserControl}">
                
                <TabControl ItemsSource="{Binding Items}"
                            ContentTemplate="{StaticResource TestTemplate}"/>                    
            
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

It seems that ControlTemplate will allow me to reference the CustomUserControl templates, but can't use it as a ContentTemplate. While a DataTemplate can be used as a ContentTemplate, but will not allow me to reference to the CustomUserControl templates.

This led me to try using a DataTemplate with the ControlTemplate referenced inside, as follows.

CustomUserControl.xaml

<ControlTemplate x:Key="TestControlTemplate" DataType="{x:Type local:CustomUserControl}">
    <StackPanel>
        <ContentControl Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ItemHeaderTemplate}"/>
        <ContentControl Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ItemContentTemplate}"/>
    </StackPanel>
</ControlTemplate>

<DataTemplate x:Key="TestDataTemplate" DataType="{x:Type local:CustomUserControl}">
    <StackPanel>
        <ContentControl Template="{StaticResource TestControlTemplate}"/>
    </StackPanel>
</DataTemplate>

<Style x:Key="Styling" TargetType="{x:Type local:CustomUserControl}">
    <Setter Property="Template">
        <Setter.Value>          
            <ControlTemplate TargetType="{x:Type local:CustomUserControl}">
                
                <TabControl ItemsSource="{Binding Items}"
                            ContentTemplate="{StaticResource TestDataTemplate}"/>                    
            
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

But then it crashes again. I get this message

''CustomUserControl' ControlTemplate TargetType does not match templated type 'ContentControl'.'

If I change the TargetType of the ControlTemplate to ContentControl, the project doesn't compile.

Is there a way for this to be done? Or do I need to do this a totally different way?

CodePudding user response:

I set the Name of the UserControl to UserControlName

<UserControl ...
             Name="UserControlName">

I then used the Source & Path to access the ItemHeaderTemplate & ItemContentTemplate templates I had defined in the CustomUserControl

<ControlTemplate x:Key="TestControlTemplate" DataType="{x:Type local:CustomUserControl}">
    <StackPanel>
        <ContentControl Content="{Binding}" 
                ContentTemplate="{Binding Source={x:Reference UserControlName},
                    Path=ItemHeaderTemplate}"/>
        <ContentControl Content="{Binding}" 
                ContentTemplate="{Binding Source={x:Reference UserControlName},
                    Path=ItemContentTemplate}"/>
    </StackPanel>
</ControlTemplate>

<DataTemplate x:Key="TestDataTemplate" DataType="{x:Type local:CustomUserControl}">    
    <ContentControl Template="{StaticResource TestControlTemplate}"/>    
</DataTemplate>

<Style x:Key="Styling" TargetType="{x:Type local:CustomUserControl}">
    <Setter Property="Template">
        <Setter.Value>          
            <ControlTemplate TargetType="{x:Type local:CustomUserControl}">
                
                <TabControl ItemsSource="{Binding Items}"
                            ContentTemplate="{StaticResource TestDataTemplate}"/>                    
            
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
  • Related