Methods for providing view model dependencies

Topics: Bootstrappers & IoC, UI Architecture
Dec 12, 2013 at 9:27 PM
Edited Dec 12, 2013 at 11:30 PM
I've been doing some reading on the "correct" method of resolving view model dependencies throughout my CM application. Having read an article [1] on Stack Overflow regarding this, I was not convinced that was the right approach as it appeared to be an implementation of the Service Locator anti-pattern. I also later read the bootstrapper customisation article here [2] which stipulates about half way through the article not to directly to pull directly from the container.

So what is the suggested method of supplying view model dependencies to view models? Is it simply provide them as constructor arguments as I would any other object creation dependency, or is there some swanky feature I am missing?

Many thanks.

[1] http://stackoverflow.com/questions/15709207/navigation-with-caliburn-micro
[2] https://caliburnmicro.codeplex.com/wikipage?title=Customizing%20The%20Bootstrapper
Dec 16, 2013 at 7:45 AM
Let's say a thing first: being a purist is sometimes not feasible, and in many cases it can cause too many hassles.

That said, the most correct way to define dependencies, in my opinion, is providing them at the constructor level. Both MEF and simple container implementations for CM are able to resolve constructor-time dependencies, so there is nothing new to implement. The advantage of this approach is that dependencies are explicit, and you know beforehand what kind of objects the class depends on.

There are cases where this approach cannot be used directly, or is not conventient: consider the case where your class needs to retrieve an object at runtime after it is has been built, or too many dependencies are required, or the class needs to build a specific instance depending on a parameter. In such cases, it is possible define a 'container' (such as a delegate or a specific service) used to used to pull other dependencies.
/// Delegate used to generate a view-model, depending on a parameter.
public delegate ISpecificViewModel CreateViewModel(object paramter);

/// Specific service interface, used to generate a view-model, depending on a parameter.
public interface ISpecificViewModelCreator
{
      ISpecificViewModel Create(object parameter);
}
Using constructor-level dependencies, and sub-services for edge cases, you can avoid to access the IoC directly... that said... there are still cases where this approach can be quite a bother, expecially if you deal with some widely-used dependencies (e.g. the service used to display message-boxes). I tend to use constructor-level (explicit) dependencies, but sometimes I try to ignore the 'smell' of using the IoC directly. ;)
Dec 17, 2013 at 11:29 AM
Edited Dec 17, 2013 at 11:30 AM
Thanks for taking the time to respond Blade, it is appreciated.

Reading your response and reading back my question I think it was unclear exactly what I was asking, so left me clarify.

Say for example I have five views (each with their own viewmodel):
* FirstView
* SecondView
* ThirdView
* FourthView
* FifthView
FirstView has the ability to display in its ContentControl either SecondView or ThirdView, SecondView and ThirdView can themselves display in their ContentControl either FourthView or FifthView respectively.

What would be the correct method for providing FirstView, SecondView and ThirdView with access to their required views so that they could display them within their ContentControl?

I see in the HelloScreens demo that the ShellViewModel is supplied an IEnumerable<IWorkspace> which contains all the objects which derive from Screen, however, there does not appear to be Screen nesting and the activating and deactivating of screens is managed only by ShellViewModel. Or have I missed something?
Oct 1, 2014 at 6:59 PM
The only way I have found to do what you're wanting to do is:

1) Include resolution of the container in the view's constructor and store the container internally
2) Resolve the view using Prism Region
3) In your view's Loaded event handler, use the container to resolve the view model and set that as your data context.

The drawback to this method are as follows:
1) You have to store the container reference internally and use it (not too bad)
2) You have to define an interface specific to the view model so it can be resolved
3) You don't get the benefit of design-time data context awareness when defining bindings to the data context, since the loaded event won't be called.

i.e.:
public interface IShellViewModel
{
    // No requirements
}

public partial class Shell : Window
{
    private IUnityContainer _container { get; set; }

    public Shell(IUnityContainer container)
    {
        _container = container;
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        this.DataContext = _container.Resolve<IShellViewModel>();
    }
}
Oct 1, 2014 at 7:00 PM
The only way I have found to do what you're wanting to do is:

1) Include resolution of the container in the view's constructor and store the container internally
2) Resolve the view using Prism Region
3) In your view's Loaded event handler, use the container to resolve the view model and set that as your data context.

The drawback to this method are as follows:
1) You have to store the container reference internally and use it (not too bad)
2) You have to define an interface specific to the view model so it can be resolved
3) You don't get the benefit of design-time data context awareness when defining bindings to the data context, since the loaded event won't be called.

i.e.:
public interface IShellViewModel
{
    // No requirements
}

public partial class Shell : Window
{
    private IUnityContainer _container { get; set; }

    public Shell(IUnityContainer container)
    {
        _container = container;
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        this.DataContext = _container.Resolve<IShellViewModel>();
    }
}