How to Handle Dependency Injection in a Wpf/Mvvm Application

How to handle dependency injection in a WPF/MVVM application

I have been using Ninject, and found that it's a pleasure to work with. Everything is set up in code, the syntax is fairly straightforward and it has a good documentation (and plenty of answers on SO).

So basically it goes like this:

Create the view model, and take the IStorage interface as constructor parameter:

class UserControlViewModel
{
public UserControlViewModel(IStorage storage)
{

}
}

Create a ViewModelLocator with a get property for the view model, which loads the view model from Ninject:

class ViewModelLocator
{
public UserControlViewModel UserControlViewModel
{
get { return IocKernel.Get<UserControlViewModel>();} // Loading UserControlViewModel will automatically load the binding for IStorage
}
}

Make the ViewModelLocator an application wide resource in App.xaml:

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

Bind the DataContext of the UserControl to the corresponding property in the ViewModelLocator.

<UserControl ...
DataContext="{Binding UserControlViewModel, Source={StaticResource ViewModelLocator}}">
<Grid>
</Grid>
</UserControl>

Create a class inheriting NinjectModule, which will set up the necessary bindings (IStorage and the viewmodel):

class IocConfiguration : NinjectModule
{
public override void Load()
{
Bind<IStorage>().To<Storage>().InSingletonScope(); // Reuse same storage every time

Bind<UserControlViewModel>().ToSelf().InTransientScope(); // Create new instance every time
}
}

Initialize the IoC kernel on application startup with the necessary Ninject modules (the one above for now):

public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
IocKernel.Initialize(new IocConfiguration());

base.OnStartup(e);
}
}

I have used a static IocKernel class to hold the application wide instance of the IoC kernel, so I can easily access it when needed:

public static class IocKernel
{
private static StandardKernel _kernel;

public static T Get<T>()
{
return _kernel.Get<T>();
}

public static void Initialize(params INinjectModule[] modules)
{
if (_kernel == null)
{
_kernel = new StandardKernel(modules);
}
}
}

This solution does make use of a static ServiceLocator (the IocKernel), which is generally regarded as an anti-pattern, because it hides the class' dependencies. However it is very difficult to avoid some sort of manual service lookup for UI classes, since they must have a parameterless constructor, and you cannot control the instantiation anyway, so you cannot inject the VM. At least this way allows you to test the VM in isolation, which is where all the business logic is.

If anyone has a better way, please do share.

EDIT:
Lucky Likey provided an answer to get rid of the static service locator, by letting Ninject instantiate UI classes. The details of the answer can be seen here

WPF MVVM Dependency Injection Constructors exception

WPF requires objects which are instantiated in XAML to define a parameterless constructor.

There are many ways to accomplish Dependency Injection in WPF using MVVM. When searching the internet the most wide spread solution seems to be the ViewModelLocator, another Service Locator implementation which is considered widely an anti-pattern (like the infamous static Singleton IoC container).

A simple solution is to use composition. You create a main view model which is composed of other view models where each is dedicated to a certain view.

MainViewModel.cs

class MainViewModel
{
public MainViewModel(IFirstControlViewModel firstControlViewModel ,
ISecondControlViewModel secondControlViewModel)
{
this.FirstControlViewModel = firstControlViewModel;
this.SecondControlViewModel = secondControlViewModel;
}

public IFirstControlViewModel FirstControlViewModel { get; }
public ISecondControlViewModel SecondViewModel { get; }
}

FirstViewModel.cs

class FirstViewModel : IFirstViewModel
{
}

SecondViewModel.cs

class SecondViewModel : ISecondViewModel
{
public SecondVieModel(IThirdViewModel thirdViewModel) => this.ThirdViewModel = thirdViewModel;

public IThirdViewModel ThirdViewModel { get; }
}

MainWindow.xaml

<Window>
<StackPanel>
<FirstUserControl DataContext="{Binding FirstViewModel}" />
<SecondUserControl DataCOntext="{Binding SecondViewModel}" />
</StackPanel>
</Window>

SecondUserControlxaml

<UserControl>
<Grid>
<ThirdUserControl DataContext="{Binding ThirdViewModel}" />
</Grid>
</UserControl>

App.xaml.cs

private void Run(StartupEventArgs e)
{
IMainViewModel viewModel = container.GetExportedValue<IMainViewModel>();
var mainWindow = new MainWindow { DataContext = viewModel };
mainWindow.Show();
}

Or use only top-level composition:

MainViewModel.cs

class MainViewModel
{
public MainViewModel(IFirstControlViewModel firstControlViewModel ,
ISecondControlViewModel secondControlViewModel,
IThirdViewModel thirdViewModel)
{
this.FirstControlViewModel = firstControlViewModel;
this.SecondControlViewModel = secondControlViewModel;
this.ThirdViewModel = thirdViewModel;
}

public IFirstControlViewModel FirstControlViewModel { get; }
public ISecondControlViewModel SecondViewModel { get; }
public IThirdViewModel ThirdViewModel { get; }
}

App.xaml.cs

private void Run(StartupEventArgs e)
{
IMainViewModel viewModel = container.GetExportedValue<IMainViewModel>();

// For simplicity, you can add the view model to the globally accessible App.xaml ResourceDictionary
this.Resources.Add("MainViewModel", viewModel);

var mainWindow = new MainWindow { DataContext = viewModel };
mainWindow.Show();
}

SecondUserControlxaml

<UserControl>
<Grid>
<ThirdUserControl DataContext="{Binding Source="{StaticResource MainViewModel}", Path=ThirdViewModel}" />
</Grid>
</UserControl>

Composition is a very simple solution to use Dependency Injection with views. If performance e.g. classes with a big dependency tree is an issue many DI frameworks like MEF support Lazy<T> exports.

WPF MVVM: Dependency Injection Without Constructor Overloading Many Parameters

A simple example how to wire up your WPF application when using Dependency Injection. It also shows how to register a factory (in this case a delegate) to enable dynamic instance creation. The idea is to let the container create any instances for you. DI allows you to avoid the new keyword and therefore elimintaes your responsibility to care for constructors and their dependencies.

The general recommended approach in WPF to display views is the view-model-first principle: basically, add data models to the view (for example by binding the view model to a ContentControl or ContentPresenter) and use a DataTemplate to let the framework load the related view.

App.xaml.cs

private async void OnStartup(object sender, StartupEventArgs e)
{
var services = new ServiceCollection();

// Look how you can chain the service registrations:
services.AddSingleton<IMainViewModel, MainViewModel>()
.AddSingleton<IRepository, Repository>()
.AddSingleton<IViewModel1, ViewModel1>()

// Factory is used to create instances dynamically.
// Alternatively, instead of a delegate you can register a factory class.
.AddSingleton<Func<IViewModel1>>(serviceProvider => serviceProvider.GetService<IViewModel1>)

// Register a factory delegate taking an argument
.AddSingleton<Func<User, IViewModel2>>(serviceProvider => user =>
{
var repository = serviceProvider.GetService<IRepository>();
var dependency1 = serviceProvider.GetService<IDependency1>();
var dependency2 = serviceProvider.GetService<IDependency2>();
return new ViewModel2(
repository, dependency1, dependency2, user);

// Alternativelyly set property instead of using constructor
var viewModel2 = serviceProvider.GetService<IViewModel2>();
viewModel2.User = user;
return viewModel2;
})

// Alternatively register an abstract factory
.AddSingleton<IViewModel2Factory, ViewModel2Factory>()

.AddSingleton<IDependency1, Dependency1>()
.AddSingleton<IDependency2, Dependency2>()
.AddSingleton<IViewModel2, ViewModel2>()
.AddSingleton<IRepository, Repository>()
.AddSingleton<MainView>();

ServiceProvider container = services.BuildServiceProvider();

// Let the container compose the dependency graph
// and export the MainView to start the GUI
var mainWindow = container.GetService<MainView>();

// Launch the main view
mainWindow.Show();
}

ViewModel2Factory.cs

class ViewModel2Factory : IViewModel2Factory
{
private IRepository Repository { get; }
private Func<IRepository> RepositoryFactory { get; }
private Func<IDependency1> Dependency1Factory { get; }
private Func<IDependency2> Dependency2Factory { get; }

public (
IRepository repository,
IDependency1 dependency1,
IDependency2 dependency2)
{
// TDODO::Initialize properties
}

public IViewModel2 Create(User user)
{
var repository = this.RepositoryFactory.Invoke();
var dependency1 = this.Dependency1Factory.Invoke();
var dependency2 = this.Dependency2Factory.Invoke();
return new ViewModel2(repository, dependency1, dependency2, user);
}
}

MainViewModel.cs

class MainViewModel : IMainViewModel
{
public IViewModel2 ViewModel2 { get; }
private IViewModel1 ViewModel1 { get; }
private Func<IViewModel1> TabItemFactory { get; }

public MainViewModel(
IViewMode1 viewModel1,
Func<IViewModel1> viewModel1Factory,
IViewModel2 viewModel2)
{
this.ViewModel = viewModel2; // public read-only for data binding
this.ViewModel1 = viewModel1; // private read-only for internal use only
this.TabItemFactory = viewModel1Factory; // private read-only for internal use only
}

private void ExecuteAddTabCommand(object commandParameter)
{
// Uses a shared instance for every tab
this.Tabs.Add(this.ViewModel1);

// Create a fresh instance for every tab using a factory
IViewModel1 tabViewModel = this.TabItemFactory.Invoke();
this.Tabs.Add(tabViewModel);
}
}

MainWindow.xaml.cs

partial class MainWindow : Window
{
public MainWindow(IMainViewModel mainViewModel)
{
InitializeComponent();

this.DataContext = mainViewModel;
}
}

MainWindow.xaml

<Window>

<!-- Bind to a nested view model of the MainViewModel -->
<MyUserControl DataContext="{Binding ViewModel2}" />
</Window>

ViewModel1.cs

class ViewModel1 : IViewModel1
{
private IRepository Repository { get; }
private IViewModel2Factory ViewModel2Factory { get; }

public ViewModel1(IRepository repository, IViewModel2Factory viewModel2Factory)
{
this.Repository = repository; // private read-only for internal use only
this.ViewModel2Factory = viewModel2Factory;
}

public void CreateViewModel2()
{
IViewModel2 newTab = this.ViewModel2Factory.Create(this.User);
}
}

ViewModel2.cs

class ViewModel2 : IViewModel2
{
public ViewModel2(
IRepository repository,
IDependency1 dependency1,
IDependency2 dependency2,
User user)
{
...
}
}

How to inject dependency into MVVM View Model class?

Some times going back to basics and keeping things simple (KISS) tends to work.

What comes to mind for this scenario is The Explicit Dependency Principle and Pure Dependency Injection.

The MainViewModel is doing way too much as evident by either injecting the container (big no no) or have way to many dependencies (code smell). Try to narrow down what it is that class is suppose to be doing (SRP)

So let's say the main view model needs a collection of panes. Then why not give it what it needs.

public class MainViewModel : ViewModelBase {
public ObservableCollection<DockPaneViewModel> DockPanes { get; set; }

//Give the view model only what it needs
public MainViewModel(IEnumerable<DockPaneViewModel> panes) {
DockPanes = new ObservableCollection<DockPaneViewModel>(panes);
}

public void ResetPanes() {
foreach (var pane in DockPanes) {
pane.Reset();
}
//notify view
}
}

Note the slight change to the base panel

public abstract class DockPaneViewModel : ViewModelBase {
// ...

public virtual void Reset() {
//...
}
}

The main view model should not concern itself with how the dependencies are created. It only cares that it gets what it explicitly asks for.

The same applies to the different pane implementations.

If a view model needs to be able to create multiple children then delegate that responsibility out to a factory.

public class MonitorPageViewModel : DockPaneViewModel {
public MonitorPageViewModel(ILogger logger, IRepository<RawMessage> repository,
IRepository<Parser> parserRepository, IParsingService parsingService,
IPaneFactory factory) {
// ...
}

public void CreateDashboard() {
var dashBoardVm = factory.Create<MonitorDashboardViewModel>();

//...
}
}

Again the subject should have as few responsibilities as possible.

View First or ViewModel First are considered implementation concerns and really does not matter if following a convention over configuration model.

If the design is well done, it really does not matter whether you use a framework or pure code.

Those frameworks though, do come in handy when it comes to putting everything together. The most simple and elegant solution is to have something create the object graph, but without that something, you are left to build it up yourself in the composition root.

How to handle dependency injection with WPF with multiple pages and navigation?

There are many possible solutions. A simple one is to introduce an event.

I also recommend to move and restrict the responsibility to select view models to the MainViewModel. Other page models should not be aware of the flow like who selects who. Otherwise this would add a too tight coupling, which is avoidable at this point.

public class MainViewModel 
{
public IPageViewModel SelectedPage { get; set; }
private Dictionary<string, IPageViewModel> Pages { get; }

public MainViewModel(AuthViewModel authViewModel, HomeViewModel homeViewModel)
{
authViewModel.AuthenticationPassed += OnAuthenticationSuccessfull;

this.Pages = new Dictionary<string, IPageViewModel>()
{
{ nameof(AuthViewModel), authViewModel },
{ nameof(HomeViewModel), homeViewModel }
};

this.SelectedPage = this.Pages[nameof(AuthViewModel)];
}

public OnAuthenticationSuccessfull(object sender, EventArgs e)
{
(sender as AuthViewModel).AuthenticationPassed -= OnAuthenticationSuccessfull;

this.SelectedPage = this.Pages[nameof(HomeViewModel)];
}
}

class AuthViewModel
{
public event EventHandler AuthenticationPassed { get; }

...
}

Dependency Injection to Page ViewModels in WPF

You are missing the configuration for some dependencies. From the code you've posted, you missed to configure IJMDataIntegration and IBalanceIntegrationContext:

protected override async void OnStartup(StartupEventArgs startupEventArgs)
{
base.OnStartup(startupEventArgs);
ServiceCollection services = new ServiceCollection();
services.AddScoped<MainWindow>();
services.AddScoped<ScaleInterfacePage>();
services.AddScoped<IJMDataIntegration, JMDataIntegration>();
services.AddScoped<IBalanceIntegrationContext, BalanceIntegrationContext>();
services.AddScoped<IScale>(provider => new Scale("1234"));
services.AddScoped<ScaleIntegrationViewModel>();

ServiceProvider serviceProvider = services.BuildServiceProvider();
MainWindow mainWindow = serviceProvider.GetService<MainWindow>();
mainWindow.Show();

}

Also, as already mentioned, you have to inject the view model into the MainWindow too. This where the dependency graph starts, the root of the application:

partial class MainWindow : Window
{
public MainWindow(ScaleIntegrationViewModel viewModel)
{
this.DataContext = viewModel;
}
}

To enable the full power of Dependency Injection (and make mocking easier) you should make use of Dependency Inversion throughout the application. This means you should only depend on interfaces and therefore only have interface types in your constructors:

partial class MainWindow : Window
{
public MainWindow(IScaleIntegrationViewModel viewModel)
{
this.DataContext = viewModel;
}
}

Controls like pages should be generated via DataTemplate and not instantiated directly in XAML. All you need to do, is to inject the page view models e.g. into another view model. Bind them to a ContentPresenter and define an implicit DataTemplate which targets the type of the page view model. This template contains the actual page. See this example.

Search for View-Model-First pattern if you need more details. Basically, a view can be defined as a data template and associated with a view model type. Data templates can be defined as resources, or they can be defined inline within the control that will display the view model. The content of the control is the view model instance, and the data template is used to visually represent it. This technique is an example of a situation in which the view model is instantiated first, followed by the creation of the view.

This is the preferred way, especially in conjunction with Dependency Injection.

WPF, MVVM, Navigation, Keeping Dependency Injection In-Tact

This is my articulation of the motivations behind my implementation of the solution provided by another author. I do not provide code as great code examples are provided by the linked article. These are, of course, my opinions but also represent an amalgamation of my research into the topic

No-Container Needed Solution

Rachel Lim wrote a great article, Navigation with MVVM, which describes how to take full advantage of WPF's DataTemplates to solve the challenges presented by MVVM navigation. Lim's approach provides the "best" solution as it greatly reduces the need for any Framework dependencies; however, there really is no "great" way to solve the problem.

The greatest objection to Rachel's approach in general is that the View Model then becomes responsible for - or, "defines" - it's own relationship to the View. For Lim's solution, this is a minor objection for two reasons (and does nothing to further justify other bad architectural decisions, described later):

1.) The DataTemplate relationship is not enforced by anything other than an XAML file, i.e. the View Models are, themselves, never directly aware of their Views nor vice versa, so, even our View's constructor is further simplified, take for example the Home class constructor - now without need for a reference to a View Model:

public Home()
{
InitializeComponent();
}

2.) As the relationship is expressed nowhere else, the association between a particular View and View Model is easy to change.

An application should be able to function (model the domain) sufficiently without a prescribed View. This ideal stems from the effort to best decouple the application's supporting architecture and to foster further application of SOLID programming principles, most specifically dependency injection.

The XAML file - not a third-party dependency container - becomes the pivotal point for resolution of the relationship between the View and the View Model (which, consequently, directly contradicts the OP).

Dependency Injection

An application should be designed to be entirely agnostic of its container and - even better - any implementation-specific information regarding service dependencies. What this allows us to do is to "assign" (inject) services that oblige by a certain contract (interface) to various classes that make up the meat of our applications' functionality.

This leaves us with two criteria for "good design":

  1. Application classes should be able to support a variety of services that perform the same task, as long as they oblige by a described contract.
  2. Application classes should never be aware of - or reference - their container, even if the container is Unity, PRISM, or Galasoft.

The second point is of utmost importance and is a "rule" that is broken, commonly. That "rule breaking" being the inspiration behind the original post.

The response to the navigation problem in many applications is to inject a wrapped dependency injection container which is then used to make calls out of the implementing class to resolve dependencies. The class now knows about the container and, worse, has even greater, more concrete knowledge of what specifics it needs in order to perform its operations (and some might argue more difficult to maintain).

Any knowledge of a dependency resolution container on the part of the View, View Model, or Model is an anti-pattern (you can read more about the justification for that statement elsewhere).

A well written application that relies on dependency injection could function without a dependency injection Framework, i.e. you could resolve the dependencies, manually, from handwritten bootstrapper (though that would require a great deal of careful work).

Lim's solution enables us to "not need" a reference to a container from within the implementation.

What we should be left with are constructors that look like:

// View:
public Home() { //... }

// View Model
public HomeViewModel (SomeModelClass someModel, MaybeAService someService)

If one goal is modularization and reusability, then the above achieves that, well. We could continue to abstract this out further by ensuring those passed-in dependencies are contract fulfillments via interfaces.

Dependency injection with Unity on wpf MVVM application

Let me show an example with explanations just for your MainWindows, as for the rest views and viewmodels steps to do are the same.

At first, you should create a contract between View and ViewModel. It shoud be some interface and let it call IViewMainWindowViewModel (keep in mind that name has to be different for other view and viewModels, for example IViewBookingViewViewModel):

public interface IViewMainWindowViewModel
{
/*This interface should not have any methods or commands. It is just
contract between View and ViewModels and helps to decide to Unity
container what it should inject(appropriate viewModel to necessary
View)*/
}

then in your viewmodel we should implement this interface:

public MenuViewModel:IViewMainWindowViewModel
{}

The view should inject this interface MainWindows.xaml.cs:

public partial class MainWindows : UserControl, IContentAView
{
public MainWindows(IViewMainWindowViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}

Delete StartupUri and override a method OnStartup in App.xaml.cs:

protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IUnityContainer container = new UnityContainer();
container.RegisterType<IViewMainWindowViewModel, MainWindow>();
container.RegisterType<IViewMainWindowViewModel, MainWindowViewModel >();
container.Resolve<MainWindow>().Show();
//Do the same actions for all views and their viewmodels
}


Related Topics



Leave a reply



Submit