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

5

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?

Laguna answered 8/10, 2008 at 23:28 Comment(2)
You actually have 2 issues here, firstly DataTemplate does not support Interfaces the Second is GenericsPortal
For recent versions of the framework see #7574212Autoroute
G
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.

Garrulity answered 9/10, 2008 at 1:26 Comment(0)
D
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;
    }
}
Denton answered 9/10, 2008 at 10:39 Comment(2)
Probably a better and easier solutionSultan
I find this easier to understand than other Template Selector tutorials. Thank youStrephonn
G
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.

Garrulity answered 9/10, 2008 at 1:26 Comment(0)
F
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.

Folie answered 28/10, 2010 at 23:9 Comment(2)
This worked for me. It is a little strange to have a completely empty class, but it gets the job done.Weatherwise
I built a generic wrapper using this techique in https://mcmap.net/q/270724/-lazy-loading-wpf-tab-contentIconoclast
S
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.

Subedit answered 4/11, 2008 at 1:28 Comment(1)
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: dotnet.uservoice.com/forums/40583-wpf-feature-suggestions/…Subedit
A
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}>
Akerley answered 11/11, 2016 at 10:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.