In WPF/MVVM, you sometimes need the ViewModel to trigger things at the View layer, such as displaying a MessageBox, opening a new window, closing the current window, or starting an animation based on some conditional state in the ViewModel.
MVVM purists seem to agree that the ViewModel should never know about the View. So to solve the above scenarios, other than a few hacks to address some of the simple scenarios, a common paradigm is to use message passing. Imagine using a message passing system just to display a messagebox - MVVM can make trivial stuff pretty complicated.
Let's consider a different approach. First, I make my View implement an interface:
public class MyWindow : IClosableView
Then, in the ViewModel's constructor, I make it take an instance of that interface as a parameter:
public class MyWindowViewModel(IClosableView view)
So when I set the DataContext in the View's constructor, I just pass in the View itself:
public MyWindow()
{
InitializeComponents();
this.DataContext = new MyWindowViewModel(this);
}
This makes it pretty simple for the ViewModel to do those things we mentioned above via the View:
public void Close()
{
this.view.Close();
}
Now before all you MVVM purists start throwing random breakable things at me, let's take a look at what we're doing here. The ViewModel is taking an interface to a view, not a view itself. This means that although the ViewModel does know about a view,
- It only knows as much as it really needs in order to trigger the necessary view-side operations (which it needs to do anyway if using a message-passing approach).
- It does not rely on any specific view; it only requires that the view that uses it will provide certain facilities, in this case the ability to close that view.
- The ViewModel is still fully testable, since the view can be mocked by implementing the IClosableView interface in another class and passing that in unit tests.
Given this reasoning, I'm curious: why is message-passing better than this simple and effective approach?
Edit: just to make things clear, as I stated at the beginning of this question, I'm talking about cases where a View action depends on the state of the ViewModel. It's very easy to have a button close a window by just wiring it up in codebehind. But what about when that depends on some state in the ViewModel?