0

I'm relatively new to WPF and MVVM and i am trying to understand how to use commands correctly when they have dependencies in more than 1 view model.

A couple of examples:

  • In my current application i have a RelayCommand which causes a save action to occur in a couple of different view models (they write a couple of different files). Currently i am handling this using a the mvvmlight messenger to send a message to those view models to get them to do the save which i think is the correct way to do it as it avoids having to provide some kind of delegate or event to/on those view models.
  • I have a RelayCommand in a view model that has a CanExecute method which relies on the state of 2 other view models. I've currently handled this via the mvvmlight messenger as well by having changes in the view models the CanExecute method depends on message that their state is now valid for the operation. This seems messy but the only alternative i could think of was to use a delegate or event effectively weaving the view models together which i believe i should be avoiding.

Is there some generally accepted way to deal with this which i am missing?

DaeDaLuS_015
  • 110
  • 5

1 Answers1

1

In general your view model layer should have a 1:1 relationship with your view, there should be no good reason for a "Save" function to exist in a view model which is then called by another view model.

What it sounds like you should be doing is putting that logic into a service i.e. something like this:

public interface ISerializationService
{
    void Save(SomeData data);
}

Then you need an implementation for this service that does the actual work:

public class SerializationService : ISerializationService
{
    void Save(SomeData data)
    {
        // actual save happens here
    }
}

Your view models should then contain properties that point to instances of these services:

public class MyViewModel : ViewModelBase
{
    [Inject]
    public ISerializationService SerializationService { get; set; }

    // called when the user clicks a button or something
    private void ButtonClickCommand()
    {
        this.SerializationService.Save(this.SomeData);
    }
}

The only question remaining is "What sets the value of SerializationService?", and for that you need a dependency injection framework. There are plenty out there, MVVMLight installs one itself, but Ninject is the de-facto standard. When implemented properly the injection framework will create all view models for you and then "inject" the dependencies, i.e. your SerializationService property, of type ISerializationService, will be initialized with an instance of your SerializationService class (which in a case like this will also be configured to be a singleton).

Dependency Injection takes a bit of work to get your head around but once you start using it you'll never look back. It facilitates complete separation-of-concerns whilst alleviating the need to pass pointers to everything all up and down your architectural hierarchy.

Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • This makes sense, I've been doing the depedency injection manually but I really should look at ninject. I hadn't considered the idea of having a service for saving. It sort of made sense to me that the view model did the saving as in a way it is handling the model objects but I can see that's not exactly ideal. What about my second example here? Or is the way I've handled that acceptable? Thanks! – DaeDaLuS_015 Jan 31 '19 at 08:28
  • Or is it the case that you see saying both issues are really the same? So in the second case I should inject the references to the view models that the command relies on into the view model the command is attached too? – DaeDaLuS_015 Jan 31 '19 at 08:55
  • I have an issue with `ButtonClickCommand` my understanding of command that it is abstraction of a functionality which shouldn't know whether it was triggered by a button click or a keyboard shortcut or clicking on a menu item etc. – Piotr Golacki Oct 11 '22 at 09:33
  • @PiotrGolacki no, that's only one use for commands. Events can't be used with binding, so in MVVM you have to translate events you are interested in to ICommands in order for your view model layer to be able to receive them. This is particularly important for unit-testing view logic with complex user interactions like drag-n-drop of elements on pages etc. – Mark Feldman Oct 11 '22 at 22:27