Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / WPF

Generic Support In XAML

Rate me:
Please Sign up or sign in to vote.
4.89/5 (5 votes)
17 Jun 2009CPOL4 min read 43.3K   10   9
Generic support in XAML

I have worked with XAML/WPF for a while now, but it wasn’t until recently I thought about generic support in XAML. I had no idea if it was possible, so I decided to have a quick look into this.

Luckily, the WPF team thought about this and did actually enable WPF developers the correct mechanisms to fully support generics from within XAML.

The first thing you need to know is that a typical WPF window is made up of 2 partial classes, the code behind and the XAML, then at compilation time, a combined file known as the generated file is formed with the combination of the code behind file and the XAML file.

37317/build-thumb.jpg

So in order to correctly support generics for a file that is made up of both code behind and XAML, we MUST ensure that both parts of the file are using generic type arguments. This should become clearer as we move on to see a small example.

In order to demonstrate the support for generics in XAML, let us consider a small example, which requires the following arrangement:

  1. We have a common window base class that is generic
  2. We inherit from the common window base class, but we then need to provide generic arguments within the code behind and the XAML in order to make it a generic type.

That’s basically what we are trying to do. In order to demonstrate this, I have constructed a small demo app that carries out the following:

I have an abstract base class called LoggingStrategy which looks like this:

C#
1:  using System;
2:
3:  namespace GenericViews
4:  {
5:      public abstract class LoggingStrategy
6:      {
7:          public abstract void LogText(string textToLog);
8:      }
9:  }

I then have a specific class that overrides the LogText(…) method, this is as follows:

C#
 1:  using System;
 2:  using System.Windows;
 3:
 4:  namespace GenericViews
 5:  {
 6:      public class MessageBoxLogger : LoggingStrategy
 7:      {
 8:          public override void LogText(string textToLog)
 9:          {
10:              MessageBox.Show(textToLog);
11:          }
12:      }
13:  }

Then within the main App class that you get, if you start a new WPF application in VS2008, I have created a simple Dictionary lookup for a Type to get the correct logging strategy to use. The idea being that the Type will come from a generic argument provided by the view, as we will see in just a minute.

For now, all you need to know is that there is a lookup against a Type that returns a LoggingStrategy inherited object.

C#
 1:  using System;
 2:  using System.Collections.Generic;
 3:  using System.Windows;
 4:
 5:  namespace GenericViews
 6:  {
 7:      public partial class App : Application
 8:      {
 9:          public static Dictionary<Type, LoggingStrategy> Loggers =
10:              new Dictionary<Type, LoggingStrategy>();
11:
12:
13:          static App()
14:          {
15:              Loggers.Add(typeof(MessageBoxLogger), new MessageBoxLogger());
16:          }
17:      }
18:  }

So we have pretty much covered all the demo stuff, now it's just a question of dealing with the actual generic stuff. In the attached demo, I have provided a small base class for views, and the base class is generic, and will lookup the correct LoggingStrategy inherited object, from the Dictionary stored in the static App Dictionary.

Here is the base class for the views.

C#
 1:  using System;
 2:  using System.Collections.Generic;
 3:  using System.Windows;
 4:
 5:  namespace GenericViews
 6:  {
 7:      public class ViewBase<T> : Window where T : LoggingStrategy
 8:      {
 9:          public LoggingStrategy CurrentLogger { get; private set; }
10:
11:          public ViewBase()
12:          {
13:              CurrentLogger = App.Loggers[typeof(T)];
14:          }
15:      }
16:  }

It can be seen that this base class expects a generic argument T in order to work. T must inherit from LoggingStrategy. What then happens is generic argument is used to obtain the correct LoggingStrategy inherited object from the Dictionary stored in the static App Dictionary.

Ok we are nearly there, all we now have to do is make a custom window which inherits from the ViewBase<T>, and passes a generic argument.

Let’s start with the easy part, the code behind. This part is dead easy, we simply use standard generic stuff, as we would any other generic class:

C#
 1:  using System;
 2:  using System.Collections.Generic;
 3:  using System.Windows;
 4:
 5:
 6:  namespace GenericViews
 7:  {
 8:
 9:      public partial class MessageLoggingWindow : ViewBase<MessageBoxLogger>
10:      {
11:          public MessageLoggingWindow() : base()
12:          {
13:              InitializeComponent();
14:          }
15:
16:          private void Button_Click(object sender, RoutedEventArgs e)
17:          {
18:              base.CurrentLogger.LogText(
19:                  String.Format("Sometext which was logged at {0}",
20:                  DateTime.Now.ToShortTimeString()));
21:          }
22:      }
23:  }

Now that the easy part is done, why don’t we focus our efforts on the XAML part.

C#
1:  <local:ViewBase
2:      x:Class="GenericViews.MessageLoggingWindow"
3:      x:TypeArguments="local:MessageBoxLogger"
4:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
5:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
6:      xmlns:local="clr-namespace:GenericViews">
7:  …
8:  …
9:  </local:ViewBase>

Now, what is going on here exactly. Well a couple of things actually, let’s go through them.

  1. x:Class: That’s the name of the class, this matches the code behind namespace and class name
  2. xmlns:local: This is a namespace alias that allows use to use local: in the XAML to point to classes in the GenericViews namespace
  3. <local:ViewBase…/>: Means we are using GenericViews.ViewBase<T> as the base class for this new Window, but wait what about the generic argument. Well that’s step 4.
  4. x:TypeArguments: This is where the generic value gets plugged in. So we now have a window declared in XAML which is really GenericViews.ViewBase<MessageBoxLogger>

That’s it…..I know my example is not exactly real world, but that was not the point of all this, I simply wanted to show you how to create generics in XAML, and to that end, this post should do just that.

Here is a small demo app, that creates a generic window with a MessageBoxLogger, and when run looks like this:

37317/image-thumb.png

Enjoy!

NOTE

A reader of this blog also pointed me to another entry by Mike Hillberg, which covers a lot of other generic support in XAML, have a look here for that, it's quite cool.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
GeneralIt is for root element ONLY Pin
Maciej Pilichowski10-Sep-10 5:15
Maciej Pilichowski10-Sep-10 5:15 
GeneralRe: It is for root element ONLY Pin
Sacha Barber10-Sep-10 5:17
Sacha Barber10-Sep-10 5:17 
QuestionHow did I miss it? Pin
Kunal Chowdhury «IN»26-Aug-10 23:05
professionalKunal Chowdhury «IN»26-Aug-10 23:05 
AnswerRe: How did I miss it? Pin
Sacha Barber26-Aug-10 23:42
Sacha Barber26-Aug-10 23:42 
GeneralRe: How did I miss it? Pin
Kunal Chowdhury «IN»27-Aug-10 0:34
professionalKunal Chowdhury «IN»27-Aug-10 0:34 
GeneralRe: How did I miss it? Pin
Sacha Barber27-Aug-10 0:49
Sacha Barber27-Aug-10 0:49 
QuestionAlmost same problem with different namespaces of base Classes and Generic type Need Help!!! Pin
Salman Mustansar28-Jul-10 0:32
Salman Mustansar28-Jul-10 0:32 
Hey Author,
Nice article i was looking for solution of almost same problem. The difference is that in my case Base class and generic type has different namespaces e.g
if i map my XAml to your article it will looks like

x:Class="Projects.IEM.Modules.SecondMod.Views.MainPage"
x:TypeArguments="MyGenericEntity:Employee"
xmlns:local="clr-namespace:BaseFramework.Common.GUIControls;assembly=GUIControls"
xmlns:MyGenericEntity="clr-namespace:SecondModEntities"

it resolves the error of Xaml and again shows the Page in designer but didn't effect on build generated file. it didn't append my Generic type to it. So i am still facing a build error with that generation

public partial class MainPage : BaseFramework.Common.GUIControls.SSIMasterPageBase {

Error: using BaseFramework.Common.GUIControls.SSIMasterPageBase<t> requires 1 type arguments...

Thanks in advance just need to know what should i do further to avoid that exception.

Regards
Salman Mustansar
AnswerRe: Almost same problem with different namespaces of base Classes and Generic type Need Help!!! Pin
Sacha Barber28-Jul-10 0:52
Sacha Barber28-Jul-10 0:52 
GeneralExactly what I was looking for Pin
PHDENG8124-Jul-09 10:25
PHDENG8124-Jul-09 10:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.