Geeks With Blogs
Kevin Rohling Building great s0ftware, 1 line at a time.

Download Sample Code

MVVM Playground

Have I mentioned lately that I love the MVVM presentation pattern??  If I haven’t please allow me to do so now.  It makes unit testing much much easier, the clean separation between logic and presentation makes me feel all warm on the inside, and (Blendability) those designers in the black turtle necks can run off and build an awesome UI in XAML while us devs write our code in peace :) 

Ahhh, so ok there are some issues that we are going to run into in the MVVM space.  I am not going to enumerate all of those here and now, I have other things to do today, but one that has bugged me for some time is how to keep my Views and ViewModels completely (and I mean 100%) separate from each other.  Why is this important??  Well, aside from the warm fuzzy feeling I mentioned before, the less your logic knows about your UI the more freedom the two have to change independent of each other.  For example, you can (for the most part) multitarget your Silverlight View Models to build a WPF solution as well, that’s portability and it’s a beautiful thing.  This is also a reason I’m not a fan of packaging Views and View Models in the same assembly.  While multitargetting may not be on your agenda I’m sure we can all agree maintainability is and loose coupling is a facilitator to that end.

Using Prism

Ok, enough raving on MVVM and allow me to go on about Prism for a moment.  Have you looked into Prism?  If you’re building an MVVM app I strongly (<—strong emphasis here) recommend it.  Yes, it is a bit complex and yes, there are several pieces to this great big PnP pie (Modularity, Regions, Event Aggregation, etc.) but there’s no need to dive in and eat the whole damn thing.  You really can take just the slices you want.  I must admit that I am just digging into the depths of some of these components myself but if you’re interested in my humble opinion I don’t see getting very far in MVVM without Event Aggregation and what I have recently learned about Regions is changing the way I tie my Views and ViewModels together, a la this blog post!

Now getting to the point, no good post is complete without a decent example project.  In this example, named Playground, I have 2 Prism modules: MonkeyBars and SwingSet.  Yes, I am amused by these names and I hope you are too :)  Each of these components is composed of 3 separate assemblies: The Module, ViewModels, and Views.

Solution View

image

Depiction of Dependencies

image

As you can see the ViewModels and Views are completely independent and are composed within the Module.  This is super!  Now, how exactly does this composition take place??  Well, if you’re using Prism Regions (and I am) you probably want to do it in your IModule.Initialize() method like so:

Code Snippet
  1. public class MonkeyBarsModule : IModule
  2.     {
  3.         private IUnityContainer UnityContainer { get; set; }
  4.         private IRegionManager RegionManager { get; set; }
  5.         public MonkeyBarsModule(IUnityContainer container, IRegionManager regionManager)
  6.         {
  7.             this.UnityContainer = container;
  8.             this.RegionManager = regionManager;
  9.         }
  10.         public void Initialize()
  11.         {
  12.             this.RegionManager.RegisterViewWithRegion("MonkeyBarsRegion", GetMonkeyBarsView);
  13.         }
  14.         private object GetMonkeyBarsView()
  15.         {
  16.             var monkeyBarsView = new MonkeyBarsView();
  17.             monkeyBarsView.DataContext = this.UnityContainer.Resolve<MonkeyBarsViewModel>();
  18.             return monkeyBarsView;
  19.         }
  20.     }

XAML View/View Model Map

This is all well and good, and probably not a bad solution.  However, I really feel that mapping like this should be done in some sort of configuration, a.k.a. XML.  Why you ask??  I favor configuration over code because code can be easily broken and changes should be retested where as changes to configuration are generally much easier and less brittle.  To make this happen, I created a “RegionCatalog” custom collection that can be instantiated and composed in XAML.  The items in this collection allow me to map together a Region Name, a ViewType, and an optional ViewDataContextType (my View Model).  Here’s what this catalog looks like:

Code Snippet
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <RegionsEx:RegionCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.                xmlns:sys="clr-namespace:System;assembly=mscorlib"
  5.                xmlns:RegionsEx="clr-namespace:RegionExtensions;assembly=RegionExtensions">
  6.     <RegionsEx:RegionInfo
  7.         RegionName="MonkeyBarsRegion"
  8.         ViewType="Playground.MonkeyBars.Views.MonkeyBarsView, Playground.MonkeyBars.Views, Version=1.0.0.0"
  9.         ViewDataContextType="Playground.MonkeyBars.ViewModels.MonkeyBarsViewModel, Playground.MonkeyBars.ViewModels, Version=1.0.0.0"/>
  10. </RegionsEx:RegionCatalog>
  11.     

And now here is what my Module class looks like after adding the Region collection:

Code Snippet
  1. public class MonkeyBarsModule : IModule
  2.     {
  3.         private IUnityContainer UnityContainer { get; set; }
  4.         private IRegionManager RegionManager { get; set; }
  5.         private RegionCatalog RegionCatalog { get; set; }
  6.         public MonkeyBarsModule(IUnityContainer container, IRegionManager regionManager)
  7.         {
  8.             this.UnityContainer = container;
  9.             this.RegionManager = regionManager;
  10.             this.RegionCatalog = RegionCatalog.CreateFromXaml(new Uri("/Playground.MonkeyBars.Module;component/RegionsCatalog.xaml", UriKind.Relative));
  11.         }
  12.         public void Initialize()
  13.         {
  14.             RegionCatalogManager.RegisterCatalog(this.RegionCatalog, this.UnityContainer, this.RegionManager);
  15.         }
  16.     }

Notice that the Region Catalog is instantiated from the RegionsCatalog.xaml file and when the Module is initialized the catalog is registered.  This will essentially register the View and Region Name with the RegionManager and when the View is needed the correct View Model will be instantiated and bound to the View’s DataContext.  Yes, Dependency Injection is used for instantiating the Views and View Models :) 

The Why

So in summary what does this achieve?  Well, we saw how we can keep our Views and View Models decoupled but this is nothing super-special.  The trick is how we compose them at run time in a loosely coupled manner.  By putting this mapping into XML we remove this mapping from code making it easier and safer to change in the future.

Caveat: This code is by no means production ready but I think the idea is pretty solid :)

Posted on Tuesday, September 8, 2009 1:17 PM | Back to top


Comments on this post: Decoupling Silverlight Views & ViewModels

# re: Decoupling Silverlight Views & ViewModels
Requesting Gravatar...
very relevant and timely post. Thanks for this.

Damon Wilder Carr
Left by Damon Wilder Carr on Oct 31, 2009 8:50 PM

# re: Decoupling Silverlight Views & ViewModels
Requesting Gravatar...
Oh my god, it helped so much too! Thank you.
Left by debrousailleuse thermique on Jan 18, 2011 1:37 PM

Your comment:
 (will show your gravatar)


Copyright © SundriedCoder | Powered by: GeeksWithBlogs.net