119

Can someone give me a quick summary of what a ViewModelLocator is, how it works, and what the pros/cons are for using it compared to DataTemplates?

I have tried finding info on Google but there seems to be many different implementations of it and no striaght list as to what it is and the pros/cons of using it.

Jon
  • 428,835
  • 81
  • 738
  • 806
Rachel
  • 130,264
  • 66
  • 304
  • 490

3 Answers3

219

Intro

In MVVM the usual practice is to have the Views find their ViewModels by resolving them from a dependency injection (DI) container. This happens automatically when the container is asked to provide (resolve) an instance of the View class. The container injects the ViewModel into the View by calling a constructor of the View which accepts a ViewModel parameter; this scheme is called inversion of control (IoC).

Benefits of DI

The main benefit here is that the container can be configured at run time with instructions on how to resolve the types that we request from it. This allows for greater testability by instructing it to resolve the types (Views and ViewModels) we use when our application actually runs, but instructing it differently when running the unit tests for the application. In the latter case the application will not even have a UI (it's not running; just the tests are) so the container will resolve mocks in place of the "normal" types used when the application runs.

Problems stemming from DI

So far we have seen that the DI approach allows easy testability for the application by adding an abstraction layer over the creation of application components. There is one problem with this approach: it doesn't play well with visual designers such as Microsoft Expression Blend.

The problem is that in both normal application runs and unit test runs, someone has to set up the container with instructions on what types to resolve; additionally, someone has to ask the container to resolve the Views so that the ViewModels can be injected into them.

However, in design time there is no code of ours running. The designer attempts to use reflection to create instances of our Views, which means that:

  • If the View constructor requires a ViewModel instance the designer won't be able to instantiate the View at all -- it will error out in some controlled manner
  • If the View has a parameterless constructor the View will be instantiated, but its DataContext will be null so we 'll get an "empty" view in the designer -- which is not very useful

Enter ViewModelLocator

The ViewModelLocator is an additional abstraction used like this:

  • The View itself instantiates a ViewModelLocator as part of its resources and databinds its DataContext to the ViewModel property of the locator
  • The locator somehow detects if we are in design mode
  • If not in design mode, the locator returns a ViewModel that it resolves from the DI container, as explained above
  • If in design mode, the locator returns a fixed "dummy" ViewModel using its own logic (remember: there is no container in design time!); this ViewModel typically comes prepopulated with dummy data

Of course this means that the View must have a parameterless constructor to begin with (otherwise the designer won't be able to instantiate it).

Summary

ViewModelLocator is an idiom that lets you keep the benefits of DI in your MVVM application while also allowing your code to play well with visual designers. This is sometimes called the "blendability" of your application (referring to Expression Blend).

After digesting the above, see a practical example here.

Finally, using data templates is not an alternative to using ViewModelLocator, but an alternative to using explicit View/ViewModel pairs for parts of your UI. Often you may find that there's no need to define a View for a ViewModel because you can use a data template instead.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • 4
    +1 for a great explanation. Can you expand further on the View and its Resources? By Resources, do you mean the View's properties? Or? Do you have a link with a concrete example to this pattern? – Metro Smurf Mar 28 '11 at 16:58
  • @MetroSmurf: Your link is in the Summary section. – Jon Mar 28 '11 at 16:59
  • 1
    Thanks. Are there any limitations to using a ViewModelLocator? I had some concerns about the fact it referenced a static resource - can ViewModels be created dynamically at runtime? And is there a lot of extra code involved hooking one up? – Rachel Mar 29 '11 at 14:01
  • @Rachel: That same link in the Summary should answer these questions with practical examples. – Jon Mar 29 '11 at 15:05
  • 2
    Extremely misleading answer. The primary purpose of View Model Locator is not to provide dummy data to the designer. You can easily do this by specifying `d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"`. The purpose of the Locator is to actually enable DI on the Views, because WPF is so bad at providing it. Example: you have a Main Window which opens some Dialog Window. To solve the DI on the Dialog Window in the usual way, you would need to pass it as a dependency on the Main Window! This is avoided with the View Locator. – hyankov Dec 31 '16 at 17:32
  • See also this http://stackoverflow.com/questions/25366291/how-to-handle-dependency-injection-in-a-wpf-mvvm-application and this http://blog.qmatteoq.com/the-mvvm-pattern-dependency-injection/ – hyankov Dec 31 '16 at 17:33
  • "*The container injects the ViewModel into the View by calling a constructor of the View which accepts a ViewModel parameter; this scheme is called inversion of control (IoC)*" This is just calling a constructor with a parameter, nothing new here. IoC is the fact the view raises events processed by the view model, meaning the view will control when the code in the view model is executed. This is reversed compared to procedural programming. IoC is nearly synonymous of event-driven when talking about a view and its model. – mins Jun 06 '20 at 14:00
  • First paragraph and I don't understand. Don't you set a DataContext in a window by using `` in XAML? and use `d:DataContext` for `UserControl` ? Confusing! – Piotr Golacki Sep 15 '22 at 13:13
10

An example implementation of @Jon's answer

I have a view model locator class. Each property is going to be an instance of the view model that I'm going to allocate on my view. I can check if the code is running in design mode or not using DesignerProperties.GetIsInDesignMode. This allows me to use a mock model during designing time and the real object when I'm running the application.

public class ViewModelLocator
{
    private DependencyObject dummy = new DependencyObject();

    public IMainViewModel MainViewModel
    {
        get
        {
            if (IsInDesignMode())
            {
                return new MockMainViewModel();
            }

            return MyIoC.Container.GetExportedValue<IMainViewModel>();
        }
    }

    // returns true if editing .xaml file in VS for example
    private bool IsInDesignMode()
    {
        return DesignerProperties.GetIsInDesignMode(dummy);
    }
}

And to use it I can add my locator to App.xaml resources:

xmlns:core="clr-namespace:MyViewModelLocatorNamespace"

<Application.Resources>
    <core:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>

And then to wire up your view (ex: MainView.xaml) to your viewmodel:

<Window ...
  DataContext="{Binding Path=MainViewModel, Source={StaticResource ViewModelLocator}}">
Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
BrunoLM
  • 97,872
  • 84
  • 296
  • 452
9

I don't understand why the other answers of this question wrap around the Designer.

The purpose of the View Model Locator is to allow your View to instantiate this (yes, View Model Locator = View First):

public void MyWindowViewModel(IService someService)
{
}

instead of just this:

public void MyWindowViewModel()
{
}

by declaring this:

DataContext="{Binding MainWindowModel, Source={StaticResource ViewModelLocator}}"

Where ViewModelLocator is class, which references a IoC and that's how it solves the MainWindowModel property it exposes.

It has nothing to do with providing Mock view models to your view. If you want that, just do

d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"

The View Model Locator is a wrapper around some (any) Inversion of Control container, such as Unity for example.

Refer to:

Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
hyankov
  • 4,049
  • 1
  • 29
  • 46
  • A view model locator does not require a container, the user decides how the view model is resolved via configuration and you can use a container or just new up a type yourself. So you can do convention based view model location, for example instead of pre-registering all your views and view models in some container. – Chris Bordeman Sep 20 '17 at 20:51
  • You're right when saying "*I don't understand why the other answers [...] wrap around the Designer*" +1, but the point of the locator is to remove from the view any knowledge of how the view model is be created, making the view independent of this instantiation, leaving that to the locator. The locator will be able to provide different flavors of the view model, maybe some custom ones added via plugins the locator will manage (and a specific one for design time). The view will be clean of any of this process of locating the correct version of the view model, that's indeed good for SoC. – mins Jun 06 '20 at 14:22