Home > English > Implementing View First using a ViewModelLocator

Implementing View First using a ViewModelLocator

A while back I did a post on a way to use dependency injection (DI) to handle View first approach to your MVVM development. Today, I am going to be talking about another approach that is Blend friendly if you are wanting to go View first and still have your VieModel created with dependencies. For this example, I am going to use a simple data entry screen that will be composed of DataEntryView and corresponding DataEntryViewModel.

Before we get started, I want to show you the most common approach for implementing the View first in Xaml:

<UserControl 
    x:Class="CodeCamp2010.Calc.Views.DataEntryView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:l="clr-namespace:CodeCamp2010.Calc"
    >
    <UserControl.DataContext>
        <l:DataEntryViewModel />
    </UserControl.DataContext>
    <Grid x:Name="LayoutRoot">
        ...
    </Grid>
</UserControl>

Here is a sample of the ViewModel:

public class DataEntryViewModel
{

    #region ctor
    public DataEntryViewModel()
    {
    }
    #endregion

    Property Bindings...

};

If you only need to use your default constructor of the ViewModel, this is a quick and easy way to wire you View and ViewModel together. This does have its limitations since it will only work with the default constructor.

Now let’s examine how we would go about facilitating dependency injection in the ViewModel. I will be using Unity as my container since I am using Prism. I will also be use the ServiceLocator from Prism. Let’s look at what it takes to register these two objects in Unity.

public class CalcModule : IModule
{
    private IUnityContainer _container;
    private IRegionManager _regionManager;
    private IEventAggregator _eventAggregator;

    #region ctor
    public CalcModule(IUnityContainer container, IRegionManager regionManager, IEventAggregator eventAggregator)
    {
        _container = container;
        _regionManager = regionManager;
        _eventAggregator = eventAggregator;
    }
    #endregion

    public void Initialize()
    {
        _container.RegisterType<DataEntryView>();
        _container.RegisterType<DataEntryViewModel>();       

        _regionManager.RegisterViewWithRegion("MainRegion", typeof(DataEntryView));
    }
};

In Prism, every separate module or assembly that you create needs to have a class that implements IModule. This interface requires a single Initialize() method. It is in this method that I am registering and also requesting the view to be displayed.

Now let’s look at the DataEntryView:

<UserControl 
    x:Class="CodeCamp2010.Calc.Views.DataEntryView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:l="clr-namespace:CodeCamp2010.Calc"
    >
    <UserControl.Resources>
        <l:ViewModelLocator x:Key="ViewModelLocator" />
    </UserControl.Resources>
    <UserControl.DataContext>
        <Binding Path="DataEntryViewModel" Source="{StaticResource ViewModelLocator}" />
    </UserControl.DataContext>
    <Grid x:Name="LayoutRoot">
        ...
    </Grid>
</UserControl>

We see that we have a new entry compared to the previous view example. We now have a UserControl.Resources entry and it is providing a ViewModelLocator resource. We then see how the DataContext uses this new Resource and requests the DataEntryViewModel as its path.

Let’s now look at the ViewModelLocator class:

public class ViewModelLocator
{
    private IUnityContainer _container;
    private IUnityContainer Container
    {
        get
        {
            if (_container == null)
                _container = ServiceLocator.Current.GetInstance<IUnityContainer>();
            return _container;
        }
    }

    public DataEntryViewModel DataEntryViewModel
    {
        get
        {
            return Container.Resolve<DataEntryViewModel>();
        }
    }
};

This class is very simple. It basically is your one-stop shopping for all of your ViewModels that need to be referenced by your Views in your module. It uses the ServiceLocator to get an instance of IUnityContainer. Then it simple uses the Container to resolve for the corresponding type that you need.

Finally, here is an exmple of the DataEntryViewModel with an overloaded constructor:

public class DataEntryViewModel
{
    private IUnityContainer _container;
    private IRegionManager _regionManager;
    private IEventAggregator _eventAggregator;

    #region ctor
    public DataEntryViewModel(IUnityContainer container, 
        IRegionManager regionManager, 
        IEventAggregator eventAggregator)
    {
        _container = container;
        _regionManager = regionManager;
        _eventAggregator = eventAggregator;
    }
    #endregion

    Property Bindings...

};

Hopefully, this gives you an idea as to another alternative to implementing your View first approach and still using the power of dependency injection.

Advertisements
Categories: English Tags: , , ,
  1. Simon
    December 7, 2010 at 2:32 am

    Putting the ViewModelLocator in the UserControl.Resources and setting the DataContext on the user control doesn’t appear to work in Cider, the VS designer. I suspect the DataContext is being set before the resources are evaluated, regardless of the order they appear in the XAML. You can test it by doing a MessageBox.Show in the ViewModelLocator.DataEntryViewModel getter.

    Setting the DataContext in child LayoutRoot container does work. I’d prefer to set the DataContext on the entire UserControl though.

  2. September 23, 2011 at 1:31 pm

    Very helpful, thanks for sharing!

  3. asaon
    July 29, 2014 at 11:05 am

    Thank you!

  1. No trackbacks yet.

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

%d bloggers like this: