75

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?

Askolein
  • 3,250
  • 3
  • 28
  • 40
Matt Hamilton
  • 200,371
  • 61
  • 386
  • 320
  • You actually have 2 issues here, firstly DataTemplate does not support Interfaces the Second is Generics – MikeT Jul 16 '14 at 11:23
  • For recent versions of the framework see http://stackoverflow.com/questions/7573712/how-to-specify-generic-type-argument-in-xaml – Ian Ringrose Sep 12 '15 at 17:51

5 Answers5

32

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;
    }
}
Samuel Slade
  • 8,405
  • 6
  • 33
  • 55
Ian Oakes
  • 10,153
  • 6
  • 36
  • 47
24

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.

Dan
  • 7,286
  • 6
  • 49
  • 114
ageektrapped
  • 14,482
  • 7
  • 57
  • 72
22

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.

Claudiu Mihaila
  • 1,322
  • 8
  • 17
7

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.

cplotts
  • 13,941
  • 9
  • 55
  • 66
  • 5
    XAML 2009 is only supported (as of .NET 4.0, WPF 4.0) in loose xaml files. That is, Blend, Cider (the Visual Studio designer), and compiled BAML (which is what your embedded xaml gets compiled into) ... do not support the new syntax. Hopefully, this will change in a future version of WPF. See the following link and vote: http://dotnet.uservoice.com/forums/40583-wpf-feature-suggestions/suggestions/478860-support-xaml2009-throughout?ref=title – cplotts Sep 24 '10 at 21:00
0

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}>
Mike de Klerk
  • 11,906
  • 8
  • 54
  • 76