Christopher Bennage

Sponsors

The Lounge

Wicked Cool Jobs

Syndication

The Influence of MVC on my WPF

I was introduced to MVC as a pattern for Web development through Castle MonoRail. From there I studied a bit of Ruby on Rails (not enough) as well as the general philosophy of 37signals.

As I mentioned before, I thought I was moving away from Web development. However, the dive into ASP.NET MVC that Silver Arcade brought about has roped me back in.

My work during the day (and some nights too) is still WPF though. I’ve discovered that MVC thinking has seeped into my WPF development.

Convention Over Configuration

A characteristic of most MVC frameworks is Convention Over Configuration. In short, it means that your “environment” makes assumptions about what you want and that you only need to be explicit about the exceptions.

A practical example of this is the way that the View for an Action is inferred from the name of the Controller and Action. After creating your Action, you create a folder for the controller and and view that matches the name of the action. It just works. If you need to make exception, you can.

An Example in WPF

In our WPF development we employ a number of Separated Presentation patterns. One of the more common patterns we use is MVP (in particular, the flavor of MVP implemented in Caliburn, which is really Supervising Controller).

Imagine a simple contact manager application. We can add new contacts, view a list of all contacts, and open individual contacts for viewing or editing. We can open as many individual contacts as we like.

mvc

Let’s say that there are buttons to open up the “Add New” view and “All Contacts” view. We can open an existing contact by double-clicking on them in the “All Contacts” view.

my imaginary solutionMy solution setup for this imaginary (and highly contrived) application might look like this.

ApplicationPresenter would be the root object of my UI. It would have an ObservableCollection of IPresenter that would be bound to a TabControl in Shell.xaml. Likewise, ApplicationPresenter will have a CurrentPresenter of IPresenter that will be bound to the SelectedItem on the TabControl.

Shell.xaml is my only window in this example. All of the other views are user controls. I named it “shell” because I think of it as the outermost level of the views. Shell’s data context is set to ApplicationPresenter.

I’m not going to dive too much deeper into this, because I want to get to my real point.

My presenters don’t know anything about their views. It’s up to data binding to render each presenter with the correct view. This means that I need to create data templates for each of the presenters. Not such a big deal in a small application, but it can get tedious as the application grows.

Hmmm… This structure is beginning to look a lot like an MVC app. Perhaps I can have it automatically infer my data templates for me?

Some Code

Yes we can. Here’s a very naiveté bit of code that will do just that. It makes some big assumptions:

  • all of your presenters implement IPresenter
  • all of your presenters are in a namespace containing “.Presenters”
  • all of your views are in an analogous namespace containing in “.Views”

Again, this is not production ready code, just my spike to prove the idea works. Please feel free to make suggestions.

public static class MvpConfiguration
{
    /// <summary>
    /// Creates a set of data templates pair to presenter classes based on naming conventions.
    /// </summary>
    /// <remarks>If a data template is already present in the resources, then it is skipped.</remarks>
    /// <param name="resources">The ResourceDictionary that the data templates for the views will be added to.</param>
    public static void InferViewsFromPresenters(ResourceDictionary resources)
    {
        var lookup = MatchPresentersToViews();

        foreach (var pair in lookup)
        {
            var template = CreateDataTemplate(pair.Value, pair.Key);
            if (resources.Contains(template.DataTemplateKey)) continue;
            resources.Add(template.DataTemplateKey, template);
        }
    }

    private static Dictionary<Type, Type> MatchPresentersToViews()
    {
        var presenters = from type in Assembly.GetExecutingAssembly().GetTypes()
                         where typeof (IPresenter).IsAssignableFrom(type)
                         select type;

        var first = presenters.FirstOrDefault();
        if (first == null) throw new Exception("I expected to find at least one presenters!");

        string viewNamespace = first.Namespace
            .Replace(".Presenters", ".Views");
        viewNamespace = viewNamespace.Substring(0, viewNamespace.IndexOf(".Views")+6);
        //yeah, that was hackish

        var views = from view in Assembly.GetExecutingAssembly().GetTypes()
                    where !string.IsNullOrEmpty(view.Namespace)
                          && view.Namespace.StartsWith(viewNamespace)
                    select view;

        var table = new Dictionary<Type, Type>();

        foreach (var view in views)
        {
            var presenter = GetPresenterForView(view, presenters);
            if(presenter == null) continue;

            table.Add(presenter,view);
        }

        return table;
    }

    private static Type GetPresenterForView(Type type, IEnumerable<Type> presenters)
    {
        string name = type.Name.Replace("View", "Presenter");
        var presenter =
            presenters.Where(p => p.Name == name).FirstOrDefault();
        return presenter;
    }

    private static DataTemplate CreateDataTemplate(Type viewType, Type dataType)
    {
        var template = new DataTemplate(dataType);

        var factory = new FrameworkElementFactory(viewType);
        template.VisualTree = factory;
        return template;
    }
}

To use this, in your App.xaml.cs within OnStartup() add:

MvpConfiguration.InferViewsFromPresenters(Resources);

What do you think? Am I totally nuts?

Oh, and I should also point out that FrameworkElementFactory is actually deprecated. :-( Caveat emptor.


Posted 05-06-2009 3:48 PM by Christopher Bennage

[Advertisement]

Comments

Ryan Riley wrote re: The Influence of MVC on my WPF
on 05-06-2009 7:24 PM

Why not just set the DataTemplate's DataType property to {x:Type namespace:XyzModel}?

Christopher Bennage wrote re: The Influence of MVC on my WPF
on 05-06-2009 8:26 PM

@Ryan I'm doing just that. That's what happens here:

var template = new DataTemplate(dataType);

The reason for this whole post though, is that I don't have to manually create new data templates to wire up the binding. Perhaps I should have been more explicit. There are no data templates in my XAML.

My views are merely user controls, and thus, more Blend friendly.

Ryan Riley wrote re: The Influence of MVC on my WPF
on 05-06-2009 9:42 PM

That is an interesting approach. I haven't seen DataTemplates used in that way before. So you are using both DataTemplates <em>and</em> UserControls? I suppose the only concern I have is that the setup appears a little too complex for what seems like such a simple concept, especially when you are already pulling out the root name and swapping Presenter for View.

I'm just playing devil's advocate here. I'm sure you have a good reason for the complexity, but I am having a hard time understanding it as an outsider.

DotNetShoutout wrote The Influence of MVC on my WPF - Christopher Bennage - Devlicio.us
on 05-06-2009 9:46 PM

Thank you for submitting this cool story - Trackback from DotNetShoutout

Stephen Smith wrote re: The Influence of MVC on my WPF
on 05-06-2009 11:25 PM

I have nothing to add to the debate as I am not using WPF but I do have "SAMS Teach Yourself WPF in 24 Hours" which I strongly recommend to those here who are using WPF as it is a gem that not only introduces the technology commencing from a beginner's perspective, but more importantly does so within the context of presenting it with best practices as to how to most effectively use the technology.

My beef, as is it is for many, is that in the Microsoft/.NET space developer's focus seems to be primarily on the power of the tool itself as though it were an end rather than the most powerful practices and principles that permit us to use the tools most powerfully as a means to achieving the end of producing solutions that add business values for project stakeholders. A powerful tool abused can cause a great deal of damage.

Hopefully you have plans for writing further books for presenting WPF and Caliburn within best practices and principles frameworks such as MVC in a more thorough treatment for those of us who graduate from WPF in 24 Hours.

Christopher Bennage wrote re: The Influence of MVC on my WPF
on 05-07-2009 12:55 AM

@Ryan Data templates are actually what I need, but the Blend support is poor. My standard approach has been to create user controls and then specify my templates like this:

<DataTemplate DataType={x:Type MyType}>

  <views:MyUserControl />

</DataTemplate>

All of this was just so I could use Blend. I've gotten so used to that, that I forget to mention it. :-P

I should post more on _why_ I take this approach, and when the extra complexity pays off.

Christopher Bennage wrote re: The Influence of MVC on my WPF
on 05-07-2009 12:57 AM

@Stephen Thanks for the positive review!

We certainly want to write more, but it literally cost us money to write a book.

I do have something in mind that is exactly what you are describing, but please don't hold your breath, it might be awhile.

Sven wrote re: The Influence of MVC on my WPF
on 05-08-2009 4:43 PM

I want to learn more about the MVP paatern with WPF. Do you maybe have like a sample application? I find this very handy to learn new things.

Good article, btw!

Christopher Bennage wrote re: The Influence of MVC on my WPF
on 05-11-2009 10:04 AM

@Sven the two quickest sources of a sample application would be:

* the LOB sample at http:\\www.codeplex.com\caliburn It's actually Silverlight, but it is still applicable.

* our book, Teach Yourself WPF in 24 Hours has a couple of chapters on building a WPF app with MVP.

Hopefully, soon, I'll also return to my series on building a WPF app. ;-)

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)

CodeBetter.Com