2

I'm new to MVVM but experienced in OOP. I have an easy solution to my problem, but I'm not sure if it violates MVVM or OOP or if it is a good practice.

I have a set of ViewModel classes in my WPF project. Below is my hierarchy, with an indent representing a public property.

ApplicationViewModel 
    SidebarViewModel SidebarPanel
         TankViewModel Tank
              TankItemViewModel CurrentTankListItem
    PopupViewModel CurrentPopupViewModel
         bool DisplayPopup
   public T CurrentSidebarListItem
    {
        get => _currentSidebarListItem;
        set
        {
            _currentSidebarListItem = value;
            OnPropertyChanged(nameof(_currentSidebarListItem));

            this.DisplayPopup = this.CurrentSidebarListItem != null; // Problematic line
        }
    }

The setter of the property CurrentTankListItem needs to modify DisplayPopup of the PopupViewModel class, and the only way I can think of to do this is to pass the value of CurrentPopupViewModel from my ApplicationViewModel down through the SidebarViewModel and TankViewModel to the CurrentTankListItem property as a constructor parameter. Does passing ApplicationViewModel as a parameter in this way violate MVVM or OOP, or it is not bad practice to do this? Is there any other way I can obtain the value?

2
  • 1
    Why can't PopupViewModel subscribe to the OnPropertyChanged event and set itself? Or you could create a lambda to do it and set it up when you instantiate all the instances. Both techniques allow you to avoid the type-to-type dependency.
    – John Wu
    Aug 29 '20 at 7:18
  • @JohnWu PopupViewModel DOES subscribe to OnPropertyChanged but for every change of CurrentSidebarListItem, I also need to change DisplayPopup.
    – Marvin
    Aug 29 '20 at 12:29
3

This approach of injecting the reference to a linked object at construction is indeed one way of doing it. This is valid OOP, and it’s still in the MVVM philosophy.

However, this approach has some inconveniences, since it creates a dependency between two components that should be independent:

  • if tomorrow you want to change your application viewmodel and get rid of the popup, or if you wanted to change it, you’d also need to change your tank item so that it still works. Since the tank item has now more than one reason to change, it is no longer compliant to the Single Responsibility Principle.
  • moreover, the tank item needs to now about the details of the application view model, which is against the principle of least knowledge
  • finally, this dependency to application specific objects prevent you from reusing the tank item as component in other applications or in a different view model of the same application.

A cleaner way to do it, could be to use the observer pattern:

  • during its initialization, the app view model registers the popup vm as observer of the tank item. This is ok, because observers are based on very generic interfaces and the app vm needs to know its components.
  • when ever the tank item changes, it notifies its observers. It doesn’t need to know their internals.
  • when the popup vm receives a notification, it knows which object sent the notification and query this object to update itself.

The weakness is still that in the popup needs to know a little more about the tank to query it.

So an even better approach would be to use the mediator pattern: instead of direct communication between the VM components, every communication is channeled via the application vm. It’s a little more work in the app vm, but it is the app vm’s role to be the glue between its component.

The benefit is that each component vm remains independent from each other, facilitating maintenance, but also reuse in several places.

8
  • I definitely would like to use the mediator pattern, and I was thinking about this before asking the question as well, but I'm not sure how to do it, considering this is the setter, which I don't think I can or should be able to access outside of the class of the property itself?
    – Marvin
    Aug 29 '20 at 12:34
  • @Marvin If the setter bothers you, the first question is why you want to make it public. If it is part of the public interface by design, then there is no more issue than if you’d invoke a method instead. The second question is, how to to design the mediator/participant interface (GoF suggest a participant specific interface as a variant). And then, should the mediator invoke the setter, or should the mediator inform that there was a change and let components query for some properties in other participants ? There are a lot of options here to explore. Therefore the simpler is sometimes better
    – Christophe
    Aug 29 '20 at 13:26
  • But it looks like with the mediator pattern I’m still passing the Mediator as a constructor parameter to access it. infoworld.com/article/3204528/…
    – Marvin
    Aug 29 '20 at 13:47
  • @Marvin yes, but the idea is different: it’s not that you pass the appvm and the item uses it to find itself the popupvm and update it. You pass the appvm as a clean mediator interface end every component discusses only with the mediator. No component knows about the others. So components remain independent of each other, and only the appvm depends on them (or more precisely on an interface with them).
    – Christophe
    Aug 29 '20 at 14:13
  • 1
    Be aware that there is a form of the observer pattern that doesn’t require a query. Instead the subject creates a DTO and sends it to the observers. Aug 29 '20 at 14:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.