Have you ever tried to create a DataTemplate for a Generic Class? During last week I had to battle this issue and it is allot more complicated then it sounds. As far as I can tell Creating DataTemplates for Generic classes is impossible. There is some kind of workaround but it is not all that good. OK lets get down to business. This is the class we are trying to template

   1: public class GenericClass<T>
   2: {
   3:     private T m_Val;
   4:  
   5:     public T Val
   6:     {
   7:         get { return m_Val; }
   8:         set { m_Val = value; }
   9:     }
  10: }

The code for the Window was this:

   1: public Window1()
   2:         {
   3:             InitializeComponent();
   4:             GenericClass<string> s = new GenericClass<string>();
   5:             s.Val = "Tests";
   6:             this.DataContext = s;
   7:         }
   8:     

So far so good. The problem starts in the Xaml, since we want to bind a control to a single class our binding expression is this:

   1: <ContentControl Content="{Binding}"></ContentControl>

The problem is in the DataTemplate. In order for it to work we have to specify the type of the class, but what is the type of the class? We can’t write GenericClass<T> because the "<" and ">" will close the tags and what the hell is T (this is the Xaml compiler talking, not me), We can’t write GenericClass<string> because the "<" and ">" will cause problems again.

Here is when I stopped and started to search in Google. A futile effort because when you enter a search phrase with Generic, Class and DataTemplate together you will get tons of tutorials about how to create DataTemplates for Generic Collections. After trying for an hour I gave up and had a brilliant idea! why not use the Binding Expression from before and NO template to see what is printed out to the screen! here is what I got:

DataTemplate Test

Interesting… I guess the number after the "`" represents the number of parameter and inside the brackets you see the type. Great, I thought to myself and quickly typed it in the DataType field of the DataTemplate so the DataTemplate looked like this:

   1: <DataTemplate DataType="{x:Type data:GenecicClass.GenericClass`1[System.String]}">
   2:             <TextBlock Text="{Binding Val}"></TextBlock>
   3:         </DataTemplate>
   4:         

only to get the following error from Visual Studio:

Error    1    Cannot find the type ‘data:GenecicClass.GenericClass`1[System.String]‘. Note that type names are case sensitive. Line 7 Position 23.    D:\VS2005Projects\BlogTests\GenericClass\GenericClass\Window1.xaml    7    23    GenericClass

Bummer!

I tried almost any combination of the name and no success…

I did not give up and played around with it some more. Despite Shahar’s post about DataTemplates for Interfaces not being supported. I decided to try using an interface. This was the code:

   1: public class GenericClass<T> : VisibileInterface
   2:     {
   3:         private T m_Val;
   4:  
   5:         public T Val
   6:         {
   7:             get { return m_Val; }
   8:             set { m_Val = value; }
   9:         }
  10:     }
  11:  
  12:     public interface VisibileInterface
  13:     {
  14:  
  15:     }

And the Template:

   1: <DataTemplate DataType="{x:Type data:VisibileInterface}">
   2:             <TextBlock Text="{Binding Val}"></TextBlock>
   3:         </DataTemplate>

And still nothing. Same result as before.

I was this close on loosing the Generic Class but I decided to sit down and think of the problem at hand.

Here is me thinking…

It seems that there is no possibility to define the DataType, so I will have to specify a template for the ContentControl without specifying the Type. The only option is the Template Attribute which accepts ControlTemplates. I Quickly created a ControlTemplate and specified the Template attribute to point to it:

   1: <Window.Resources>
   2:         <ControlTemplate x:Key="temp">
   3:             <TextBlock Text="{Binding Val}"></TextBlock>
   4:         </ControlTemplate>
   5:  
   6:     </Window.Resources>
   7:     <Grid>
   8:         <ContentControl Content="{Binding}" 
   9:                         Template="{StaticResource temp}">
  10:         </ContentControl>
  11:     </Grid>

And check this out! I got exactly what I wanted:

Generic Class ControlTemplate

All that work for a crappy window with "Tests" written in it…

Hope it helps

Amit

Tags :

8 Responses to “How To Create a WPF Template For a Generic Class”


  1. James Curran

    Said on July 29, 2008 :

    Did you try:

    class StringGenericClass : GenericClass
    {
    }

  2. martin

    Said on July 30, 2008 :

    @James: That’s more than stupid. The classes should be generic.
    One thing to try would to to create a non genric class that omits objects…

  3. Amit

    Said on July 30, 2008 :

    @ James
    I thought of that but, it is the same as using a non generic class, so I dont think it is an improvment

  4. James Curran

    Said on July 30, 2008 :

    Let’s look at the WPF:
    DataType=”{x:Type data:GenecicClass.GenericClass`1[System.String]}”

    By the time it gets here, it’s a concrete class, regardless of the route it took getting to that place.

    All that is doing is giving it a name that more palatable to WPF.

    Also the editor messed up my code. It should be
    class StringGenericClass : GenericClass<string>
    {
    }

  5. Amit

    Said on July 31, 2008 :

    @ James

    I get what you are saying, but then I will have several templates for the same class which is back to square one, my goal was to have just one.

    Your solution will be great if the T is a complex type.

  6. Bragi

    Said on July 31, 2008 :

    I tend to solve this by not using the DataType attribute but only x:Key with a DataTemplate and if it is still required to pick 1 out of multiple templates, I use a template selector.

  7. Amit

    Said on July 31, 2008 :

    @ Bragi

    The only problem is that ContentControl has no ItemTemplateSelector Property or any other way to specify the selector to it.

  8. Terry Bondy

    Said on September 3, 2009 :

    FWIW, you have misspelled the type name in one of your attempts above (i.e. you have DataType=”{x:Type data:GenecicClass.GenericClass`1[System.String]}”). It doesn’t seem to make a difference however if you spell it right.

Post a Comment