1
votes

I'm trying to use both MVVM and Dependency Injection pattern in my WPF MDI application. I'm using VM first approach.

Basically, my app starts with the App.xaml.cs class which is supposed to be, if I understood the thing well, my composition root (where all dependencies are resolved). Here's a sample :

    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {          
            base.OnStartup(e);
            ...
            var login = new LoginView();
            login.DataContext = new LoginViewModel(Dependency1, Dependency2);
      
            loginView.ShowDialog();

            if (loginView.DialogResult.GetValueOrDefault())
            {
                 var app = new     MainWindow();
                 var mainVM = new MainViewModel(Dependency3, Dependency4);
                 app.DataContext = mainVM;
                 app.Show();
            }
        }
    }

No problem so far, I can resolve dependencies for both LoginViewModel and MainViewModel whether I use a DI container or Dependency Injection by hand. Now let's dig into MainViewModel.

I was inspired by Rachel Lim's approach and used a SelectedViewModel property to get/set the currently used ViewModel which is bound to its View using DataTemplates. I'll let you look at the link for more details on the process since it is quite unrelated to my issue here. The important thing is that my MainViewModel is in charge of switching ViewModels when needed. But my children ViewModels have dependencies. Here's a simplified sample :

     class MainViewModel
     {

            private ViewModel1 vm1;
            private ViewModel2 vm2;                

            public MainViewModel(Dependency1, Dependency2)
            {
                 ...
            }

            ...

            // Method used by an ICommand to display the ViewModel1's associated View
            private void DisplayView1() 
            {
                 vm1 = new ViewModel1(Dependency3, Dependency4, Dependency5);

            // Method used by an ICommand to display the ViewModel2's associated View
            private void DisplayView2() 
            {
                 vm2 = new ViewModel2(Dependency3, Dependency6);
                 SelectedViewModel = vm2;
            }
            ...
     }

As you can see, some dependencies are shared between several children ViewModels and some are not. My problem is, I have trouble injecting those from the composition root. So far, I have found only two solutions :

  • Having two composition root (kinda) : resolving LoginViewModel and MainViewModel in App.xaml.cs and children ViewModels in MainViewModel. This implicates, when using an IOC container, referencing the container in both classes.
  • Passing children ViewModels as MainViewModel's constructor parameter and treat them like any other dependencies. My problem with this approach is that, if I have, let's say, ten ViewModels, the MainViewModel's constructor will become huge.

I read that one could pass a factory to the MainViewModel and delegate the responsibility to create the children ViewModels to it, but I didn't see any sample using children ViewModels with constructor parameters.

I don't understand how I could use this method without passing all children's dependencies to the MainViewModel's constructor and hence, without making it huge again.

Maybe there's something I don't see, but it seems like a deadend to me.

Please help me getting this right and show me the right direction.

Thanks.

1
You're trying to do all the DI stuff yourself, you need to bring in a DI container such as unity, mef, Autofac or something else. Unity is a standard one supported by the .NET framework, here's a link to a start to finish tutorial: accusoft.com/blog/… If you use a DI container you set it up with all the dependencies in this and simply resolve the object needed, in your case ViewModel1 & ViewModel2 in the MainViewModel's constructor.Coops
Actually, I do use a DI container (ninject). I leave it out of the sample on purpose since I read that a good dependency graph could be resolved regardless of using a container. So basically, what're saying is that my child ViewModels should be treated like MainViewModel's dependencies and hence injected in its constructor ?olhybrius
Excellent stuff, you need to inject all your objects and get them resolving from the container then. I've not used ninject but I'm guessing that it supports Constructor Injection. If you change the Constructor of the MainViewModel to take ViewModel1 & ViewModel2 as paramters then your DI container will resolve them and all their depedencies for you. It's the DI containers job to worry about dependencies, you just need the instance of the ViewModel injected where you want it.Coops
Thanks for your answers. But isn't this a problem if my MainViewModel has like 10 childs ? Isn't this too much dependencies/constructor parameters for a single class ?olhybrius
If your MainViewModel takes ViewModel1-10 then so be it...have them resolved by the container. The whole idea of IOC is that you don't do the actual construction of objects. To reduce things you could create a factory class that creates the ViewModel1-10 and then all you need to do is inject the factory. Then in your code factory.CreateViewModelX and it'll return one for you.Coops

1 Answers

0
votes

I just realized I got this whole factory thing wrong up until now.

Actually, the DI container that I use, Ninject, has an extension which address the exact same issue I am having. This extension needs an interface containing the methods to create the required dependencies (the ViewModels in my case) and create the concrete factory behind the scenes. I was just misusing it, thinking I needed to pass all dependencies to the interface somehow while it can resolve the ViewModels itself since these are already registered in the container.

So now, all I have is a single composition root (my App.xaml.cs) where the container resolve the LoginViewModel and the MainViewModel. The latter has the factory interface as a dependency which is used to resolve all children ViewModels and is also resolved by the container. No extra reference to the container needed !

Thank you so much Coops for your help ! You definitly got me on the right track here !