faldeland
faldeland

Reputation: 587

Changing from View First to ViewModel First - How do I handle Constructor Dependency Injection

Our new project started out with View first pattern using ViewModelLocator to marry the view with the viewmodel.

I'd like to change to a ViewModel first pattern.

Here is my view model constructor:

public DeviceSelectionViewModel(IDataModel dataModel, IMessenger messenger)
{
    if (dataModel == null) throw new ArgumentNullException("dataModel");
    if (messenger == null) throw new ArgumentNullException("messenger");

    Selector = new PlantDataTemplateSelector();
    PlantSelector = new PlantNodesSelector();
    Plants = new List<Plant>(0);

    messenger = messenger;
    messenger.Register<PlantDataLoadedMessage>(this, m => DispatcherHelper.CheckBeginInvokeOnUI(() => OnPlantDataLoaded(m.Plants)));

    RefreshData(_dataModel);
}

Here is how I'm now selecting the appropriate ViewModel using ViewModel first.

public class MainViewModel : Module
{

    public MainViewModel()
    {
        SelectedView = new DeviceSelectionViewModel();
    }

    public ViewModelBase SelectedView { get; set; }

}

With View first, I never directly called the ViewModel via code so the Constructor Dependency Injection worked fine.

Now that I'm calling the ViewModel via a controller ViewModel, it wants the 2 parameters for the ViewModel Constructor.

Is the proper play here to hold the references in the controller view model and pass them into the constructor? I'm missing something here with how DI works in this scenario?

I'm still putting the pieces together with DI(Ninject) and MVVM, so be kind :)

Upvotes: 1

Views: 898

Answers (1)

Pavel Voronin
Pavel Voronin

Reputation: 13983

We had the same issue.

You have the following choices:

  • MainViewModel creates DeviceViewModel providing the values for required parameters
  • MainViewModel gets already resolved DeviceViewModel

The first variant increases coupling in case when DeviceViewModel is exposed only for View bindings. If MainViewModel communicates with DeviceViewModel then coupling is already implied unless MainViewModel sees DeviceViewModel through some interface what inevitably requires the second option.

Another problem of the 1st approach is that in some cases nested VM can require more values for its creation as parent VM needed.
This can be solved by extending parent VM constructor to contain these additional parameters. But when (though never in practice) building complex VM graphs root VM's constructor can become bloated.

We now have a mix of both options.

All our ViewModels inherit from ViewModelBase abstract class. It has several overloaded constructors and most of them get IViewModelFactory as an input. So we can ask the factory to create ViewModel wee need. Wee decided not to worry about the returned type and just return the type of created ViewModel incapsulating only its creation. So it is not true DI.

We also use Messanger but our ViewModels cannot send messages explicitly and can only subscribe to the messages. We did so intentionally as a way to restrict a set of messages particular VM can send (and even subscribe to).

Upvotes: 1

Related Questions