MVVM: Invoke Model Logic in One UserControl from Another Without Violating Principles

cmvvmwpf

Here is WPF application consisting from 3 UserControls:

enter image description here

UserControl3 is a part of UserControl2 content. I keep MVVM during developing and using Prism.

I need to invoke custom class method (which is model in terms of MVVM) in UserControl3 from view-model of UserControl1. The restriction that service in UserControl3 can't be singleton. I suppose to do it one of the following way:

  1. Using event aggregator from Prism. UserControl1 view-model is publisher and UserControl3 model is subscriber. For this I'll need to create unique Id in Window and pass it to UserControl1 and UserControl3.

  2. Creating service instance in Window and pass it to UserControl1 and UserControl3. Then UserControl1 just invoke method on this instance.

  3. Window pass UserControl2 instance to UserControl1. View-model in UserControl1 will just invoke method of UserControl2, which will invoke method of UserControl3 and so on.

It seems like 2 and 3 approaches violates MVVM. What will you prefer?

Best Answer

This question, while focusing mostly on WPF applications applies equally to any GUI application.

When you have two different pieces of the user interface that must interact, you are really left with two possible solutions:

  1. Events, which you outline as solution #1. The advantage here is the publisher and subscriber are decoupled, and do not have to know the structure of the user interface in order to do their job. The disadvantage here is that subscriber and publisher are decoupled, making UI problems harder to debug.

  2. Composition (in Object-Oriented terms). You need to encapsulate the logic somehow. If this logic is purely UI logic, then one User Control could have a direct reference to another, if and only if the structure of the UI will now and forever more facilitate this object relationship.

    If the two User Controls cannot know the structure of their containing window, go with option #1 above.

    If the logic is not UI logic, then a service class injected into each User Control upon object creation will work (also known as Dependency Injection).

My preference is to use events or messages between two UI components. An event can be generated by the user, or by the application.