Tackling the Problem of Modal Dialogs in MVVM

One of the first issues you’ll run into when wading into the waters of MVVM is how to display model dialogs to the user while executing code in the view-model.  Popping up a dialog, such as a MessageBox or a ChildWindow, from the view-model is an anti-pattern that violates the separation of concerns that exists between the view and the view-model.  One of the main benefits of MVVM is better application maintainability. If you separate concerns of presentation (the view) from business logic (the view-model) and data (the model), making a change in one area is less likely to impact other areas.  Another benefit is testability.  It is notoriously difficult to test the user interface by simulating things like button clicks and mouse overs.  Encapsulating functionality into a view-model means that you can test it independently of the view, which is just another consumer of the view-model.  If you were to display a dialog from the view-model in order to get input from the user, it would be impossible to run a unit test against the view-model because there’s no way for a unit test to respond to the dialog.  In addition, MVVM promotes better workflow between designers and developers.  Designers can wire up actions to elements straight from the XAML.  Some code-behind may still be necessary, but it should be restricted to view-related activities, such as starting and stopping animations or communicating with the user via messages or dialogs.

In response to these arguments I’ve heard it said, “We don’t have designers, and I don’t care about testability or maintainability.”  In this case, the answer is simple: don’t use MVVM! If all you care about is quickly building an app, then just use code-behind and forget about MVVM, even if everyone else is doing it.  Better not to use MVVM than sabotage it by taking shortcuts.

OK, so let’s say I’ve convinced you not to display dialogs from the view-model.  How do you go about obtaining information from the user from a method that resides in the view-model?  One approach is to define a dialog service interface.  The problem I have with this method is that it feels too tightly coupled to me.  This is typical of interfaces in general. The view-model needs to hold onto a reference to the object implementing the interface, and classes must implement all members of the interface even the ones they may not care about.  Another approach is to use a message bus to handle all communication between view and view-model.  This feels to loosely couples to me.  I can appreciate the need for a mediator to pass messages between various view-models (and I plan to add this feature to my toolkit), but the view already holds a reference to the view so decoupling them seems pointless to me.

In my Simple MVVM Toolkit I advocate what I like to call “goldie locks” coupling: not too tight, not too loose.  The answer is to use an event-based communication model.  Events have finer granularity than interfaces because subscribers can pick and choose which events to subscribe to, and the publisher is relieved from the burden of maintaining direct references to subscribers.  In addition, the view-model is not aware that it’s displaying a dialog – it just needs some data from somewhere and doesn’t care who provides it or how it’s obtained.  Using events also alleviates the need for an intermediary such as a message bus.  Because the view already has a reference to a view-model, it can easily subscribe to events and specify a callback method.  In the callback the view can show information to the user and get a response in any way it wants.

Let’s say you want to notify the user if there is an exception in your view-model. You may want to display a message box or set the content of a label control on the view.  Doing so directly from the view-model would violate the separation of concern and tightly couple the view to the view-model, at the same time making the view-model untestable.  To inform the view that you want to notify it of an error all you have to do is expose an event on your view-model and let the view subscribe to it.  It’s customary to use the built-in EventHander<T> delegate type, where T is a class that extends EventArgs.  My toolkit has a NotificationEventArgs type for just this purpose. It has a single Message property of type string.

public class NotificationEventArgs : EventArgs
{
    public string Message { get; protected set; }
}

There is also a generic version of NotificationEventArgs that allows you to send along an outgoing payload, which the subscriber can consume.

public class NotificationEventArgs<TOutgoing> : NotificationEventArgs
{
    public TOutgoing Data { get; protected set; }
}

Your view-model simply exposes an event as an EventHandler<NotificationEventArgs>, then fires the event when it wants to communicate with external parties, such as a view or unit test.

public class ProductListViewModel
{
  public event EventHandler<NotificationEventArgs
    <Exception>> ErrorNotice
    
  private void ProductsLoaded(List<Product> entities,
    Exception error)
  {
    if (error != null && ErrorNotice != null)
    {
      ErrorNotice(this, new NotificationEventArgs<Exception>
          ("Unable to retrieve products", error));
    else
      Products = entities;
    }
  }
}

The view can subscribe to the ErrorNotice event with a method that, for example, shows a message box or dialog of some sort.

public partial class ProductListView : UserControl
{
  ProductListViewModel model;

  public ProductListView()
  {
    InitializeComponent();

    // Get model from data context
    model = (ProductListViewModel)DataContext;

    // Subscribe to notifications from the model
    model.ErrorNotice += OnErrorNotice;
  }

  void OnErrorNotice(object sender, 
    NotificationEventArgs<Exception> e)
  {
    // Show user message box
    MessageBox.Show(e.Message, "Error", MessageBoxButton.OK);

    // Trace error information
    Debug.WriteLine(e.Data.ToString());
  }
}

This shows how the view-model can communication information to the view in a loosely coupled manner.  But what if you need to obtain information from the user that the view-model needs in order to proceed? The way to accomplish this is with a callback parameter in NotificationEventArgs where you can process the user response, which could be anything from a boolean (for example in the case of a delete confirmation) to the result of a child window.  This plays nice with the asynchronous nature of dialogs in Silverlight, which are not truly model as they are in WPF or Windows Forms (this is because you can’t rely on the Windows message pump in a cross-platform framework such as Silverlight).

Here is an example of a event fired by the view-model to indicate that a particular product is available for order. It exposes an event with a NotificationEventArgs that takes two type arguments, both boolean, for outgoing and incoming data.  The view-model needs to send a boolean value to the view indicating whether or not the product is available, and the view needs to reply with a boolean value indicating whether the user wishes to order the product.

public class ProductListViewModel 
  : ViewModelBase<ProductListViewModel>
{
  public event EventHandler<NotificationEventArgs<bool, bool>> 
    ProductAvailableNotice;

  private void ProductAvailable(bool available)
  {
    // Notify view that product is available
    if (ProductAvailableNotice != null)
    {
      ProductAvailableNotice(this, 
        new NotificationEventArgs<bool, bool>
        (null, available, PlaceOrder));
    }
  }

  private void PlaceOrder(bool confirm)
  {
    if (confirm) serviceAgent.OrderProduct();
  }
}

This version of NotificationEventArgs also has a Completed event, which is an Action<TIncoming> and can be set in the constructor.  That’s the secret sauce of allowing the view to communicate a response back to the view-model, thus enabling two-way communication between the two.  In this example, we are calling OrderProduct on the service agent if we received a confirmation from the user.

public class NotificationEventArgs<TOutgoing, TIncoming> 
    : NotificationEventArgs<TOutgoing>
{
    // Completion callback
    public Action<TIncoming> Completed { get; protected set; }
}

In this case we wish to prompt the user by displaying a ChildWindow with a message that shows the product name and asks the user to click either the Yes or No button.  After receiving a response, we will execute the callback passed to the view in the NotificationEventArgs parameter.

void OnProductAvailableNotice(object sender, 
    NotificationEventArgs<bool, bool> e)
{
    // Product is available
    if (e.Data)
    {
        // Show OrderProductView
        OrderProductView orderProductView = new OrderProductView();
        var orderProductVm = (OrderProductViewModel)
            orderProductView.DataContext;
        orderProductVm.Product = model.SelectedProduct;
        orderProductView.Closed += (s, ea) =>
        {
            if (orderProductView.DialogResult == true)
            {
                // Notify view model to order product
                e.Completed(true);
            }
        };
        orderProductView.Show();
    }
    else
    {
        MessageBox.Show("Product is unavailable.",
            "Product Availability", MessageBoxButton.OK);
    }
}

Notice how we invoke the event args Completed event in the Closed event of the dialog if the user clicked the Yes button, which sets the DialogResult of the ChildWindow to true.  We now have full two-way loosely-coupled communication based on events.  Using events keeps the view-model UI-agnostic and fully testable. And because views and view-models are generally paired, there’s no need in this case for an event mediator or message bus.

For the full source code to this example, just download the latest version of my Simple MVVM Toolkit and open up the solution in the SimpleMvvm-Main folder in the Samples directory.

About Tony Sneed

Sr. Software Solutions Architect, Hilti Global Application Software
This entry was posted in Technical and tagged , . Bookmark the permalink.

25 Responses to Tackling the Problem of Modal Dialogs in MVVM

  1. Pingback: Tweets that mention Tackling the Problem of Modal Dialogs in MVVM | Tony Sneed's Blog -- Topsy.com

  2. Maxxx says:

    Hi.
    I wonder if this is the “right” approach for MVVM. If your dialog requires some data, or captures some data then is it not a View that requires a ViewModel that should be shown in the same way you are showing any other view (other than in this case it is modal?)

    The ViewModel is meant to be a model of the view – and as such playing around with callbacks to the view seems to me to be outside of its scope.

    Say the user clicks on a SaveFile button in the V which invokes a command on the VM; The file already exists and the VM needs to check it’s OK to overwrite…

    The dialog that says ‘Overwrite?? Yes/No’ is a view that requires a ViewModel and needs to be instantiated in whatever way you normally use. If it needs to be a dialog, then the question is, should this be determined by the VM (i.e. out presentation layer logic is that this needs to be a dialog) or the View (i.e. our presentation logic just says the message needs to be displayed – possibly modally, possibly not).

    If the latter, then the view needs to be instantiated with the VM as usual and left to its own devices (maybe the view will be displayed on the current window, for example, preventing access to the underlying controls – effectively being modal)

    If the former, then (assuming you’re not only dealing with single-window applications), then you will have some way of showing a View which is a Window – and surely it is here that you need to have the ability of telling the system that the view needs to be shown as a modal window.

    Maybe I am missing something, and the fact that there are so many postings proposing solutions similar to yours that I almost feel I must be, but I rather feel that the issue is being over-complicated.

    • Tony Sneed says:

      @Maxxx: First I’d like to thank you for your thoughtful comment / question on my blog post. You bring up a very important aspect of the dialog question, which has to do with who determines the appearance of the dialog (for example, whether it’s model or not, the number and kinds of buttons, the color of the message text, etc). Should it be the ViewModel whose logic triggers the dialog? Or should it be the View which has subscribed to an event fired by the ViewModel? This is much more a discussion of the separation between ViewModel and View, and the kind of information exchanged, than it is about the actual communication mechanism (events vs message bus, vs dialog service). And this relates not only to dialogs but also things like navigation, where business logic in the ViewModel should drive the application flow.

      It’s important at this point to remember that the View is rather passive in its role as Presenter and that the ViewModel should abstract away from the View the data, behavior and presentation logic. The only thing the View should concern itself with is painting to the screen, which is expressed mostly in XAML and possibly some code-behind. There should be no business or presentation logic per se in the code-behind of the View.

      So how does this square with my assertion that the View, and not the ViewModel, should be the entity that displays dialogs? Where do you put the logic that determines whether the prompt should be a modal dialog (i.e. ChildWindow) or a status message shown in a TextBlock? I believe the logic should reside in the ViewModel, but that it be abstract and not graphical in nature. This is the same rule that governs the separation of concerns you want to maintain between Views and ViewModels in general. For example, you would not want to have a property on the ViewModel that is of type Brush. That would inhibit testability. A better approach would be to provide a property allowing the View to decide the appropriate color based on whatever triggers the change, such as an IsManager property. The same goes for the question of dialogs. The ViewModel would expose data to the View (via a message) that the View would use to determine how to paint the pixels.

      How the message is communicated from the ViewModel to the View is a matter of personal preference. That could be a dialog service, a message bus, or an event. If the View needs to convey the result back to the ViewModel (for example whether the user clicked Yes, No or Cancel), that can be performed as a callback, which is generally how it’s done no matter which communication mechanism you use. I prefer events to a dialog service that implements an interface. That’s because events offer finer granularity than interfaces, are widely used and understood, and provide looser coupling than do interfaces. On the other hand, some developers still have a hard time getting their head around events and would prefer an interface, or they want even looser coupling provided by a message bus. All of that is OK in my book. In fact, I think each of these approaches has its place, and my toolkit includes a full-fledged message bus for inter-ViewModel communication. The important thing is that the ViewModel not show the dialog directly. If you take that route, you will make the ViewModel untestable and, in my opinion, will be violating the separation of concerns that should exist between View, Model, and ViewModel.

      To summarize, when you fire a notification event from a ViewModel, it should carry a payload that indicates to the View how it should be handled according to logic contained in the ViewModel. For example, you can have a MessageType enum with values like Informational, Critical, RequiresResponse. The View can then act on this abstraction to either show a status message or to pop up a dialog. This rule holds true no matter how you want to communicate the message to the View.

      If you’re a bit offput by events, consider that bindings are all based the PropertyChanged event. So events are already being used as a communication mechanism in Silverlight and WPF. Using them for generic notifications seems like a natural extension to me, because we’re talking about sending information from the ViewModel to the View, which already has a reference to the ViewModel. When it comes to sending information among various ViewModels, that’s where a message bus, also called a mediator or event aggregator, comes in — which will be the topic of my next post. 🙂

    • Vic Klien says:

      Hi Tony,

      I like the idea of the VM exposing events that the view can subscribe to, but I want to have a good story for the MVVM police.

      When you say “the view already has a reference to the VM”, that is not 100% true. “Normal” MVVM uses the binding system via the DataContext of the view and the coupling is a bit looser via “magic strings” for the bound properties. Your way casts the DataContext to a “hard reference” to the VM. Obviously that’s the only way to subscribe to the VM’s events. I see in your sample code you go even further and have the view’s code-behind call methods on the VM.

      IMO that seems legitimate, at least a unit-test exercising the VM could also do those things.

      But wouldn’t some (most?) MVVM purists not allow the view to have a hard reference to the VM?

      Thanks,
      Vic

      • Tony Sneed says:

        Hi Vic,

        Sorry it’s taken me so long to respond to your question. In MVVM the ViewModel is an abstraction of the View that can be tested independently. In my opinion that is one of the best reasons to use MVVM, that is, to make your app more testable and maintainable through a separation of concerns. There isn’t any reason I’m aware of for the View not to be aware of the ViewModel, so it’s perfectly acceptable to reference the ViewModel from the VIew, both in the XAML and possibly in the code-behind. On the other hand, if the ViewModel were to be aware of the View, or mix in UI concerns, it could render the ViewModel untestable.

        Another motivation for MVVM is to make life easier for a graphic designer using a tool like Blend. It’s for that reason that the ViewModel is instantiated by the View by setting the DataContext to the ViewModel in the XAML. Property values returned from the ViewModel can then be shown by a design tool such as Blend or even Visual Studio. That is why I pull out the ViewModel from the DataContext in the code behind in order to handle notification events from the ViewModel. There are other ways to achieve the same result that are more designer-friendly, such as Blend style triggers and behaviors. But the idea is the same: let the View take care of interacting with the user.

        Hope this helps!
        Cheers,
        Tony

  3. Brian says:

    Tony,
    Thank you for this post. I’m using Prism and MVVM for my project and have been trying to find a clean way to handle exceptions. Prism works nicely and I tried to use their event aggregator for this purpose but when you publish an event from the MVVM, it sends the message off to neverland so there’s no easy way to wait and receive a return response from the View. One way to receive a response is to create a listener but it all seems very messy as other events may fire while waiting for your modal result to return. The other way is to create a service locator but to be honest, your idea seems like it serves the same purpose but done in a much simpler way.

    This post has helped me out greatly. I don’t know why I didn’t think of it. At any rate, thank you.

  4. Tony Sneed says:

    @Brian, The interesting thing about how Simple MVVm Toolkit does messaging is that it uses different versions of a NotificationEventArgs class. There is a non-generic version that has a string Message property, and also two generic versions. One generic version takes a type to deliver a strongly-typed payload from the sender to the receiver. The other generic version takes two type arguments, and . This second type allows the receiver to call back the sender and pass a strongly-typed payload to the sender.

    The classic example is to obtain confirmation from the user to delete some entity. The ViewModel can send the entity to the View, which can pass it to a delete confirmation dialog. The View can then call back the ViewModel with the result of the dialog, which would probably be a boolean indicating whether the user confirmed or cancelled the deletion.

    The Simple MVVM Toolkit also has a MessageBus class, which is an event aggregator or mediator. But I recommend using it mainly for communication between ViewModels, where you do not want ViewModels to reference one another. However, there’s no need for the MessageBus if all you are doing is communicating between a View and its ViewModel. For that all you need to do is expose an event from the ViewModel and subscribe to the event from the View.

    Cheers,
    Tony

  5. st3fanus says:

    Hi Ton,

    I’m stefanus ( indonesian )

    I’m a newbie in the WPF n MVVM,
    currently i’m developing a small app for my friends, so i decide to use WPF & MVVM so that i hope i can learn a lot.

    I search on google and come to your article, and this article has given a lot for me,
    I’m interested to know “What you mean about communicating between view models”. ?

    Currently ,
    I have a window called it Form A, and then on Window A there is a button when i clicked on it , it will run a command to show / open a Child Window called it Window B to enter certain data ( Good for ex ) , and then on WIndow B after user complete and submit entring those data, the Window B will be closed and WIndow is refresh based on action on previous window B.

    Is it a scenario where i can use your solution or is it included in “Communicating between viemodels”. ?

    Thanks a lot
    God Bless You

  6. Tony Sneed says:

    Hello Stefanus,

    A child window is how Silverlight implements a modal dialog, so your situation would be addressed by this blog post on modal dialogs. The idea is that the code in Window A’s ViewModel calls the Notify method exposed by the ViewModelBase class, which fires an event. The Window A View subscribes to the event and responds by showing Window B (the child window). In the Closed event of Window B, Window A reads the properties of the Window B View and sends those back to Window A’s ViewModel.

    For an example of how to do this, take a look the Main sample installed by the toolkit, Part 1, and see how the Product main view opens up the ProductDetail view in order to add or edit a product.

    Cheers,
    Tony

  7. Mark says:

    Tony, I am looking at your Toolkit and I like what I see so far 🙂 Does it support XBAPs?

    • Mark says:

      I am going to answer my own question for now 🙂

      I did not find a template in Tony’s toolkit to do this, so I followed the steps in the link below and converted the WPF Win App to WPF XBAP

      http://www.charlespetzold.com/blog/2006/11/120718.html

      Keep in mind that I had to do one extra step that is not included in the link above. The “Platform target” (under Build) needs to be set to “Any CPU”

    • Tony Sneed says:

      Yes, XBAP’s are a WPF project type, so the toolkit supports it. There isn’t a specific project template for it, but just create an XBAP project and add the toolkit item templates.

      • Mark says:

        Tony, running into a problem with references. Mine is a XBAP app., and the IsInDesignTool property (!DesignerProperties.IsInDesignTool) in the InjectedViewModelLocator class does not compile.

        Your sample app version uses the DesignerProperties class found in System.Windows (Silverlight v4). Mine uses the DesignerProperties class found in System.ComponentModel and it does not have a property called IsInDesignTool

        Now, if I add (to my XBAP project) a reference to System.Windows (Silverlight v4), I get a lot more errors about existing classes found in different assemblies.

        Any suggestions?

      • Tony Sneed says:

        Yes, if you are using WPF, then you need to use the .NET 4 classes. You should remove the Silverlight reference from the “Injected ViewModel Locator” and use System.ComponentModel for .NET 4. The Silverlight references won’t work for WPF. 😉

  8. Mark says:

    Tony, still going over your toolkit. I like what I see so far. Very well organized. Wanted to run this by you. I need to effectively handle communication with the user (notifications, confirmation, popups, etc.) and your toolkit is the best “option” I have found. I did try going the Dialog Service route but it is just too much for what I need now. That said, I am converting a prototype I wrote so that it uses your toolkit and your templates. What I am finding is that they are not working exactly the same. Let me explain.

    I have a Data Form control and a Grid View. Imagine that I load Categories in the Data Form control and Products in the Grid View control. The load process works fine. What doesn’t work (my code using the Simple MVVM toolkit) is that Data Form control does not default to any Category. I have set the CurrentItem property of the Data Form

    CurrentItem=”{Binding Path=CurrentCategory, Mode=TwoWay}”

    But when CurrentCategory public property code is hit, the value is always null. Because of this, loading the Products fails. The “same” logic works fine on the prototype I have not using the Toolkit. Any ideas/suggestions on what could be the problem?

    We can take this offline if you would like.

    Thanks in advance…

  9. Mark says:

    Tony, using the BackgroundWorker for async calls has many advantages. But how would you go about highlighting a row in a GridView programmatically? Here is the scenario: A new “part” is added to a given Category, this means adding it to the database (thru the Service Agent) and doing a reload to refresh the Collection and the GridView. I should use the property bound to the GridView SelectedItem but this does not work because this “update” happens before the async call finishes. Any suggestions? Thank you!

    • Tony Sneed says:

      I think you need to set SelectedItem in the callback for the async service operation. Like you said, the callback will not fire until the async operation completes. So you want to trigger the UI update at that point in time. The setter for the SelectedItem property in the ViewModel should fire NotifyPropertyChanged, which will update any bound properties.

      Cheers,
      Tony

  10. Steve Stoural says:

    This is great. Do you think it would be suitable to utilize the NotificationEventArgs contruct to communicate between ViewModels as well? Not just ViewModel to View as described in your post.

  11. Joachim says:

    Hi Tony,
    great articel, thank you very much.
    Do you have an approach how to handle if a ModalDialog should be shown when the ViewModel send’s an event AND the dialog should be disaper, if the ViewModel send’s another event (maybe from a thread).
    As an example this could be the case if the ViewModel is loading or saving data and the ViewModel want’s to say “I’m busy”. In this case the View can show a Modal dialog or do something else (e.g. disalbe the whole window (Window.IsEnabled={Binding IsBusy}) ).

    • Tony Sneed says:

      In this case, I would recommend that the dialog have its own ViewModel. You can then bind to the IsBusy property on the dialog’s ViewModel while you are loading or saving data.

  12. Dean K. says:

    In the third paragraph of this article you say “but the view already holds a reference to the view so decoupling them seems pointless to me.” Did you mean to say “but the view already holds a reference to the VIEWMODEL so decoupling them seems pointless to me”. I’m pretty sure you did since the statement as written makes no sense…

Leave a Reply to Joachim Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.