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:

Click Counting App with Dialog

Thinking thoroughly

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:

  1. using an event in the view model that the view subscribes to
  2. 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:

MVVM diagram

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)

The PropertyChanged event

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:

public event EventHandler<AskConfirmationEventArgs> AskConfirmation;
protected virtual void OnAskConfirmation(AskConfirmationEventArgs args)
{
    AskConfirmation?.Invoke(this, args);
}

public class AskConfirmationEventArgs : EventArgs
{
    public string Message { get; set; }
    public bool Confirmed { get; set; }
}

and then use the event in our command:

private void IncreaseCount()
{
    var args = new AskConfirmationEventArgs
    {
        Message = "Do you want to increase the count?"
    };
    OnAskConfirmation(args);
    if (args.Confirmed) Count++;
}

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 Count: 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:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    ViewModel.AskConfirmation += ViewModel_AskConfirmation;
}

private void Window_Unloaded(object sender, RoutedEventArgs e)
{
    ViewModel.AskConfirmation -= ViewModel_AskConfirmation;
}

and of course here’s the code that actually shows the MessageBox, gets the confirmation from the user and stores it in Confirmed:

private void ViewModel_AskConfirmation(object sender, AskConfirmationEventArgs args)
{
    var result = MessageBox.Show(args.Message, "Confirmation", MessageBoxButton.YesNo);
    args.Confirmed = result == MessageBoxResult.Yes;
}

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: Buttons, TextBlocks, MessageBoxes.

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 AskConfirmationEventArgs.Confirmed to true or false depending what you want to test.

[TestCase(0)]
[TestCase(10)]
public void CountStaysConstantWhenUserDoesNotConfirm(int startingCount)
{
    var viewModel = new ViewModel() { Count = startingCount };
    viewModel.AskConfirmation += (s, e) => e.Confirmed = false;

    viewModel.IncreaseCountCommand.Execute("unused parameter");

    Assert.That(viewModel.Count, Is.EqualTo(startingCount));
}

[TestCase(1, 0)]
[TestCase(2, 1)]
public void CountIncreasesWhenUserConfirms(int expectedCount, int startingCount)
{
    var viewModel = new ViewModel() { Count = startingCount };
    viewModel.AskConfirmation += (s, e) => e.Confirmed = true;

    viewModel.IncreaseCountCommand.Execute("unused parameter");

    Assert.That(viewModel.Count, Is.EqualTo(expectedCount));
}

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 Count is correct

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.

MVVM Dialog Events - Unit Tests

Source Code

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.