user17162137
user17162137

Reputation:

WPF Data Template Selector based on Generic Types

I've seen a lot of examples how to use Data Template Selector to show different controls according to the x:TargetType. I want to create an items control that show a RadioButton, TextBox or TextBlock according to the class tyepe. My class could be like this:

public class MyExample<T>
{
   public string Name {get;set;}
   public Type Type => TypeOf(T)
   public T Value {get;set;}
}

I know that the Xaml can't recognize generics and I don't want to create a markup extension for supporting generics, I want to keep it simple. I don't want to create a concrete class for each type. I know that I can use a Data Trigger to set the content template according to a property (for example type name, or Type Type) but I think that should be an easier way using a Data Template Selector. Can I use the TargetType on the Type Property instead of class type?

Upvotes: 0

Views: 1433

Answers (2)

user17162137
user17162137

Reputation:

This worked for me:

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" xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
        mc:Ignorable="d"
        Background="Red"
        d:DataContext="{d:DesignInstance local:ViewModel}"
        WindowStartupLocation="CenterScreen"
        Title="MainWindow" d:Height="100" d:Width="150" Width="300" Height="300">
    <Window.Resources>
        <Style x:Key="TextBlockStyle" TargetType="TextBlock">
            <Setter Property="TextAlignment" Value="Center"/>
        </Style>
        <DataTemplate x:Key="DefaultTemplate">
            <TextBlock Text="{Binding Value}" Style="{StaticResource TextBlockStyle}"/>
        </DataTemplate>
        <DataTemplate x:Key="NumberTemplate">
            <syncfusion:UpDown Name="NumericUpdDown" Value="{Binding Value}"/>
        </DataTemplate>
        <DataTemplate x:Key="TextTemplate">
            <TextBlock Text="{Binding Value}" Style="{StaticResource TextBlockStyle}" />
        </DataTemplate>
        <DataTemplate x:Key="CheckBoxTemplate">
            <CheckBox IsChecked="{Binding Value}" HorizontalAlignment="Center"/>
        </DataTemplate>
        <DataTemplate x:Key="MyDataTemplate">
            <ContentControl Content="{Binding}">
                <ContentControl.Style>
                    <Style TargetType="{x:Type ContentControl}">
                        <!-- Default Template -->
                        <Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}" />
                        <!-- Triggers to change Template -->
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Type.Name}" Value="Int32">
                                <Setter Property="ContentTemplate" Value="{StaticResource NumberTemplate}" />
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Type.Name}" Value="String">
                                <Setter Property="ContentTemplate" Value="{StaticResource TextTemplate}" />
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Type.Name}" Value="Boolean">
                                <Setter Property="ContentTemplate" Value="{StaticResource CheckBoxTemplate}" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </ContentControl.Style>
            </ContentControl>
        </DataTemplate>
    </Window.Resources>
    <ItemsControl 
        ItemsSource="{Binding Definitions}"
        ItemTemplate="{StaticResource MyDataTemplate}">
    </ItemsControl>
</Window>

The reason that I don't want concrete type class is avoid adding a lot of code, everytime that I add a new type. What do you think about this solution ?

Upvotes: 1

mm8
mm8

Reputation: 169400

Can I use the TargetType on the Type Property instead of class type?

No.

The obvious solution would be to create a sub-type and a corresponding DataTemplate for each type of T. The implementation for each sub-type would be a one-liner:

public class MyExampleInt : MyExample<int> { }
public class MyExampleString : MyExample<string> { }

If you don't want to create concrete sub-types for whatever reason, you could use a DataTemplateSelector to select a template based on the type of each MyExample<T>:

public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
    switch (item)
    {
        case MyExample<int> i:
            return IntTemplate;
        case MyExample<string> s:
            return StringTemplate;
    }

    return null;
}

Upvotes: 4

Related Questions