Prompting the User From a View Model–Prism 4 Gems

Prism 4 is out at last and has a lot of goodness added to what was already there from previous versions. I’ve obviously got a biased view – I had the privilege to work directly as a part time member of the team on Prism 4 as I did on Prism 1. That just means I was there enough to annoy the team and maybe help steer them in the right direction at times, although for the most part they didn’t need much steering as they are an incredibly talented and fun group to work with.

In Prism 4, you’ve still got the modularity, UI composition (regions), commands and events from Prism 2.2 and prior that work with both WPF and Silverlight. In addition, you’ve now got some great guidance on how to do the MVVM pattern, navigation, and using MEF for your dependency injection container under the covers instead of Unity. I’ll be posting some other articles on those features in the near future.

Along with the major features of Prism, there is an awful lot of useful code in the QuickStarts and RI samples that come along with Prism, and a few useful behaviors and coding patterns that were written in the process of fleshing out the MVVM pattern guidance – the hidden gems of Prism. This post will highlight one of those – interaction requests. This post assumes you are familiar with the MVVM pattern and the reasons why you would want to separate your view and your view model, as well as the standard structure of the MVVM pattern (View.DataContext  = ViewModel). The key things to keep in mind for what I am going to cover here is the it is the view’s responsibility to determine the visual rendering of a part of the application functionality, it is the view model’s job to encapsulate the state and logic that support that view without being coupled to the view too tightly and without specifying visual aspects directly itself. When doing MVVM right, you should be able to hand the view’s XAML over to a designer, allow them to change every single element in there if they choose to, and not break the application as long as they maintain the bindings that were in the view so they display things in similar ways.

How Hard Can a MessageBox Be?

Strangely enough, when you start doing the MVVM pattern, one challenge you quickly come across is that one of the most conceptually simple tasks in UI development suddenly becomes hard. A very common question is “How can I pop a MessageBox or prompt the user from my view model?” If done right, the view model is the place the logic will reside that determines when it is time to prompt the user. It may be based on some validation error, but for that you should probably be using the native validation mechanisms of WPF or Silverlight. But more likely it is because an async service call failed or something else happened in the background, and you need to notify the user and possibly collect an answer from them.

The obvious and simple answer is that a view model could simply do a MessageBox.Show(), ChildWindow.Show() in Silverlight, or Window.Show() in WPF. However, that would both be a violation of the design pattern because now you are not leaving all aspects of the visual presentation up to the view, and it would also limit the testability of your view model because that popping of dialogs is not going to work so well from an automated unit test.

So how can you show a prompt to the user without showing a prompt to the user? Well, there are a couple of ways you could go about it. But the way the Prism team came up with is simple, elegant, and Blendable, so what more do you need?

Step 1: Expose a Property of type IInteractionRequest from your ViewModel

The IInteractionRequest interface in the Prism.Interactivity library defines a simple API for a view model to notify a view when it needs an interaction with the user. It simply exposes an event that can be raised to the view:

   1: public interface IInteractionRequest

   2: {

   3:     event EventHandler<InteractionRequestedEventArgs> Raised;

   4: }

 

The event args carry along some information that can be used to prompt the user (just as data, not appearance) and the ability to wait and call back when that interaction is done:

   1: public class InteractionRequestedEventArgs : EventArgs

   2: {

   3:     public Action Callback { get; }

   4:     public Notification Context { get; }

   5: }

 

The Notification object just has a Content property of type object (which the view can put a data template on for rendering of complex information) and a string Title property, assuming you might want to present a pop up with a title of some sort in the view, but not requiring it.

There is a concrete implementation of this interface in Prism 4 called InteractionRequest<T> that has a pre-built behavior for the client side that can show a notification window (OK/Cancel) with minimal hook up. It is also extensible in that you can provide a custom template for the rendering of the window so you could accommodate Yes/No/Cancel or other kinds of user prompting with minimal effort.

So the first thing is to expose an IInteractionRequest property from the view model, using the InteractionRequest<Confirmation> type to make it minimal code for your view model.

   1: public SomeViewModel()

   2: {

   3:     DoWorkCommand = new DelegateCommand(OnDoWork);

   4:     PromptUserRequest = new InteractionRequest<Confirmation>();

   5: }

   6: public InteractionRequest<Confirmation> PromptUserRequest { get; private set; }

   7: public ICommand DoWorkCommand { get; private set; }

   8:  

   9: private void OnDoWork()

  10: {

  11: }

 

Step 2: Raise the Raised event from the ViewModel When Appropriate

When you want to prompt the user from the view model, simply call the Raise method on the InteractionRequest<T>, supplying the appropriate confirmation arguments.

   1: private void OnDoWork()

   2: {

   3:     // Do some work, then prompt the user

   4:     PromptUserRequest.Raise(

   5:         new Confirmation { Content = "Work has been done", 

   6:                            Title = "View model notification" }, 

   7:         (cb) => { UserInputResult = cb.Confirmed ? 

   8:                   "User clicked OK" : "User cancelled"; });

   9: }

Again, the content could be a whole object tree that can be rendered via a custom data template in the view, but in this case I am just showing simple text.

If you have further work to do after the user input is complete, you can handle the callback as I am showing here and you will be passed a Confirmed flag to tell whether they picked OK or Cancel. If you don’t need confirmation, you can just use InteractionRequest<Notification> as well.

Step 3: Add the InteractionRequestTrigger and PopupChildWindowAction Behavior to Your View

These two objects constitute a behavior based on the Blend SDK that get attached to your view through the Interaction.Triggers attached property. If you have not heard of Blend Behaviors before, do some quick reading here. The InteractionRequestTrigger binds to the exposed IInteractionRequest property on your view model, firing when the Raised event happens. The PopupChildWindowAction works in Silverlight to create a ChildWindow with OK/Cancel buttons and by default sets the Content property of the Notification object you pass when firing the event as the Content of the ChildWindow. It also sets the Title as the Title not surprisingly.

   1: <UserControl ...>

   2:     <UserControl.DataContext>

   3:         <local:SomeViewModel />

   4:     </UserControl.DataContext>

   5:     <i:Interaction.Triggers>

   6:         <prism:InteractionRequestTrigger SourceObject="{Binding PromptUserRequest}">

   7:             <prism:PopupChildWindowAction/>

   8:         </prism:InteractionRequestTrigger>

   9:     </i:Interaction.Triggers>

 

In this simple sample, when the user presses the button hooked up to the DoWorkCommand, they will see:

11-14-2010 8-35-23 PM

Pretty simple. If you wanted to put a custom rendering on the Content object, you would just define a data template as a resource and point to it from the ContentTemplate property on the PopupChildWindowAction:

   1: <UserControl.Resources>

   2:     <DataTemplate x:Key="flipIt">

   3:         <TextBlock Text="{Binding}" FontSize="25" FontFamily="Comic Sans MS"/>

   4:     </DataTemplate>

   5: </UserControl.Resources>

   6: <i:Interaction.Triggers>

   7:     <prism:InteractionRequestTrigger SourceObject="{Binding PromptUserRequest}">

   8:         <prism:PopupChildWindowAction ContentTemplate="{StaticResource flipIt}"/>

   9:     </prism:InteractionRequestTrigger>

  10: </i:Interaction.Triggers>

 

That’s all there is to it.

All of this applies to the WPF side of the house as well other than the PopupChildWindowAction. That is only in Prism for Silverlight, although it would be a simple matter to migrate the code to WPF. I’ll save that for a future post.

Enjoy. You can download the sample code here.

Comments are closed.