Simple ViewModelLocator with MEF

Download SourceCode

These days design pattern #1 seems to be the MVVM pattern, at least in the Silverlight/WPF community. As soon as you start to dig deeper into MVVM you get in touch with another design pattern, the ViewModelLocator which is basically the ServiceLocator pattern but instead of locating Services it helps us to locate our ViewModels. Just bing it and you will find a wide variety of implementations. The only reason why I came up with my own implementation is simply because I couldn’t find one which meets my requirements:

  • Support for design-time data
  • As less as possible code to wire up the ViewModels with the ViewModelLocator
  • Easy syntax to bind the ViewModel in XAML

The best implementation so far was the ViewModelLocator from John Papa. Unfortunately it doesn’t support design time data seamless, that means you have to code extra ViewModels for design-time data and set the datacontext twice with help of the designer attribute. But I borrowed some ideas from his implementation as well from Kellabytes blog post on how to use MEF with the ViewModelLocator from the  famous MVVM Light Toolkit. What I learned from both is that MEF is the best option to implement a light weight ViewModelLocator.

But before we start lets talk about ViewModels and design-time data. Mostly ViewModels relying on external services to get the data they want and to polish them for the View. So the most obvious option would be to mock the ViewModel and instead of asking the service for data, it provides some fake data by itself. Since we live in a decoupled world their is one problem with this approach and that is the fact that we have to introduce interfaces for our ViewModel and to implement each ViewModel twice, one for design-time and one for run-time.  I think it’s a much better approach to inject  ServicProvider/Helper into the ViewModels which creates an abstraction layer between the ViewModels and the external services and  gives us the possibility to foist our design-time data. Plus, a ServiceProvider/Helper is potentially used by more than one ViewModel. So it makes absolute sense to place the fake data there.

Here it comes, the easy 3+1 steps you need to get your ViewModels with full design-time support:

1. Export ServiceHelper

 1: [Export(typeof(IServiceHelper))]
 2: public class ServiceHelper : IServiceHelper
 3: {...
 4:
 5: [Export(typeof(IServiceHelper))]
 6: [DesignTimeExportAttribute(DesignTime = true)] //explicite mark this as design-time export
 7: public class DummyServiceHelper : IServiceHelper
 8: {...

2. Export ViewModel via contract name and import ServiceProvider

 1: [Export(ViewModelTypes.MainViewModel)]
 2: public class MainViewModel : INotifyPropertyChanged
 3: {
 4: ...
 5: [ImportingConstructor] //lets MEF provide an implementation
 6:    public MainViewModel(IServiceHelper serviceHelper)
 7:    {
 8: ...

3. Bind the ViewModel

 1: <!--
 2:  For extended support in the designer tool add a typed version of the ViewModel to ViewModelLocator
 3:  and instead of the indexer use the typed property
 4:  DataContext="{Binding Source={StaticResource VMLocator}, Path=MainViewModel}"
 5:  -->
 6:   <Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource VMLocator}, Path=[MainViewModel]}">

4. Optional add a typed property to the ViewModelLocator if you need support for the PropertyGrid in the VS/Blend

 1: ...
 2: //string indexer to access the right ViewModel by contract name
 3: public object this[string viewModel]
 4: {
 5:     get
 6:     {
 7:         return _container.GetExportedValue<object>(viewModel);
 8:     }
 9: }
 10:
 11: //a typed property to access the MainViewModel
 12: //is not needed because the string indexer can be used directly from XAML
 13: //but adds extra value to the design experience
 14: public MainViewModel MainViewModel { get { return (MainViewModel)this[ViewModelTypes.MainViewModel]; } }
 15: ...

If you are interested in how it works and prefer to read source code then grab it from here or if you are fine with my ESL – blog post style then keep reading.

Ok you still here, then lets start with some infrastructure that helps us to define our design-time exports easily. MEF allows us to add meta-data to an export definition. The following class implements a simple Boolean attribute which indicates whether an export is meant to provide a implementation at design-time.

 1: [MetadataAttribute]
 2: [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
 3: /// <summary>;
 4: /// Marks an export as a design-time replacement for another export with the same contract.
 5: /// </summary>
 6: public class DesignTimeExportAttribute : ExportAttribute
 7: {
 8:     #region Constructors
 9:     public DesignTimeExportAttribute()
 10:     {
 11:         DesignTime = false;
 12:     }
 13:
 14:     public DesignTimeExportAttribute(Type contractType)
 15:             : base(contractType)
 16:     {
 17:         DesignTime = false;
 18:     }
 19:
 20:     public DesignTimeExportAttribute(string contractName)
 21:             : base(contractName)
 22:     {
 23:         DesignTime = false;
 24:     }
 25:
 26:     public DesignTimeExportAttribute(string contractName, Type contractType)
 27:             : base(contractName, contractType)
 28:     {
 29:         DesignTime = false;
 30:     }
 31:     #endregion
 32:
 33:     [DefaultValue(false)]
 34:     public bool DesignTime
 35:     {
 36:         get; set;
 37:     }
 38: }

Now we can take advantage of one extensibility point of MEF and code our custom ComposablePartCatalog which filters and prioritize design-time exports (the idea and part of the code comes from here). The overall goal of the strategy is to to replace a ServiceProvider implementation with a dummy implementation at design-time by utilizing the DesignTimeAttribute. Maybe a ServiceProvider doesn’t rely on external source and we don’t want to be forced to provide design-time export, that’s why exports without the DesignTimeAttribute stay valid at design-time. However, at run-time we wanna get rid of design-time data and we exclude all exports with the DesignTimeAttribute set to true.

 1: /// <summary>
 2: /// A design- vs run-time wrapper catalog which provides prioritizing and filtering based on the DesignTimeExportAttribute
 3: /// </summary>
 4: public class DRPartCatalog : ComposablePartCatalog
 5: {
 6:     private ComposablePartCatalog _catalog;
 7:     private bool _designTime = false;
 8:     public bool DesignTime
 9:     {
 10:         get
 11:         {
 12:             return _designTime;
 13:         }
 14:     }
 15:
 16:     /// <summary> 
 17:     /// Creates a new DRPartCatalog around an existing catalog. 
 18:     /// The catalog that this class decorates is provides in the constructor 
 19:     /// paramater "catalog". The "DesignTime" property is set to false.
 20:     /// <summary> 
 21:     public DRPartCatalog(ComposablePartCatalog catalog)
 22:     {
 23:         _catalog = catalog;
 24:     }
 25:
 26:     /// <summary> 
 27:     /// Creates a new DRPartCatalog around an existing catalog. 
 28:     /// The catalog that this class decorates is provides in the constructor 
 29:     /// paramater "catalog". The "designTime" parameter controls sets the "DesignTime"
 30:     /// property which is used to control the import satisfaction
 31:     /// <summary> 
 32:     public DRPartCatalog(ComposablePartCatalog catalog, bool designTime)
 33:     {
 34:         _designTime = designTime;
 35:         _catalog = catalog;
 36:     }
 37:
 38:     public override System.Linq.IQueryable<ComposablePartDefinition> Parts
 39:     {
 40:         get { return _catalog.Parts; }
 41:     }
 42:
 43:     /// <summary>
 44:     /// Returns the exports in the catalog that match a given definition of an import.
 45:     /// This method is called every time MEF tries to satisfy an import.
 46:     ///</summary>
 47:     public override IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>> GetExports(ImportDefinition importDef)
 48:     {
 49:         // If ImportMany is defined and we are at design-time the use the standard bahavior and return
 50:         // all matching exports.
 51:         if (importDef.Cardinality == ImportCardinality.ZeroOrMore && DesignTime)
 52:         {
 53:             return base.GetExports(importDef);
 54:         }
 55:
 56:         //otherwise we have to do our own logic
 57:         IList<Tuple<ComposablePartDefinition, ExportDefinition>> result
 58:             = new List<Tuple<ComposablePartDefinition, ExportDefinition>>();
 59:
 60:         // Walk through all parts in that catalog...
 61:         foreach (ComposablePartDefinition partDef in Parts)
 62:         {
 63:             // ... and for each part, examine if any export definition matches the
 64:             // requested import definition.
 65:             foreach (ExportDefinition exportDef in partDef.ExportDefinitions)
 66:             {
 67:                 if (importDef.IsConstraintSatisfiedBy(exportDef))
 68:                 {
 69:                     //ok the import definition is satisfied
 70:                     Tuple<ComposablePartDefinition, ExportDefinition> matchingExport = null;
 71:                     matchingExport = new Tuple<ComposablePartDefinition, ExportDefinition>(partDef, exportDef);
 72:                     object designTimeMetadata;
 73:                     exportDef.Metadata.TryGetValue("DesignTime", out designTimeMetadata);
 74:                     //if DesignTimeAttribute is set then ToBool returns the assigend value
 75:                     //ohterwise it returns false 
 76:                     bool hasDesignTimeAttribute = ToBool(designTimeMetadata);
 77:
 78:                     //If ImportMany is defined and we are at run-time then filter out
 79:                     //design-time exports
 80:                     if (importDef.Cardinality == ImportCardinality.ZeroOrMore)
 81:                     {
 82:                         if (DesignTime || !hasDesignTimeAttribute)
 83:                             result.Add(matchingExport);
 84:                     }
 85:                     //If Import or Import(AllowDefault=true) then prioritize design-time exports
 86:                     //at design-time
 87:                     else
 88:                     {
 89:                         if (DesignTime)
 90:                         {
 91:                             if (result.Count == 0) //also allow run-time exports at design-time
 92:                                 result.Add(matchingExport);
 93:                             else if (hasDesignTimeAttribute) //but prioritize design time data at design time
 94:                             {
 95:                                 result.Clear();
 96:                                 result.Add(matchingExport);
 97:                             }
 98:                         }
 99:                         else
 100:                         {
 101:                             if (!hasDesignTimeAttribute) //only allow run-time exports at run-time
 102:                                 result.Add(matchingExport);
 103:                         }
 104:                     }
 105:                 }
 106:             }
 107:         }
 108:         return result;
 109:     }
 110:
 111:     /// <summary>
 112:     /// Converts an untyped value into a bool. If the object is null
 113:     /// or cannot be converted to an bool, returns false.
 114:     /// </summary>
 115:     protected static bool ToBool(object value)
 116:     {
 117:         if (value == null)
 118:         {
 119:             return false;
 120:         }
 121:
 122:         bool result = false;
 123:         bool.TryParse(value.ToString(), out result);
 124:         return result;
 125:     }
 126: }

Now we have the important parts in place and we can code an example on how we take advantage of all this. In order to simulate a more real world project we going to have separated projects for the View, the ViewModel and the Model. The following interface defines the functionality for a ServiceHelper.

 1: public interface IServiceHelper
 2: {
 3:     void GetData(Action<IEnumerable<Foo>> callback);
 4: }

Allright, here a “real” implementation of the interface and another one to provide fake data for designer.

 1: [Export(typeof(IServiceHelper))]
 2: public class ServiceHelper : IServiceHelper
 3: {
 4:     public void GetData(Action<System.Collections.Generic.IEnumerable<Foo>> callback)
 5:     {
 6:         //lets pretend we do a external service call here
 7:         //which would fail at design-time
 8:
 9:         List<Foo> result = new List<Foo> {
 10:             new Foo { Bar = "Runtime data 1"},
 11:             new Foo { Bar = "Runtime data 2"},
 12:             new Foo { Bar = "Runtime data 3"}
 13:         };
 14:         //and now hand-over the data via the callback
 15:         callback(result);
 16:     }
 17: }
 18:
 19: [Export(typeof(IServiceHelper))]
 20: [DesignTimeExportAttribute(DesignTime = true)] //explicite mark this as design-time export
 21: public class DummyServiceHelper : IServiceHelper
 22: {
 23:     public void GetData(Action<System.Collections.Generic.IEnumerable<Foo>> callback)
 24:     {
 25:         //we just need dummy data 
 26:         List<Foo> result = new List<Foo> {
 27:             new Foo { Bar = "Design-time data 1"},
 28:             new Foo { Bar = "Design-time data 2"},
 29:             new Foo { Bar = "Design-time data 3"}
 30:         };
 31:         //and now hand-over the data via the callback
 32:         callback(result);
 33:     }
 34: }

The ViewModel imports IServiceHelper but doesn’t know about run-/design-time data. It just expects an implementation of IServiceHelper and exports itself via contract name. The name of the ViewModel that is used as contract name for the export is managed in a sealed class and prevents typos within the export/import definitions.

 1: [Export(ViewModelTypes.MainViewModel)] //export via contract name
 2: public class MainViewModel : INotifyPropertyChanged
 3: {
 4:     private IServiceHelper _servicHelper;
 5:
 6:     private ObservableCollection<Foo> _foos;
 7:     public ObservableCollection<Foo> Foos
 8:     {
 9:         get
 10:         {
 11:             return _foos;
 12:         }
 13:         set
 14:         {
 15:             _foos = value;
 16:             if (PropertyChanged != null)
 17:                 PropertyChanged(this, new PropertyChangedEventArgs("Foos"));
 18:         }
 19:     }
 20:
 21:     [ImportingConstructor] //lets MEF provide an implementation
 22:     public MainViewModel(IServiceHelper serviceHelper)
 23:     {
 24:         _servicHelper = serviceHelper;
 25:         _servicHelper.GetData((result) =>
 26:             {
 27:                 Foos = new ObservableCollection<Foo>(result);
 28:             });
 29:     }
 30:
 31:     public event PropertyChangedEventHandler PropertyChanged;
 32: }

Finally, we can have a look at the ViewModelLocator. The implementation is pretty simple and straight forward except for getting the assemblies we need for MEF. Since the standard initialization of MEF doesn’t work at design-time, we have to manually add all assemblies which contains export and/or import definitions until MEF gets a better design-time support. It’s a ugly hack, but  the easiest way to get the assembly is via the type of a known class or interface. Please let me know if you know about a more elegant way that works also at design-time.

 1: public class SimpleViewModelLocator
 2: {
 3:     private static CompositionContainer _container;
 4:
 5:     /// <summary>
 6:     /// Initializes a new instance of the ViewModelLocator class.
 7:     /// </summary>
 8:     public SimpleViewModelLocator()
 9:     {
 10:         //detect design-time
 11:         bool designTime = DesignerProperties.IsInDesignTool;
 12:
 13:         //since the default catalog initializing in MEF doesn't work at runtime
 14:         //we have to create the catalog manually for now
 15:         var aggregatedAssemblyCatalog = new AggregateCatalog(
 16:                          new AssemblyCatalog(typeof(MainViewModel).Assembly) //assembly that contains the ViewModels
 17:                        , new AssemblyCatalog(typeof(IServiceHelper).Assembly) //assembly that contains the Models
 18:                                         );
 19:
 20:         //the design/run time catalog helps to filter exports depending on a design-time attribute
 21:         var drpCatalog = new DRPartCatalog(aggregatedAssemblyCatalog, designTime);
 22:
 23:         //the container to resolve exported ViewModels
 24:         _container = new CompositionContainer(drpCatalog);
 25:     }
 26:
 27:     //string indexer to access the right ViewModel by contract name
 28:     public object this[string viewModel]
 29:     {
 30:         get
 31:         {
 32:             return _container.GetExportedValue<object>(viewModel);
 33:         }
 34:     }
 35:
 36:     //a typed property to access the MainViewModel
 37:     //is not needed because the string indexer can be used directly from XAML
 38:     //but adds extra value to the design experience
 39:     public MainViewModel TestViewModel { get { return (MainViewModel)this[ViewModelTypes.MainViewModel]; } }
 40: }

Here we go; after all the efforts we can easily wire our ViewModels in 3 + 1 steps (see above) with first class design-time support. If you prefer to have support for DataBinding  in the PropertyGrid of Cider/Blend then step 4 is a matter of seconds with the following code snippet.

 1: <?xml version="1.0" encoding="utf-8" ?>
 2: <CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
 3:     <CodeSnippet Format="1.0.0">
 4:         <Header>
 5:             <Title>vmltprop</Title>
 6:             <Shortcut>vmltprop</Shortcut>
 7:             <Description>Code snippet for defining a typed Property in Bitdisaster's SimpleViewModelLocator</Description>
 8:             <Author>Jan Hannemann</Author>
 9:             <SnippetTypes>
 10:                 <SnippetType>Expansion</SnippetType>
 11:             </SnippetTypes>
 12:         </Header>
 13:         <Snippet>
 14:             <Declarations>
 15:                 <Literal>
 16:                     <ID>VM</ID>
 17:                     <ToolTip>ViewModel</ToolTip>
 18:                     <Default>ViewModel</Default>
 19:                 </Literal>
 20:             </Declarations>
 21:             <Code Language="csharp">
 22:         <![CDATA[public $VM$ $VM$ {get{return ($VM$)this[ViewModelTypes.$VM$];}}
 23:         $end$]]>
 24:             </Code>
 25:         </Snippet>
 26:     </CodeSnippet>
 27: </CodeSnippets>

Let me know what you think!

6 thoughts on “Simple ViewModelLocator with MEF

      1. Thank you! The link that you provide here works indeed! But in the text above where you said: “If you are interested in how it works and prefer to read source code then grab it from here or if you are fine with my ESL – blog post style then keep reading.” – ‘here’ points to some other address – http://www.sharpedtools.net/picdropbox/SimpleViewModelLocator.rar

        P.S. Ohhh.. now I see the link at the top of the page!!! It points to the correct source! Thank you again!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s