1

I really like the flexibility of declaring my ViewModel on the DataContext of a View through XAML, but I am having a hard time figuring out how I can tie this ViewModel into the rest of the system.

Ex.

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ViewModels">

<Window.DataContext>
    <local:MainViewModel />
</Window.DataContext>    

The problem here, is that my ViewModels will often have and share dependencies with other parts of the system, and I am not sure how I can inject a reference of another object into the MainViewModel declared above.

If I were doing a manual sort of Dependency Injection, then I would create a bunch of factories at the start of the application responsible for wiring everything up.

How can I take this same approach in a MVVM WPF application? And what is the most effective way to get a handle on all of my ViewModels?

Nicholas
  • 3,286
  • 5
  • 27
  • 35

5 Answers5

2

You can use a view model first approach, where you instantiate your view models (injecting any dependencies through constructor injection), and then instantiate your view, and set its DataContext to your view model instance programmatically.

I use the Caliburn.Micro framework which does view location and binding through conventions automatically. You can find it at http://caliburnmicro.codeplex.com/

Rob Eisenburg's original Build Your Own MVVM Framework talk gives an excellent overview of what became Caliburn.Micro - http://live.visitmix.com/MIX10/Sessions/EX15

devdigital
  • 34,151
  • 9
  • 98
  • 120
  • I will take a closer look at your framework, but I am trying to avoid coupling XAML to startup code. Ideally I would be able to use the method above to create a ViewModel and still be able to inject dependencies via properties. In this way, my start up does not have to be concerned with the view at all, and those can be swapped in at will. In fact, just talking about this right now makes me think that I may be able to perform most of this start up in the App file before my MainWindow is initialized. Either way, I appreciate your response. Thanks. – Nicholas Jan 05 '11 at 02:33
  • With Caliburn.Micro, you can specify the starting view model as the type of a generic BootStrapper, so you can easily swap it out. In fact, Caliburn.Micro will use whichever IoC container you have configured to resolve the starting view model, so you can specify an abstraction as the BootStrapper type, and this will be resolved via the container on startup, so you can swap this out even more simply. See http://caliburnmicro.codeplex.com/wikipage?title=Basic%20Configuration,%20Actions%20and%20Conventions – devdigital Jan 05 '11 at 10:52
  • OK, this is pretty cool. It's similar to auto-wiring, in that it resolves View - ViewModel dependencies through naming convention. Typically I try to avoid this sort of thing because it swaps out one problem with another. In this case I don't necessarily want my View to be semantically liked to my ViewModel by name. That being said, it still seems to solve my question pretty well. – Nicholas Jan 05 '11 at 13:45
2

To achieve complete decoupling, set ViewModels on the ResourceDictionary found on the main App class. There are two ways to do this, and for the most part it doesn't matter which method is used. There are trade-offs however.

Method 1

If it is done progamatically, you must ensure Dictionary keys match. This causes a weak coupling between the strings defined in the XAML and those defined programmatically. Not ideal, but not the end of the world either. The advantage here is that ability to use constructor injection.

//App.xaml.cs
//This line invoked somewhere after App OnStartUp function is called. 
MainViewModel mainViewModel = new MainViewModel( new Dependency() );
Current.ResourceDictionary["MainViewModel"] = mainViewModel; 

//MainView.xaml
<Window DataContext="{StaticResource MainViewModel}">

The Business logic doesn't care what a View is, and the application can be Bootstrapped in any way... using factory/builder object or any IOC container. (As long as it all starts in the OnStartUp function).

Method 2

Define ViewModels in App.xaml using Application.Resource. Using this method all key names will be located in XAML, which feels pretty nice. The only negative result is that .NET automatically builds the ViewModels, forcing the to provide default constructors. Sometimes it is desirable for the IOC container to build your objects, or use Constructor Injection in custom factories/builders.

//App.xaml
<Application 
    xmlns:local="clr-namespace:ViewModels" />

<Application.Resources>
     <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary>
                <local:MainViewModel x:key="MainViewModel" />
            </ResourceDictionary>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

//App.xaml.cs
MainViewModel mainViewModel = Current.ResourceDictionary["MainViewModel"];
mainViewModel.propertyInjection = new Dependency();

//MainView.xaml
<Window DataContext="{StaticResource MainViewModel}">

Both ways are valid options, and with a little key management, a mix and match can be used to suit requirements.

Nicholas
  • 3,286
  • 5
  • 27
  • 35
1

I like to use a DataTemplate, defined in a resource dictionary within App.xaml (or elsewhere), to map a ViewModel to a View like this:

<DataTemplate DataType="{x:Type vm:CustomerViewModel}">
    <vw:CustomerView />
</DataTemplate>

This has the effect of automatically assigning the ViewModel to be the view's DataContext at runtime. In this case I would instantiate an object called,say, myCustomerViewModel, in the ViewModel for the window or other user control that would host the CustomerView, and then in the View for that window, use a ContentControl like this:

<ContentControl Content="{Binding Path=myCustomerViewModel}" />
echo
  • 7,737
  • 2
  • 35
  • 33
0

I guess you have a dependency that you want to constructor inject like this:

class MainViewModel
{
    public interface IDependency { };
    readonly IDependency _dep;
    public MainViewModel (IDependency dep)
    {
        if (dep == null)
            throw new ArgumentNullException("IDependency dep cannot be null");
        _dep = dep;
    }
}
  1. Is that what you are trying to do??

Secondly, to my understanding to be able to use your MainViewModel in XAML, you need to provide a default constructor, which in combination with the injected dependency is a "Bastard Injection": What is the real difference between "Bastard Injection" and "Poor Man's Injection"

So far I have not seen a way to avoid "Bastard Injection" and still be able to this:

<Window.DataContext>
    <local:MainViewModel />
</Window.DataContext> 

or that:

<UserControl.Resources>
    <DataTemplate DataType="{x:Type vm:ProgrammingViewModel}">
        <local:ProgrammingView />
    </DataTemplate>
</UserControl.Resources>
Community
  • 1
  • 1
0

I create a view model for the entire application and either make it the data context of my main window or (if I'm not going to have just one main window) stick it in the application's resource dictionary. All of the startup code goes inside of the application view model's constructor, and the AVM exposes other view models as properties so that they can be accessed via binding.

It's kind of a blunt instrument, but it's got the virtue of simplicity.

Robert Rossney
  • 94,622
  • 24
  • 146
  • 218