MVVM – Message Dialogs

A Message to You Rudy

In this post I’m going to continue my look at some ‘hard’ MVVM issues, this time how to display a message dialog from the ViewModel layer, but in a MVVM pure manner. Remember that the key principle of MVVM is separation of the user interface from business logic, so we shouldn’t just write something like this in the ViewModel …

    MessageBox.Show("Here's a message ...");

In the last post I introduced the concept of using a service as a means of indirectly accessing the U.I. layer from the ViewModel. We can do the same for message dialogs – but this time it is a little more complicated. The Dialog Service within the Peregrine.WPF.View library is split into three distinct segments.

A. perDialogServiceRegistration.Register

This part provides the connection between the ViewModel and the user interface layer. It is effectively the View saying “whatever is my DataContext (i.e. its ViewModel) may want to display a message dialog – when it does, it can use my U.I. context to do so”. This technique was introduced by a great WPF user interface toolkit – Mahapps Metro, in particular their DialogParticipation class. I was inspired by that concept and have used it as the basis for my own code.

One important point to note is that the dialog registration class persists for the lifetime of the application, but we don’t want to be blocking any garbage collection operations once a View or ViewModel object is no longer otherwise required. To counter this, the ViewModel → View links are held in a very indirect manner – the dictionary key is the hash code of the ViewModel rather than the object itself, and the View is attached using a WeakReference.

B. perDialogServiceRegistration.DialogContent

This optional element allows you to display a dialog containing any UIElement rather than just a simple text string. It can be set as a single item, or (as in the case of the demo application for this post) as an array of items which are identified using their Tag property. The data context of the selected U.I. component will be set to the calling ViewModel when it is displayed (in ShowContentDialogAsync()), allowing the dialog contents to bound to any of its properties. Again, the registration dictionary just contains weak references to the contained items.

C. perDialogService

This part follows the service pattern I’ve introduced previously – the ViewModel has an interface parameter which is injected into the constructor by the IoC container, the implementation of which is defined in the View layer. The interface contains three methods for displaying dialogs to the user…

public interface IperDialogService
        Task ShowMessageAsync(object viewModel, string body, perDialogIcon dialogIcon = perDialogIcon.Information, string title = "");
        Task<perDialogButton> ShowDialogAsync(object viewModel, perDialogButton buttons, string body, perDialogIcon dialogIcon = perDialogIcon.Information, string title = "" );
        Task<perDialogButton> ShowContentDialogAsync(object viewModel, perDialogButton buttons, perDialogIcon dialogIcon = perDialogIcon.Information, string title = "", string tag = "");

I’ve defined my own icon (a direct clone of System.Windows.MessageBoxImage) and button enumeration types too, so that the ViewModel layer has no dependencies on any View classes …

    public enum perDialogIcon

    public enum perDialogButton
        None = 0,
        Ok = 1,
        Yes = 1 << 1,
        No = 1 << 2,
        Cancel = 1 << 3,
        Retry = 1 << 4,
        Ignore = 1 << 5,
        Abort = 1 << 6,
        Save = 1 << 7,
        YesNo = Yes | No,
        YesNoCancel = Yes | No | Cancel,
        OkCancel = Ok | Cancel,
        RetryCancel = Retry | Cancel,
        AbortRetryIgnore = Abort | Retry | Ignore,
        SaveCancel = Save | Cancel

perDialogButton is defined with the Flags attribute so that we can build any combination of buttons we need in our application.

The implementation of the interface creates a perDialog (a chromeless window) and populates the title, icon, content and buttons properties as required. The display context for this window is requested from perDialogServiceRegistration.GetAssociatedControl(), using the passed ViewModel instance as a parameter. To aid with our drive towards clean-code, the dialog is displayed in an async manner, with the caller (the ViewModel) waiting for the result.

To allow ViewModel classes using the dialog service to be unit tested (without any kind of U.I. interaction), there is also a series of mock implementations that only return the specified result value. To use these, set the implementation of the interface in the IoC container to the required mock for each specific unit test.

In my next post, I’ll continue with this theme by looking at another ‘hard’ MVVM problem – data validation.

Don’t forget that all of the code samples for this blog are available on Github, along with my own personal C# / WPF library.

Leave a Reply

Your email address will not be published. Required fields are marked *