Click here to Skip to main content
Click here to Skip to main content

Blendable MVVM ViewModelLocator using MEF

By , 6 Oct 2010
 

Introduction

Recently I entered the wonderful world of MVVM. There is a lot to read on this subject (which I did) and there are several very good frameworks available. So I 'collected' handy bits and pieces from these frameworks in order to create my first MVVM application.

During this process, I found out that the ViewModel locator was debated a lot and that all the various solutions had their pros and cons (see: Nikhil Kothari Options for Hooking a View to its Model). Since the introduction of MEF in .NET Framework 4, a new possibility came into reach which was described by (johnpapa: Simple ViewModel Locator for MVVM: The Patients Have Left the Asylum).

Basically what it comes down to is how to you 'wire' your ViewModel to your view without specifying it in multiple places and with the preservation of designtime binding in Blend and VS2010.

Since code says more than an article, I included a simple project which demonstrates how it works.

DynamicObject

The backbone of my solution is the new DynamicObject which was introduced in .NET Framework 4. This object can be extended runtime, if you want more information on how these things work, you can find it here: A Multi-level C# 4.0 Dynamic Object. When a property is used from a DynamicObject, the TryGetMember is called with the name of the property which is requested. When a DataContext is specified, the property name can be specified like this:

DataContext="{Binding Demo1, Source={StaticResource Locator}}"

Since my Locator is a DynamicObject, the TryGetMember is called with the parameter name "Demo1". Initially, I then created the corresponding ViewModel based on this name like this:

public override bool TryGetMember(GetMemberBinder binder, out object result) 
{ 
    string name = binder.Name; 
    if (!dictionary.TryGetValue(name, out result)) 
    { 
        switch (name) 
        { 
            case "Demo1": 
                dictionary[name] = (result = new Demo1ViewModel()); 
                return true; 
        } 
        return false; 
    } 
    return true; 
} 

But when I read the article by JohnPapa, I decided to add his MEF implementation as well! Also a lot of articles on this subject can be found here: .NET 4.0 MEF FAQ. In order to find the ViewModel, an ExportViewModel attribute is introduced:

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class ExportViewModel : ExportAttribute
{
	public string Name { get; private set; }
	public ExportViewModel(string name, bool isStatic)
		: base("ViewModel")
	{
		Name = name;
	}
}

Which can be used on a ViewModel like this:

[ExportViewModel("Demo1", false)]
class Demo1ViewModel : ViewModel
{
	public Demo1ViewModel()
	{
		EnDisable = new RelayCommand(() =>
		{
			isEnabled = !isEnabled;
			Action.RaiseCanExecuteChanged();
		});

		Action = new RelayCommand(() => 
			MessageBox.Show("demo1 test"), () => isEnabled);
	}

	private bool isEnabled = false;
	public RelayCommand EnDisable { get; set; }
	public RelayCommand Action { get; set; } 
}

Basically what happens here is that with the help of MEF, the ViewModel gets a name "Demo1" and can be found by using a CompositionContainer like this:

[ImportMany("ViewModel", AllowRecomposition = true)]
private IEnumerable<Lazy<object, IViewModelMetadata>> ViewModels { get; set; }
 

var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(typeof(ViewModelLocator).Assembly));
CompositionContainer _container = new CompositionContainer(catalog);
var compositionContainer = new CompositionContainer(catalog);
compositionContainer.ComposeParts(this);

The big advantage of this approach is that there is no maintenance of the link between the View and the ViewModel other than this, the locator is generic for all the ViewModels in your project! The TryGetMember will then look like:

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
	string name = binder.Name;
	if (!dictionary.TryGetValue(name, out result))
	try
	{
		if (ViewModels == null)
		{
			var catalog = new AggregateCatalog();
			catalog.Catalogs.Add(new AssemblyCatalog
				(typeof(ViewModelLocator).Assembly));
			CompositionContainer _container = 
					new CompositionContainer(catalog);
			var compositionContainer = 
					new CompositionContainer(catalog);
			compositionContainer.ComposeParts(this);
		}
		dictionary[binder.Name] = (result = ViewModels.Single
					(v => v.Metadata.Name.Equals(name)).Value);
		return result != null;
	}	
	catch (Exception ex)
	{
		Console.WriteLine(ex);
	}
	return true;
}

Designtime Binding

There are a lot of Locators available which can also handle the same functionality like my locator but most of those have problems with blendability where the properties of the ViewModel do not show up in Blend or VS2010. Since I like that a lot, I tried to add some stuff I had used back in the days where all the database classes we had were DataSet and DataTable. The ITypedList is used to specify which properties are 'not really there' - see: Implementing ITypedList for Virtual Properties. Together with the TypeDescriptionProvider which can be used to support dynamic run-time properties, all the ViewModels which are created this way can be bindable.

blend1.png

Points of Interest

I learned a lot by reading all the various articles about MVVM. I hope this article can help others.

History

  • 6th October, 2010: First version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Jan Brandsma
Web Developer
Netherlands Netherlands
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionIs this still a good approach for binding view models to views ?memberDerek_Ewing27 Dec '12 - 10:55 
Hi,
I have been doing quite a bit of reading up on MVVM\silverlight 5\MEF 2.
 
Am I correct in saying that we can not use MEF to dynamically compose the viewmodels at design time ?
 
I was just wondering as this article is from 2010, and here we are almost 2013. This looking like the "best" approach I have seen so far.
 
Regards
Derek
BugAnother link does not workmemberClifford Nelson4 Dec '12 - 12:47 
Nikhil Kothari Options for Hooking a View to its Model
BugLink no longer worksmemberClifford Nelson4 Dec '12 - 12:46 
johnpapa: Simple ViewModel Locator for MVVM: The Patients Have Left the Asylum does not work.
QuestionViewModel is staticmemberMember 784113521 Sep '12 - 4:26 
i used this nice ViewModelLocator in my application.
now i see a VERY big problem... Frown | :(
please help!!!
 
the problem is:
i created a user control - view, and i use it a few times, but the view model loactor dose not
create a new insatnce of view model each time i use the usercontrol.
the view model is static?!?
 
i saw that you alredy added a property in the exported attribute "is static".
but you never use it in the view model.
 
how do i make it works?
that each view will have a new instance of view model??
 
i would very appriciate to get an answer as soon as possible.
before my boss will kill me Sniff | :^)
 
thanks,
El
Bugmulti instances of viewsmemberMember 784113530 Aug '12 - 1:25 
Hi,
 
the view model locator is very nice.
i used it in my application, but now i face a problem.
 
what happend in case i need to show two times the same view.
then- i want that the view model will be a new instance, that the view will be injected to a new instance of the view model, so i'll be able to have different data values in the views.
currentl, when i edit somte data in the first view - the other view is changed too.
 
please help!!!
 
thanks,
QuestionFragile [modified]memberkman22 Jul '11 - 5:19 
I have been using your approach for viewModel locating for a few months and have determined that while it is my favorite implementation, it is a bit fragile.
 
Has anyone else noticed this, are there any fixes?
 
One thing i've noted while debugging is that the ViewModels property is always null forcing the TryGetMember function to compose the MEF container on EVERY lookup. Is this as designed?
 
if (ViewModels == null)
                    {
                        var catalog = new AggregateCatalog();
                        catalog.Catalogs.Add(new AssemblyCatalog(typeof(ViewModelLocator).Assembly));
                        CompositionContainer _container = new CompositionContainer(catalog);
                        var compositionContainer = new CompositionContainer(catalog);
                        compositionContainer.ComposeParts(this);
                    }

modified on Friday, July 22, 2011 11:29 AM

AnswerRe: Fragilememberkman22 Jul '11 - 5:50 
After looking at it more closely, looks like the locator is not retained when in blend. When running we get one init. Still, I still seem to be having trouble with me adding a few new properties in the VM then rebuilding in Blend and the changes don't show up.
 
Ideas?
GeneralMy vote of 5memberaaltaya11 Mar '11 - 4:22 
Easy to use and one of the best approaches regarding viewmodellocator
QuestionViewModel parameters?memberdvoxsis2 Dec '10 - 23:36 
Hello,
 
Your approach seems very straightforward and handy, nicely done!
However, I am searching any solution to pass parameters to my ViewModels (either MEF or MVVMLight messaging);
 
How would you consider this?
 
Thanks...
GeneralMy vote of 4membertec-goblin11 Oct '10 - 11:41 
Very very interesting. Could you describe a real life scenario where this is useful, as well as the performance implications?

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 6 Oct 2010
Article Copyright 2010 by Jan Brandsma
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid