This post is born out of my frustration with current MVVM examples available on the Internet. I’ll demonstrate how to show a dialog and get the result back in an MVVM way and also show you how to unit test it.
The problems that annoy me
The current examples are:
- too simple since they cover only data binding
- using MVVM frameworks and are difficult to understand without knowing the basics
- saying that the MVVM pattern leads to increased testability, yet they fail to offer examples (either none at all or crappy examples)
- not using commands and unit testing them
- not showing the tricky parts of MVVM: like how do I show a dialog in an MVVM way and also make it testable with a clear separation between the view and the view model?
In this post I’ll implement the simplest app I can think of that shows a dialog and gets the result back to the view model.
The sample repository shows you how to unit test the sh*t out of this app using best practices and excellent naming for variables, functions and unit tests. This is the simplest example that you can learn best practices from!
Click Counting App with a Dialog
This is what we aim for:
Normally, without applying MVVM you would just call
MessageBox.Show from the view’s code behind:
MainWindow.xaml.cs. Things get a bit more complicated when trying to apply MVVM: instead of a click handler that you’d put in the code behind you’d use a
Command, but then if you call
MessageBox.Show there then you’d break the view - view model separation of concerns. More exactly: the view should know about the view model, but the view model should not know about the view.
The reason for this is that you should be able to use the view model with any view and if you show a
MessageBox from the view model’s command (which is still part of the view model) you break this separation. The view model will know about the
MessageBox which is a view.
Even worse: now how do you write unit tests? If you try to execute the command from the tests it will pop up a
MessageBox asking you to confirm the action and block the tests. Also, how do you test what happens when the user confirms and what happens when the user doesn’t confirm? So, then how do we solve this problem?
There are 2 solutions for this:
- using an event in the view model that the view subscribes to
- using dialog services
I consider using events to be the cleanest and nicest way of implementing MVVM. The key trick to really understanding MVVM and applying it even in the toughest situations is to use events.
MVVM component dependencies review
I have a problem with most MVVM diagrams since they get the dependencies wrong. When you’re starting out it’s very important to understand the right way to implement it, but it’s very difficult to find proper examples. The diagram below is the correct one and I’ll explain why:
The most important thing to notice is the use of continuous and interrupted lines: they indicate what coupling is used between the MVVM components:
- the interrupted lines (“Send Notifications”) mean loose coupling which in this case means using events.
- the continuous lines mean tight coupling - there is a strong one way relationship. For example the view model has a direct reference to the model and the view has a direct reference to the view model.
Understanding these relationships between components is critical when implementing MVVM.
The “Send Notifications” arrows are implemented in code using events:
- the arrow starting point indicates where the event is declared (i.e. who owns the event)
- the arrow ending point (arrow head) indicates who subscribes to the event
In MVVM you have these situations where you use events:
- the View Model owns events that it raises and that the View subscribes to
- the Model owns events that it raises and that the View Model subscribes to
- between view models by using a Mediator (see Mediator pattern)
One specific and extremely common case where the View Model uses events that the View subscribes to is when implementing data binding through
INotifyPropertyChanged: you declare the
PropertyChanged event in the View Model and you raise it when someone sets the properties. But how does the View know that something has changed? Well, the View subscribes to the
PropertyChanged event from the View Model and when that event is raised then the View updates its components. You may wonder: “where does the View subscribe to the
PropertyChanged event?” - the answer is that this code is hidden in WPF, but that doesn’t mean it doesn’t exist! It does!
We are going to use the same technique as used for the
PropertyChanged event, but for showing a dialog. Let’s see how to do it in the next section…
How to use events to ask for confirmation?
To solve our current problem we simply declare an event called
AskConfirmation in our view model:
and then use the event in our command:
The way the communication between the view model and the view is done is by using the
AskConfirmationEventArgs to send the
Message to be displayed to the view and then the view stores the result from the user in
Confirmed. Next, if the user
Confirmed we increase the
if (args.Confirmed) Count++;. Pretty neat, huh?
View-ViewModel separation of concerns
Now the view model raises the
AskConfirmation event when it wants to ask confirmation from the user, but we still need the view to actually listen to the event. This is how we do it:
and of course here’s the code that actually shows the
MessageBox, gets the confirmation from the user and stores it in
Please pay attention to the view model and view separation: we show the
MessageBox from the view’s code behind, not from the view model. The view model does not know about any view specific things:
Unit testing - simulating user’s choice
Now’s the interesting part: we have to test what happens when the user confirms and when the user doesn’t confirm the count increase. This is quite simple the way the code is structured now: just subscribe to the
AskConfirmation event and set
false depending what you want to test.
Please also note that we want to follow the Arrange, Act, Assert pattern for organizing the test code since it’s easier to follow what happens:
- Arrange: we prepare the view model by setting the starting count and the user’s confirmation
- Act: we trigger the action/command that we want to test
- Assert: we verify that our expectation about click
I recommend you check the source code for the tests in the repository linked below. They also show to to test the
PropertyChanged related things.
I aimed to make this MVVM example as simple and as complete as possible (I know it’s a bold claim). It has extensive tests - tests that I would have liked to see when I was learning MVVM.
The full source code is available on GitHub. You can clone the repo and try it locally.