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?
I guess you have a dependency that you want to constructor inject like this:
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:
or that:
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
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.
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:
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: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.
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
usingApplication.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.Both ways are valid options, and with a little key management, a mix and match can be used to suit requirements.