Supporting Graceful Shutdown and Saving on Close From a WPF Prism App

When you start building applications the Prism way, your UI logic can be spread out across any number of loosely-coupled, independent modules and their supporting code. This is a good thing in terms of breaking up the functionality of your application into reusable components and being able to distribute the work across multiple developers or teams. However, the decentralizing of the UI logic can lead you to scratch your head frequently when you need to implement some piece of functionality that sort of naturally feels like the main window has to handle it all.

A common requirement that is one of these common head scratchers is what to do when you want to prevent the application from shutting down until all work in progress (unsaved documents, pending downloads, etc.) is complete. This came up (again) with a customer the other day, so I figured it would make a good post (thanks for the idea John :) ).

In a Prism app, the code that performs those actions and manages the state associated with them should be implemented within the modules that are loading into the application, and that code probably belongs in a ViewModel or Controller that is supporting the associated views.

So when the user clicks on the red X to close the application, or invokes some other command from the menu or toolbar that indicates it is time to close the application, you need a clean communication path from the shell down to all chunks of code that may need to vote on whether the application should be allowed to shut down right now.

Picking a Communication Option

Prism offers two loosely-coupled communication mechanisms: Prism events and commands. One approach that might occur to you is to use Prism events and have the shell publish a shutdown event, and have the module code subscribe and handle it. This could work, but part of the pattern of pub-sub events is that the publisher should not know or care if there are any subscribers out there, nor should it care what the subscribers do if they are there.

So if you just wanted to notify the module code that shutdown is happening, you could use a Prism event, but the problem is that you probably need to block the shutdown until they have all done their clean up, and you often need to be able to cancel shutdown if there is unsaved state or incomplete tasks.

One of the key differentiators between events and commands in my mind is that commands represent an action taking place for which there is an expectation that there will be some handling code out there. So for a shutdown communication to the modules, a command is a better way to go. And the way Prism CompositeCommands work, all of the contained command handlers will be invoked before the execution of the command is complete.

In general, a command invoker is not expecting any results back from the handlers. However, to support the cancellation of shutdown, we would need to effectively pass arguments back to the invoker to let it know whether to complete the shutdown. To do this, we can just piggy back on a well-established pattern in WPF and Windows Forms – pass a CancelEventArgs argument as the command parameter, and allow the handlers set the Cancel property of that parameter to true if they don’t want shutdown to happen.

Putting it Together

To demonstrate one way to put this together, I’ve put together a simple Prism application that is like a simple Wordpad of sorts. You can fire it up, open multiple documents in tabs, edit them, and save them independently. You can see the app running below. A few minor bells and whistles include setting the tab header to the document title, and showing “dirty” documents by making their tab header bold/italic. The New command in the toolbar is always enabled, but the Save command is only enabled if the currently active document is dirty. I’ll do a follow on post about this aspect, which uses the  IActiveAware facility of Prism.

DocEditorApp

The logic code for supporting an individual instance of an editor views is fully implemented in the ViewModel class for the view, DocumentEditorViewModel.

There is also a controller class that takes care of creating the instances of the view when the New command is invoked.

I’m not going to drill into all aspects of the application architecture, but you can take a look at it as a fairly well organized example of a Prism app if you download the sample code. The basic project structure is shown below. I have a ShellApp that is the WPF application project. It contains a bootstrapper like most Prism apps that gets things going by presenting the shell. The MainWindow class is the shell window, and has two regions in it: a MainToolbarRegion and a DocumentRegion. The toolbar is (not surprisingly) a Toolbar, and the document region is just a TabControl. There is also a StatusBar at the bottom to show an indication of why close is not happening if one of the documents needs to be saved.

ProjectStructure

The SomeApp.Common is just a class library that contains the shared types between the shell and the modules – specifically some constants and the commands in this case.

The SomeDocumentModuleLib is the only module in this example, and contains the SomeDocumentModule, the DocumentController, the DocumentEditorViewModel, and its supporting DocumentEditorView. The view class (DocumentEditorView) is very simple, just a TextBox and the IActiveAware implementation I’ll talk about in a subsequent post.

The controller is called by the module’s Initialize method to get things going, and it populates the toolbar with the New and Save command buttons using Prism regions, and also has the handling code for New, which simply creates a new instance of a view and a viewmodel and marries them (sets the view’s DataContext = viewmodel).

The ViewModel is where the action is at for both the Save command and the Shutdown command that I am really trying to focus on here.

Notifying ViewModels of Shutdown

To get the word out to the ViewModel instances for open documents when someone goes to close the application, you have to first start with the WPF Window.Closing event that is fired. That event passes a CancelEventArgs allowing you to prevent the closing of the Window. But first off we don’t want to do logic handling in a view (the Shell), and second, the Shell is supposed to be fairly stupid about what the app components want to do about the event anyway. So we need a clean way to dispatch the event out as a command and get results back to decide whether to let the close happen or not.

The way I chose to approach it is to use a Prism CompositeCommand as the form of communication. Each ViewModel (or other interested background code) can provide a command implementation for that command, and in that implementation they can decide and modify the argument that is passed to indicate whether they are OK with the shutdown or not.

So starting at the source, we first have to handle the Closing event at the Window level:

<Window x:Class="ShellApp.MainWindow"
    ... 
    Closing="OnWindowClosing"
     >

To dispatch the command to others, we need a command definition.

public static class Commands
{
    ...
    public static readonly CompositeCommand ShutdownCommand = new CompositeCommand();
}

The ShutdownCommand is the one we are focusing on here.

The code behind handler for the Closing event basically needs to execute the Shutdown event and pass along the CancelEventArgs as a command parameter, allowing the handlers of the command to potentially set Cancel to true:

private void OnWindowClosing(object sender, CancelEventArgs e)
{
    if (Commands.ShutdownCommand.CanExecute(e))
        Commands.ShutdownCommand.Execute(e);
    if (e.Cancel)
        m_StatusText.Text = "One or more documents need saving before you can shutdown";
}

 

The way the CompositeCommand works in Prism, it will go out to all command handlers that register with the command and invoke their handling code.

The DocumentEditorViewModel provides the implementation of that command, one for each instance of an open document:

public DocumentEditorViewModel()
{
    ...
    ShutdownCommand = new DelegateCommand<CancelEventArgs>(OnShutdown);
    Commands.ShutdownCommand.RegisterCommand(ShutdownCommand);
}

public DelegateCommand<CancelEventArgs> ShutdownCommand { get; set; }

private void OnShutdown(CancelEventArgs args)
{
    if (IsDirty)
    {
        args.Cancel = true;
    }
}

The view model also maintains the dirty state of the document and uses it to drive both the enablement of the Save command, as well as to decide whether to allow shutdown. If the document is dirty, it simply sets the CancelEventArgs.Cancel property to false. Since the same argument is passed by reference to all the handlers of the command, if any one sets it to true, Closing will not happen.

That’s all there is to it. Piece of cake, right? Clean, simple, loosely coupled communications that achieves the end goal. Shell doesn’t have to have any knowledge of what it takes to allow the app to shut down, and the individual chunks of code that care don’t have to know about each other.

Any questions or comments, please post a comment and I’ll get back to you.

You can download the full sample app code here.

Comments are closed.