Can I specify a generic type in XAML (pre .NET 4 Framework)?

C#WpfXamlGenerics

C# Problem Overview


In XAML I can declare a DataTemplate so that the template is used whenever a specific type is displayed. For example, this DataTemplate will use a TextBlock to display the name of a customer:

<DataTemplate DataType="{x:Type my:Customer}">
    <TextBlock Text="{Binding Name}" />
</DataTemplate>

I'm wondering if it's possible to define a DataTemplate that will be used any time an IList<Customer> is displayed. So if a ContentControl's Content is, say, an ObservableCollection<Customer> it would use that template.

Is it possible to declare a generic type like IList in XAML using the {x:Type} Markup Extension?

C# Solutions


Solution 1 - C#

Not directly in XAML, however you could reference a DataTemplateSelector from XAML to choose the correct template.

public class CustomerTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item,
                                                DependencyObject container)
    {
        DataTemplate template = null;
        if (item != null)
        {
            FrameworkElement element = container as FrameworkElement;
            if (element != null)
            {
                string templateName = item is ObservableCollection<MyCustomer> ?
                    "MyCustomerTemplate" : "YourCustomerTemplate";

                template = element.FindResource(templateName) as DataTemplate;
            } 
        }
        return template;
    }
}

public class MyCustomer
{
    public string CustomerName { get; set; }
}

public class YourCustomer
{
    public string CustomerName { get; set; }
}

The resource dictionary:

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    >
    <DataTemplate x:Key="MyCustomerTemplate">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="150"/>
            </Grid.RowDefinitions>
            <TextBlock Text="My Customer Template"/>
            <ListBox ItemsSource="{Binding}"
                     DisplayMemberPath="CustomerName"
                     Grid.Row="1"/>
        </Grid>
    </DataTemplate>

    <DataTemplate x:Key="YourCustomerTemplate">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="150"/>
            </Grid.RowDefinitions>
            <TextBlock Text="Your Customer Template"/>
            <ListBox ItemsSource="{Binding}"
                     DisplayMemberPath="CustomerName"
                     Grid.Row="1"/>
        </Grid>
    </DataTemplate>
</ResourceDictionary>

The window XAML:

<Window 
    x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" 
    Height="300" 
    Width="300"
    xmlns:local="clr-namespace:WpfApplication1"
    >
    <Grid>
        <Grid.Resources>
            <local:CustomerTemplateSelector x:Key="templateSelector"/>
        </Grid.Resources>
        <ContentControl 
            Content="{Binding}" 
            ContentTemplateSelector="{StaticResource templateSelector}" 
            />
    </Grid>
</Window>

The window code behind:

public partial class Window1
{
    public Window1()
    {
        InitializeComponent();
        ObservableCollection<MyCustomer> myCustomers
            = new ObservableCollection<MyCustomer>()
        {
            new MyCustomer(){CustomerName="Paul"},
            new MyCustomer(){CustomerName="John"},
            new MyCustomer(){CustomerName="Mary"}
        };

        ObservableCollection<YourCustomer> yourCustomers
            = new ObservableCollection<YourCustomer>()
        {
            new YourCustomer(){CustomerName="Peter"},
            new YourCustomer(){CustomerName="Chris"},
            new YourCustomer(){CustomerName="Jan"}
        };
        //DataContext = myCustomers;
        DataContext = yourCustomers;
    }
}

Solution 2 - C#

Not out of the box, no; but there are enterprising developers out there who have done so.

Mike Hillberg at Microsoft played with it in this post, for example. Google has others of course.

Solution 3 - C#

You also can wrap your generic class in a derived class that specifies the T

public class StringList : List<String>{}

and use StringList from XAML.

Solution 4 - C#

aelij (the project coordinator for the WPF Contrib project) has another way to do it.

What's even cooler (even though it is sometime off in the future) ... is that XAML 2009 (XAML 2006 is the current version) is going to support this natively. Check out this PDC 2008 session for info on it and more.

Solution 5 - C#

Quite defeats the purpose of a generic, but you could define a class that derives from the generic like so, with the sole purpose of being able to use that type in XAML.

public class MyType : List<int> { }

And use it in xaml e.g. like

<DataTemplate DataType={x:Type myNamespace:MyType}>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMatt HamiltonView Question on Stackoverflow
Solution 1 - C#Ian OakesView Answer on Stackoverflow
Solution 2 - C#ageektrappedView Answer on Stackoverflow
Solution 3 - C#Claudiu MihailaView Answer on Stackoverflow
Solution 4 - C#cplottsView Answer on Stackoverflow
Solution 5 - C#Mike de KlerkView Answer on Stackoverflow