Wednesday, January 26, 2011

Auto-ViewModel binding with Ninject

I have done a lot of post about binding ViewModels to Views in Silverlight, so can you see that it is really a subject I am quite interested in.  It also allows me to learn more about several technologies all in the context of this domain.

Having said this, lets have a look at using Ninject for Injecting our ViewModels into our Views.  We are going to use the concept of a ViewModelLocator that will be hosting our Ninject kernel in the application scope.

This means we are going to host our Locator as a resource in our App.xaml resource dictionary and bind to it in our views, using the StaticResource as a source.

You would go about doing this as following, and reference it in your code
<Application.Resources> 
<Foo:ViewModelLocator x:Key="viewModelLocator"/>
</Application.Resources>

<UserControl DataContext="{Binding [Bar], Source={StaticResource viewModelLocator}}"/>

You can see that we have defined our ViewModel with the name ‘Bar’ and within square brackets, this means we are binding to an indexer of the ViewModelLocator.  This allows us to do some pretty cool stuff, on the expense of loosing Intelli-Sense.
So lets get started on writing our locator, we first start my setting up our class and initializing some Ninject stuff, creating the Kernel, adding modules,…
public class ViewModelLocator
{       
private readonly IKernel kernel;       
public ViewModelLocator()
{           
kernel = new StandardKernel(GetModules());           
kernel.Load();       
}
}

So the really cool thing we can do now, is create an indexer that will allow us the query our ViewModels setup in our Kernel dynamically without having to type it, and add a new Property for every ViewModel.

The indexer is pretty straightforward, it looks like this :
public object this[string viewModel]
{
get { return GetViewModel(viewModel); }
}

So as you could imagine, the real stuff goes on in the GetViewModel method, so lets have a quick look at that now, I'll explain what we are doing here afterwards.
private object GetViewModel(string viewModel)
{
String viewModelName = viewModel;
if (!viewModel.ToUpper().EndsWith("VIEWMODEL"))
{
viewModelName = viewModelName + "ViewModel";
}
return kernel.Get(GetViewModelType(viewModelName));
}

private Type GetViewModelType(string viewModelname)
{
foreach (string location in GetLocations())
{
Type type = Type.GetType(string.Format("{0}.{1}", location, viewModelname), false, true);
if (type != null)
return type;
}
return null;
}

When we are calling our “Bar” viewmodel, the GetViewModel method will get a string with the value '”Bar”, from this it will need to locate the ViewModel from our kernel which could have a whole bunch of dependency’s injected ( or should I say nInjected ) into it.

Our locator will will take into account a few conventions, it will check the ViewModel string ends with “ViewModel” and if not will append it.  It will then attempt to find the ViewModel Type and return the corresponding instance of that type from the Ninject kernel.

This is where our last convention comes into play, our ViewModels in our application will most likely be located on some very well defined locations (hence the GetLocations method). We can easily define these conventions and possibly even add new ones later, a simple convention for locations might look something like this :
 private IEnumerable GetLocations()
{
string applicationNamespace = GetType().Namespace;
yield return applicationNamespace;
yield return applicationNamespace + ".Shared";
yield return applicationNamespace + ".Shared.ViewModel";
yield return applicationNamespace + ".Views";
yield return applicationNamespace + ".Views.ViewModel";
}

So this way you can add ViewModels with Ninject without having to define any properties to bind to, you just have to type your ViewModel’s name and the locator will do the rest for you.

No comments:

Post a Comment