DCSIMG
Blendability Part III – View Model Locator Replacement using MEF - Essential WPF

Blendability Part III – View Model Locator Replacement using MEF

Everybody loves MEF! Don't you? I think MEF is one of the best things happened to the latest .NET Framework 4.

Just in case that you don't know what I'm talking about, I urge you to start reading about MEF in my colleague, Bnaya Eshet, blog. He has great MEF tutorial for beginners.

So why am I writing about MEF in this WPF related post anyway? Well MEF is a great framework for extensibility and object composition, also it can be used as a DI container, declaratively and imperatively.

With my experience in architecting several WPF client applications, I find MEF fits like a glove, makes everyone's life easier.

Lets have a simple sample of how may I use MEF in my client application.

[Export(typeof(ICommandBarViewModel))]
public class CommandBarViewModel : NotificationObject, ICommandBarViewModel
{
    [
Import
]
   
private ILogger
Logger
    {
       
get
;
       
set
;
    }

    [
Import
]
   
private IConfigurationService
ConfigurationService
    {
       
get
;
       
set
;
    }       

   
public IEnumerable<ICommandBarAction
> Actions
    {
       
get
        {
           
return new ICommandBarAction
[]
            {
               
new CommandBarAction
                {
                    Content =
"Open"
,
                    Command =
new DelegateCommand<object>(p => Open(
))
                },
               
new CommandBarAction
                {
                    Content =
"Save"
,
                    Command =
new DelegateCommand<object>(p => Save(
))
                }
            };
        }
    }
}

The sample code above demonstrates a simple usage of MEF for composing the CommandBarViewModel with both Logger and Configuration Service dependencies.

As you can see, the CommandBarViewModel type is decorated with MEF's ExportAttribute attribute, implementing the ICommandBarViewModel. Composing an instance of type CommandBarViewModel using MEF's container, satisfies all of its imports which are ILogger and IConfigurationService.

Implementing the MVVM pattern, especially as view-first, requires injection of the view-model instance into the relevant view which is created directly from XAML. There is a popular pattern called View Model Locator (a service locator pattern) which can be useful in such scenario.

Personally I find the View Model Locator very awkward, as you should always create an instance of it and place it in a resource-dictionary, then you should use Binding and StaticResource markups to resolve it. Since you should also provide a hint of what view-model to create, you should set the Binding.Path with the view-model type or a different hint, or have a binding converter. This ends with something like this:

<Application.Resources>
    <t:ViewModelLocator x:Key="viewModelLocator"/>
    <t:IndexerConverter x:Key
="viewModelIndexerConverter" />
</
Application.Resources>
DataContext="{Binding 
  
Source={StaticResource viewModelLocator},
 
  
Converter={StaticResource viewModelIndexerConverter},
 
  
ConverterParameter=MainViewModel}"

Needless to say that the implementation of the view model locator itself should deal somehow with a ridiculous index provided as "MainViewModel" hint to the indexer binding converter.

Using MEF with a simple markup extension I wrote, your XAML ends like this:

<UserControl x:Class="Blendability.Solution.Parts.CommandBarView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ts="http://blogs.microsoft.co.il/blogs/tomershamam"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 
   
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 
   
mc:Ignorable="d"
d:DesignHeight="76" d:DesignWidth="460"
    DataContext="{ts:Import ts:ICommandBarViewModel}">

    <ToolBar ItemsSource="{Binding Actions}">
        <ToolBar.ItemTemplate>
            <DataTemplate>
                <Button Style="{DynamicResource ResourceKey={x:Static ToolBar.ButtonStyleKey}}"
                       Content="{Binding Content}"
                       Command="{Binding Command}" />
            </DataTemplate>
        </ToolBar.ItemTemplate>
    </ToolBar>

</UserControl>

Much more elegant and less fragile solution I think, and I haven't talked about the Blendability part yet. Please be patient… this is the interesting part.

The ImportExtension markup-extension I used from XAML is a custom markup-extension I wrote which helps with the UI composition. Working with view-first we can easily create and compose our view-model with MEF directly from XAML. In our case we should only provide the view-model contract, which is the Import markup single argument of type Type, and in our case: ICommandBarViewModel.

Have to say that contracts in MEF can be also a simple string, but string is fragile and this is only one of the reasons I've chosen an interface instead of a string. Other reasons are unit-testing and Blendability.

The simple trick here is composing the given type inside the Import extension using MEF.

 

Composing our view-models with MEF, we are solving many problems we should deal with in the development phase:

  1. Create a view-model with all of its dependencies from both XAML and code.
  2. Create a dummy view-model at design-time but real one at runtime.
  3. Create a view-model stub/mock for unit-testing.
  4. Resolve dependencies such as services without being care of objects life-time and origin.

So now that we agree (or not) that MEF is really cool with our MVVM pattern, lets talk about Blendability of view-models created by MEF.

First lets mention the d:DataContext XAML attribute added to VS2010, also exist in Blend 3 and 4. I've talked about it in my previous post: Blendability Part I – Design time ViewModel.

The d:DataContext XAML attribute is really helpful, but is it really necessary having two definitions of data-context, one for runtime and one for design-time?

Personally, I prefer having only one definition of data-context which will serve both runtime and design-time. Here we go back to old school, working with the DataContext property only and here is how:

<UserControl x:Class="Blendability.Solution.Parts.CommandBarView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:ts="http://blogs.microsoft.co.il/blogs/tomershamam"
   DataContext="{ts:Import ts:ICommandBarViewModel, IsDesigntimeSupported=True}">
 
   
...

</UserControl>

As you can see, I'm using the same Import markup extension for having two different implementation of ICommandBarViewModel. One implementation for runtime, and one for design-time. The runtime implementation will be composed and return by the Import markup extension at runtime only, and the design-time implementation will be composed and return by the Import markup extension at design-time only.

And you may ask:

  1. How does DataContext work at design-time?
  2. What if I want to use the magnificent Blend sample data?
  3. What is and where the design-time implementation of ICommandBarViewModel coming from?

And the answers to these questions are:

  1. The same way as if you would create your view-model directly from XAML and set the DataContext property with. It supported by both VS and Blend.
  2. You can always remove the IsDesigntimeSupported=True and continue working with d:DataContext or you could add another property to the Import markup extension which is the object to use at design time, and use the regular d:SampleData to fetch it.
  3. It's getting too late over here, so I'll provide the answer + code to this one in my next post.

Next, I'll reveal design-time support for MEF and provide both the Import markup extension code and the rest. Stay tuned!

Published Wednesday, January 19, 2011 11:39 PM by Tomer Shamam

Comments

# re: Blendability Part III – View Model Locator Replacement using MEF

Thursday, January 20, 2011 1:23 PM by Elad Katz

Uhum.

1. Custom MarkupExtesions are not yet available in Silverlight, atleast until v5 (scheduled 2011 Q4). Solutions that work only in WPF are... bad.

2. The only real-world solution I know to date for SL is using ViewModelLocator. I don't think it's as cumbersome as you imply.. It's a service locator, simple and straight.

3. I don't think you should not use d:DataContext - using Blend to mock your data is priceless.. Mocking that yourself is far from trivial.

Good post, but not real world as far as I'm concerned.. screw SL is not in my jargon ;-)

# re: Blendability Part III – View Model Locator Replacement using MEF

Thursday, January 20, 2011 1:35 PM by Tomer Shamam

Elad,

1. This post is WPF only (look at the tags)!

2. (a) Same as 1. (b) In the eye of the beholder.

3. Read carefully, you always have an option to use d:DataContext as I said, but there are several cases Blend is not sophisticated enough generating the sample data, so you don't have any choice.

# re: Blendability Part III – View Model Locator Replacement using MEF

Thursday, January 20, 2011 6:05 PM by Roni Dayan

Nice post :)

I have been wondering about MEF and WPF.

I found many ways to use it with regards to WPF.

It will be nice if you can elaborate on the different approaches (View first, View Model first, controller ...).

In addition I found a complete framework for MEF using the MVVM pattern: WAF http://waf.codeplex.com/.

# re: Blendability Part III – View Model Locator Replacement using MEF

Friday, January 21, 2011 2:43 AM by Elad Katz

Let me rephrase.. :)

For WPF it's excellent. I just don't like solutions that are wpf-specific. As far as I can tell, both Prism and MVVMLight try as much as possible to give solutions to both at the same time, and I don't think we should stray from this.

That being said, if all one is looking for is for an elegant solution for wpf, your posts gives all the answers :)

# re: Blendability Part III – View Model Locator Replacement using MEF

Friday, January 21, 2011 10:55 AM by Tomer Shamam

As opposed to Silverlight, WPF based on the .NET Framework and provides excellent and full solution for developing desktop applications target the Windows OS. So if you’re not planning to move to SL, at least not in the next 4 years then it would be reasonable to leverage each and every feature in the WPF framework which can help in the development phase.

And this is exactly why I’m not agree with you, saying “I just don't like solutions that are wpf-specific“.

# re: Blendability Part III – View Model Locator Replacement using MEF

Tuesday, March 01, 2011 8:06 AM by Geir Inge

When using an ImportingConstructor as follows:        [ImportingConstructor]

public ProfileViewModel(IRegionManager regionManager, IEventAggregator eventAggregator)

Do I need to hook up the RegionManager and EventAggregator in DesigntimeBootstrapper somehow to make this work?

# re: Blendability Part III – View Model Locator Replacement using MEF

Thursday, March 03, 2011 12:07 AM by Tomer Shamam

Hi Geir,

You don't really have to do that, since your view-model for design shouldn't deal with event aggregator or other application classes, only design time properties.

# re: Blendability Part III – View Model Locator Replacement using MEF

Thursday, March 03, 2011 9:58 AM by Geir Inge

The reason I asked is that when we used the ImportingConstructor it did not work.

When we moved the RegionManager and IEventAggregator to imported properties it worked, perhaps because it then was a default constructor.

Leave a Comment

(required) 
(required) 
(optional)
(required) 

Enter the numbers above:
Powered by Community Server (Commercial Edition), by Telligent Systems