Using Generic Types for MVVM

The MVVM pattern seems to have become the defacto standard for implementing cool WPF applications.

Rob Eisenberg suggested using Conventions to help enforce a separation of View and ViewModel. This to me smacks of Magic Strings which is just not nice.

Lately I’ve been playing with a different method of doing this using XAML Generics.

I’d like to share this with the community and see how you all feel about this approach.

The basic idea is that all Views should derive from a ViewBase where T specifies the type of ViewModel they should be built against.

For example:

Assume we have a ViewModel of type SomeViewModel and we want to create a view that represents it, all we have to do is create the following XAML:

<ve:ViewRoot x:Class="app.SomeView" x:TypeArguments="vm:SomeViewModel"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:ve="clr-namespace:ViewEngine;assembly=ViewFramework"
 >
</ve:ViewRoot>

and a Code Behind file:

public partial class SomeView : ViewRoot<SomeViewModel>
{
    public SomeView()
    {
        InitializeComponent();
    }
}

And bingo… our application will use SomeView everywhere SomeViewModel occurs in the visual tree.

Because of the data binding system we can now build our view referencing the view model, so assuming there is a Title property in the view model we can write this to a label like this:

<ve:ViewRoot x:Class="app.SomeView" x:TypeArguments="vm:SomeViewModel"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ve="clr-namespace:ViewEngine;assembly=ViewFramework"
 >
      <Label Content="{Binding Title}"/>
</ve:ViewRoot>

No naming conventions, no DataTemplate writing, just completely transparent intent.

Framework Wire-Up

Of course this doesn’t happen out of the box and requires a framework and a little global wiring up.

Let’s start with the simple bit, wiring it up, and then get to explaining how this works behind the scenes.

To make it simple I did away with the App.xaml startup system and went back to the old static main in Program.cs approach… I have no doubt it could be integrated into the app.xaml system if needed.

[STAThread]
public static void Main()
{
    var app = new Application();
    ViewEngine.Initialise(app, Assembly.GetExecutingAssembly());
    ViewEngine.Run(new WindowViewModel());
}

Simple huh?

Framework

Of course all the magic and challenge happens in the framework itself.

The basic principle is straightforward:

  • Scan the provided assembly and find all subclasses of ViewRoot.
  • Set up mappings between the ViewClasses and their models.
  • Wrap those in DataTemplates.
  • Load the data templates into the applications root ResourceDictionary.

The rest is handled by WPF for us.

There are however a couple of challenges to using Generics in WPF that make this more complex than one might expect.

Access to Properties

Not being able to access things like ResourceDictionary properties on the children of a generic type.

Fix: Create a 2 stage derivation of ViewRoot, the first called ViewRoot and the second called ViewRoot. This allows us to use the convention in the XAML and keeps the established XAML conventions runnings.

    public class RootView<T> : RootView { }
    public class RootView : ContentControl { }

Top Level Windows

Of course top level windows cannot be derived from ContentControl and must be derived from Window so we have to introduce some special case handling.

Its own assembly

As I discovered in one of my earlier posts on XAML it is important to build the ViewEngine in a separate assembly.

View Engine

Still it’s pretty plain sailing, in fact a whole ViewEngine class can be presented here. Obviously this isn’t commercial ready but it gives you a base to play with.

public interface IView { }
internal interface IViewRoot : IView { }
public class ViewRoot<T> : ViewRoot { }
public abstract class ViewRoot : ContentControlIViewRoot { }
public class WindowRoot<T> : WindowRoot { }
public abstract class WindowRoot : WindowIView { }

public static class ViewEngine
{
    private static Application sApp;

    public static void Initialise(Application app, params Assembly[] assembliesWithViews)
    {
        sApp = app;
        CreateViewViewModelMapping(assembliesWithViews);
    }

    public static Window Run(object viewModel)
    {
        var rootWindow = CreateRootWindow(viewModel);
        sApp.Run(rootWindow);
        return rootWindow;
    }

    private static void CreateViewViewModelMapping(IEnumerable<Assembly> assembliesWithViews)
    {
        foreach (var assemblyWithViews in assembliesWithViews)
            AddViewTypesToTemplates(assemblyWithViews.GetTypes());
    }

    private static void AddViewTypesToTemplates(IEnumerable<Type> potentialViewTypes)
    {
        foreach (var potentialViewType in potentialViewTypes)
            if (TypeImplementsValidViewInterface(potentialViewType))
                AddViewTypeMapping(potentialViewType);
    }

    private static bool TypeImplementsValidViewInterface(Type potentialViewType)
    {
        if (typeof(IView).IsAssignableFrom(potentialViewType))
            return potentialViewType.BaseType.GetGenericArguments().Length > 0;

        return false;
    }

    private static void AddViewTypeMapping(Type viewType)
    {
        var modelType = viewType.BaseType.GetGenericArguments()[0];

        if (typeof(IViewRoot).IsAssignableFrom(viewType))
        {
            var template = new DataTemplate(modelType);
            var visualFactory = new FrameworkElementFactory(viewType);
            template.VisualTree = visualFactory;

            sApp.Resources.Add(template.DataTemplateKey, template);
        }
        else
            sApp.Resources.Add(modelType, viewType);
    }

    private static Type FindViewForModelType(Type modelType)
    {
        return sApp.Resources[modelType] as Type;
    }

    private static Window CreateRootWindow(object viewModel)
    {
        Type viewType = FindViewForModelType(viewModel.GetType());
        if (viewType == null)
            throw new Exception(string.Format("No View for ViewModel type: {0}",
                         viewModel.GetType().Name));

        var view = Activator.CreateInstance(viewType);
        var window = view as Window;

        if (window == null)
            throw new Exception(string.Format("Could not initialise root WindowView({0})",
             viewModel.GetType().Name));
        window.DataContext = viewModel;

        return window;
    }
}

In case you also need an example MainWindow it is straightforward:

<ve:WindowRoot x:Class="app.MainWindow" x:TypeArguments="WindowViewModel" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:ve="clr-namespace:ViewEngine;assembly= ViewEngine " 
        Title="{Binding TitleProperty}" Height="300" Width="300"
        Content="{Binding ContentProperty}"
 >
    <ve:WindowRoot.Resources>
    </ve:WindowRoot.Resources>
</ve:WindowRoot>

Have fun and do let me know if you find any way to make this better…

see also

Your thoughts are valuable... post your thoughts on this topic...



7 + = eleven

community content (1 approved comment so far)
Click to expand

Basic XAML Part 4 (Generics)
Click to expand

By default XAML only supports generics in the definition of types (or in other words the root element). It does this by using more x: magic. The x: TypeArguments attribute lets the type derive from a generic class.

Lets take a simple base class with a single Generic type parameter T.

[System.Windows.Markup.ContentProperty("PropertyOne")]
public class SimpleBase<T>
{
    public object PropertyOne { get; set; }
    public T PropertyTwo { get; set; }
}

Then we can pretty easily create a XAML derivation of it so:

<custom:SimpleBase x:Class="TestType" x:TypeArguments="sys:String"
                   xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:custom="clr-namespace:ConsoleApplication1;assembly="
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
</custom:SimpleBase>

Apart from the odd way of specifying it (and honestly how else would we do it in xml?) this works.

However; all is not quite as it seems…

If you now try and set any kind of property on the object or any kind of nested content the compiler accept it but the runtime will blow up in your face with one of a bewildering array of possible error messages, most of which are as helpful as salt water on a boat.

Why? Turns out the reason is pretty obvious once you know. When the XAML is compiled it generates a new type, when we try to set the properties using InitialiseComponent() the runtime basically uses reflection under the covers, however it cannot correctly determine the class name because it seems to ignore the Generic Type Argument. I almost consider this a bug in the XAML system.

Anyway, as normal the most important thing is to know how to get around the problem. And the trick appears to be to only set Proeprties from XAML that are declared on a base class of our generic type, muchly irritating because in this way we loose half the power of generics but (as we will see in another post) still more useful than nothing.

[System.Windows.Markup.ContentProperty("PropertyOne")]
public class BasicBase
{
    public object PropertyOne { get; set; }
    public string PropertyTwo { get; set; }
}

public class SimpleBase<T> : BasicBase
{
}

I think therefore that it is fair to class the Generics support of XAML is pretty limited!

Side Note: Multiple type parameters can be specified in a comma seperated list.

see also

Your thoughts are valuable... post your thoughts on this topic...



+ 8 = fifteen

community content (no approved comments so far)
Click to expand

Basic Xaml Part 3 (its own Assembly)
Click to expand

So my last XAML basics post ran aground when I couldn’t get custom objects to accept pure textual content. Since that time I’m glad to say a concerted documentation reading effort unearthed a little gem: Pure textual content can only be used when the enclosing type is defined in a separate Assembly.

Why is that? Haven’t got the foggiest idea but suffice it to say it probably won’t be a significant issue in normal use since the only reason to use Xaml is likely to be working with some kind of framework (WPF, WCF, etc) that will come wrapped in its own Assembly anyhow.

So having made the tweak to put my SimpleBase class in a separate VS Project (==Assembly) and licked the text issue I want to press on probing the rest of the xaml-space.

Event Handlers

First up on the hit list were EventHandler wiring, e.g.

<custom:SimpleBase x:Class="TestType"
                    xmlns:custom="clr-namespace:ConsoleApplication1;assembly="
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                   OnClick="HelpHandler">
</custom:SimpleBase>

after some Reflection I can report that it works just like you would expect – it adds a delegate to the HelpHandler method to the SimpleBases OnClick event when you call the InitialiseComponent method of the SimpleBase instance. Nothing fancy going on here – nice :)

x:Name

Then we get on to a bit of cleverness, introducing variables/properties using x:Name. This is kind of strange because it gives a completely different meaning to an attribute than normal – normal attributes in xaml are (best I can tell) evaluated during the InitialiseComponent call. x:Name however actually adds new members to the type being defined (further conflating the two concepts).

What type of member does it add? Actually it seems to Infer the type it needs to be from the type of the object being named, thus <Button x:Name=”Quest”/> will create an internal member variable of Type Button called Quest. The value of this variable will still only be set during the call to InitialiseComponent.

OK, accepted, but what happens if we name a button inside a grid inside a xaml element? To find out I created some simple types and did some more reflecting.

<custom:SimpleBase x:Class="TestType"
                    xmlns:custom="clr-namespace:ConsoleApplication1;assembly="
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <custom:SimpleTypeOne x:Name="SimpleVarOne">
        <custom:SimpleTypeTwo x:Name="SimpleVarTwo">
        </custom:SimpleTypeTwo>
    </custom:SimpleTypeOne>
</custom:SimpleBase>

The result is that both SimpleVarOne and SimpleVarTwo are declared as internal member variables of the TestType class. Or in other words nesting of the XAML appears to have no impact on the declaration of the members. In many respects I’m not sure what else it could mean but it is certainly interesting and worth keeping in mind.

Summing up

I have one more thing to investigate in this line for which you should tune back in next time, however XAML is now more of an open book to me and hopefully you. To summarise:

Xaml is a conflation of Type Derivation and Property Assignment.

The Root Element specifies the Base Type and via the x:Class attribute the name of the New Type.

Any element can be named with the x:Name attribute and this has the effect of creating an internal member variable with the given name and the Type of the element on which it is applied.

The Type is compiled at compile time, but the properties are not set until an instance is a) created, and b) initialised with a call to InitialiseComponent().

The Type created by the xaml is automatically marked as Partial, this is what allows us to have the code-behind file. The code behind is nothing more than another partial class with the same name.

If you look at the auto-generated xaml code behind file you will find the call to InitialiseComponent slipped into the constructor where it can perform its magic on type instantiation.

see also

Your thoughts are valuable... post your thoughts on this topic...



× two = 6

community content (no approved comments so far)
Click to expand

Basic XAML Part 2
Click to expand

What does it mean then to declare nested xaml elements inside our simple class then?

e.g.

<custom :SimpleBase x:Class="TestType"
                    xmlns:custom="clr-namespace:ConsoleApplication1;assembly="
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
Test
</custom>

Well, if you try it you will find this isn’t possible. And indeed I also can’t figure out what it might mean. And yet if you open any book on XAML you will find things like <button>Click Me</button>. Hmm.

The answer lies in the definition of the base type (in our case SimpleBase).

In order to be able to assign direct content such as this we must declare a property on the base type and add a [System.Windows.Markup.ContentProperty("...")] attribute to it where the … is replaced with the name of the property that should take the content.

    [System.Windows.Markup.ContentProperty("PropertyOne")]
    public class SimpleBase
    {
        public string PropertyOne { get; set; }
        public string PropertyTwo { get; set; }
    }

Now in xaml we can assign the value PropertyOne using the following syntax:

<custom :SimpleBase x:Class="TestType"
                    xmlns:custom="clr-namespace:ConsoleApplication1;assembly="
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">
    <sys :String>Test</sys>
</custom>

Interestingly it still isn’t possible to directly enter the string ‘Test’, only to assign a sys:String object containing Test. Putting Test directly in the xml leads to a compile error.

see also

Your thoughts are valuable... post your thoughts on this topic...



six × 1 =

community content (no approved comments so far)
Click to expand

Most basic XAML
Click to expand

As part of gaining a deeper understanding of WPF I felt it was important to gain a deeper understanding of how XAML works and what it actually means. Therefore I will attempt to use XAML independantly of WPF…

Naturally my first question was “What is the simplest XAML file I can conceive of?”.

What I came up with was this:

<sys:Object xmlns:sys="clr-namespace:System;assembly=mscorlib">
</sys:Object>

If you create a new Console Application and add a code file called “Test.xaml” then enter the above code it will build. Yipee.

But what does it mean?

I was assuming it creates a new type extending Object, unfortunately since I couldn’t specify a type name I had no way to test that assumption. So from here I wanted to extend my question to include “and I can reflect on”.

A bit more playing landed me this:

<sys:Object x:Class="TestType"
            xmlns:sys="clr-namespace:System;assembly=mscorlib"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</sys:Object>

This allows me to create a new type called TestType that extends Object. How do I know this? I can access it from my Main function by calling:

TestType t = new TestType();

Great… so I fire up reflector expecting to find a decompilation giving me:

using System;

namespace ConsoleApplication1
{
    public class TestType : Object
    {
    }
}

But, no, it is a load more complex than that. What I actually get to see is:

[GeneratedCode("PresentationBuildTasks", "4.0.0.0")]
public class TestType : IComponentConnector
{
    // Fields
    private bool _contentLoaded;

    // Methods
    [DebuggerNonUserCode]
    public void InitializeComponent()
    {
        if (!this._contentLoaded)
        {
            this._contentLoaded = true;
            Uri resourceLocator = new Uri("/ConsoleApplication1;component/basictest.xaml",
 UriKind.Relative);
            Application.LoadComponent(this, resourceLocator);
        }
    }

    [EditorBrowsable(EditorBrowsableState.Never), DebuggerNonUserCode]
    void IComponentConnector.Connect(int connectionId, object target)
    {
        this._contentLoaded = true;
    }
}

Holy batman and robin. That was not quite what I expected, although the good news is it does indeed create a new type that derives from Object it also implements IComponentConnector.

However, when you stop and think a little it appears that XAML is in fact the conflation of 2 quite different concepts. The first is class definition (what we expected) and the second is Property Assignment (the extra stuff).

I’m assuming therefore that calling InitializeComponent will traverse through the inner XAML setting all the properties of the object.

Unfortunately using Object as the root does not let me test this, so I quickly whip up a simple base class:

using System;

namespace ConsoleApplication1
{
    public class SimpleBase
    {
        public string PropertyOne { get; set; }
        public string PropertyTwo { get; set; }
    }
}

And then modify my XAML to:

<custom:SimpleBase x:Class="TestType"
                    xmlns:custom="clr-namespace:ConsoleApplication1;assembly="
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</custom:SimpleBase>

NOTE: Notice the use of “;assembly=” at the end of the namespace definition, this is the most conformant way of specifying “look in the current assembly”. It also has the added advantage of making it easy to re-direct later if you refactor the type into a new assembly.

OK, so with that done I can again compile my program and I expect it to be just creating a new instance of TestType (derived from SimpleBase) but not yet setting any properties.

Score one for careful thinking… it does exactly that.

Next up I add a call to on my object t.

TestType t = new TestType();
t.InitialiseComponent();

I would expect this to change nothing since I don’t actually specify any of the properties in XAML.

And indeed it doesn’t, however it does change the hidden field “_contentLoaded” to true.

So for the next test the obvious step is to specify some values for the properties and see if they get set a] before the call to InitialiseComponent or b] after (or c] never…).

<custom:SimpleBase x:Class="TestType"
                    xmlns:custom="clr-namespace:ConsoleApplication1;assembly="
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            PropertyOne="Hello"
            PropertyTwo="World"
>
</custom:SimpleBase>

I can tell you now that the expected happens and the values are set after the call to initialise component.

So the first step of understanding XAML is completed.

What is XAML?
It is a conflation of Type Derivation and Property Setting.

Urg, I hate conflations. But OK, it is what it is and that is the future of programming according to MS.

Join me next time to dig deeper into XAML and how to use it.

see also

Your thoughts are valuable... post your thoughts on this topic...



+ 2 = four

community content (no approved comments so far)
Click to expand
Design and Content © Copyright Duncan Kimpton 2010