Best practice for associating views with viewmodels using MEF

Topics: Prism v4 - Silverlight 4, Prism v4 - WPF 4
Nov 30, 2011 at 8:04 PM

While reading through the PRISM 4.0 November 2010 guide on MSDN, in chapter 5, it is mentioned how one would associate a viewmodel with a view (i.e. make the viewmodel the datacontext of a specific view).  The way I do it is through MEF property injection in the view's code-behind:

[Export(typeof(MainToolbarView))]
public partial class MainToolbarView : UserControl
{
    public MainToolbarView()
    {
        InitializeComponent();
    }

    [Import]
    public MainToolbarViewModel ViewModel
    {
        set { this.DataContext = value; }
    }
}

The downside here is that I'm coupling my view with a specific viewmodel.  How can I decouple this?  What's the best approach for associating a view with a view model?  Should I just suck it up and keep it in the code behind?

Developer
Dec 1, 2011 at 1:22 PM
Edited Dec 1, 2011 at 1:23 PM

Hi,

You might find there are different techniques for wiring up a view to a View Model, importing the view model into a property of the view using MEF like you suggested is a common accepted pattern to achieve this goal.

On the other hand if your scenario requires decoupling your view from its View Model, you could try exporting your View Model using a common Interface (e.g. IMainToolbarViewModel). If so, in your particular case, your code could result in something like this:

[Export(typeof(MainToolbarView))]
public partial class MainToolbarView : UserControl
{
  (...)

    [Import]
    public IMainToolbarViewModel ViewModel
    {
        set { this.DataContext = value; }
    }
}

 


Also, if you are interested in more information about the MVVM pattern, you might find the In The Box MVVM Training by Karl Shifflett handy.

I hope you find this useful

Agustin Adami
http://blogs.southworks.net/aadami


Dec 2, 2011 at 3:47 PM

Adding to what Adami says, it would be better to inject your view model interface in views constructor. That way you are double sure that your view fails straight away if it is unable to locate the view model. You would do

[Export(typeof(MainToolbarView))]
public partial class MainToolbarView : UserControl
{
    [ImportingConstructor]
    public MainToolbarView(IMainToolbarViewModel viewModel)
    {
        InitializeComponent();
        DataContext = viewModel;
    }

    public IMainToolbarViewModel ViewModel
    {
        get { return (IMainToolbarViewModel)DataContext; }
    }
}

Dec 2, 2011 at 3:55 PM

@gan_s : That makes sense to me, but why use an interface?  My views/viewmodels are in the same assembly anyway, so does it matter?  Currently, my viewmodels all inherit an abstract viewmodel class "ZNavigationViewModelBase", which implements "IRegionMemberLifetime" and "IConfirmNavigationRequest" and inherits "NotificationObject".  Should I be designing my viewmodels differently? Perhaps have an IZNavigationViewModelBase that implements the aforementioned interfaces and then have create a IMainToolbarViewModel that implements this base interface??

Dec 2, 2011 at 4:04 PM
Edited Dec 2, 2011 at 4:06 PM

To answer your question about interfaces and why to use them, here is a good article

http://fci-h.blogspot.com/2008/03/oop-design-concepts-interfaces_05.html

I would create a interface for the view model to make it clear which of the properites of my view model would ideally be consumed by my view through binding.

The purpose of creating a viewmodelbase (in this case NotificationObject) it to have common logic for all view models like

  • property changed
  • IsBusy (for busy indicators)
  • Dirty tracking

The IRegionMemberLifetime and IConfirmNavigatinRequest are used for defining the lifetime of the view in the region and to handle navigations to and from the view, respectively. An alternative for IRegionMemberLifetime is to use the [RegionMemberLifetime(KeepAlive = false)] attribute on your view model.

So ideally you would have

 

public abstract class ViewModelBase : NotificationObject
{
   ... other base properties you wish to have all view models
}

public interface IMyViewModel
{
   ....
}

[Export(typeof(IMyViewModel)]
[RegionMemberLifetime(KeepAlive = false)]
public class MyViewModel : ViewModelBase, IMyViewModel
{
   ....
}

ANd you would inject IMyViewMOdel into your view

Hope this makes sense :)

Cheers!