by Laurent Bugnion via Silverlight on 10/18/2009 7:25:56 PM
It is often difficult for large applications with multiple, loosely coupled components to cleanly shut down. Once a clean shutdown procedure is established, adding new components can disrupt the whole sequence, and things get ugly fast.
In this article, we will show a way to use a Messenger to create a loosely coupled shutdown sequence, making it easy to add new components if needed. For the sake of the demonstration, we will use the MVVM Light Toolkit Messenger, but of course this can also be realized with other messaging systems.
As a bonus, the sample application will also show how to use the same Messenger class to enable communication between two ViewModels that don’t know each other, making it very easy to refactor the application or to modify the client application (for example by adding new screens, etc…).
CleanShutdown demo from Laurent Bugnion on Vimeo.
To cleanly shut down a system, we need a central point from which the shutdown sequence can be started. This central point (we’ll call it the ShutdownService) should be reachable from any place in the application in an easy way. This way, if we add new components, they can request shut down without the ShutdownService needing any modification.
Some components might need a little more time to process important information, to display a “shutdown animation”, to log some information to a web service, etc. Some of these operations might be asynchronous, meaning that the shutdown sequence should be interrupted temporarily. This should be implemented in a way that allows adding new components without the ShutdownService or other components being changed.
Some components might need to be notified when shutdown is happening, for example to save important information to the disk. Here too, we need to be able to add new components without changing anything else.
This is quite easy: we simply provide a static method in the ShutdownService class. This method called RequestShutdown can be called by any object in the application. A more sophisticated implementation could include a reason for shutdown, letting the ShutdownService decide if the complete shutdown sequence must be executed, of if it should be ignored (for example, in case of a critical application error, a “catastrophic shutdown”, it could be dangerous or impossible to attempt a clean shutdown. Similarly, we could avoid to use a static method, and instead store an instance of the ShutdownService in an IoC container.
The ShutdownService provides a possibility for any object in the application to interrupt shutdown. We use the MVVM Light Toolkit’s Messenger class so that objects can register to be notified when a shutdown sequence starts, and to interrupt it if needed. We will use a NotificationMessageAction<bool>. This message type contains:
Note that the recipient that interrupts shutdown is responsible for requesting shutdown again, after the asynchronous operation has completed. A more elaborate implementation could use a timeout to force shutdown if the asynchronous operation still did not complete after a certain time.
When shutdown is really happening, a last message will be sent to notify recipients that this time, it’s for real. this gives a unique opportunity to recipients to take last minute measures, such as saving their state to a file, etc. In the sample application, we use a CommandMessage with a string meaning NotifyShutdown. Recipients who subscribed to this message can do what needs to be done.
So time to see some code. The source code can be downloaded here. The application can be run in WPF or in Silverlight, and looks like this:
This class exposes one static method used by other objects to request shutdown. The class is instantiated on startup by the Application object (in App.xaml.cs).
public static void RequestShutdown() { var shouldAbortShutdown = false; Messenger.Default.Send(new NotificationMessageAction<bool>( Notifications.ConfirmShutdown, shouldAbort => shouldAbortShutdown |= shouldAbort)); if (!shouldAbortShutdown) { // This time it is for real Messenger.Default.Send(new CommandMessage(Notifications.NotifyShutdown)); Application.Current.Shutdown(); } }
In our sample application, we want to play an animation when the application is shut down. Animations are asynchronous, so we need to temporarily interrupt the shutdown sequence. To do this, we register for the message type NotificationMessageAction<bool>.
To make it easier to share this code between Silverlight and WPF, we place this code in a class called ShutdownAnimationService. This class is instantiated in the MainWindow (for WPF) or in the Page (for Silverlight).
Note: Some like to see MVVM applications without any code in the code-behind of a View. However, playing animations, showing popups and other functionalities are really the responsibility of the View. There is nothing wrong in placing code in the View, as long as it is View-specific!
Messenger.Default.Register<NotificationMessageAction<bool>>(this, message => { if (message.Command == Notifications.ConfirmShutdown) { if (!_shutdownAnimationHasRun) { var sbd = this.Resources["ShutdownStoryboard"] as Storyboard; if (sbd != null) { message.Execute(true); // true == abort shutdown sbd.Completed += ShutdownStoryboardCompleted; sbd.Begin(); } } // If the animation ran already, no need to reply // to the message, allow shutdown. } });
Remember that it is now the responsibility of this object to restart the shutdown sequence! It is done in the completed event of the Storyboard:
void ShutdownStoryboardCompleted(object sender, System.EventArgs e) { _shutdownAnimationHasRun = true; // Now that our pre-shutdown task is done // we can request shutdown again. ShutdownService.RequestShutdown(); }
Note: The animation is defined in the Page.xaml (for Silverlight) and in MainWindow.xaml (for WPF) and not in MainSkin.xaml. While storing the animation in the external resource dictionary would work in WPF, it fails in Silverlight.
The class SettingsViewModel defines three properties (Red, Green and Blue) that are databound to the 3 sliders. In fact, the SettingsViewModel is the DataContext of the half-white panel on the left of the X button.
public SettingsViewModel() { if (!IsInDesignMode) { Messenger.Default.Register<CommandMessage>(this, m => { if (m.Command == Notifications.NotifyShutdown) { SaveSettings(); } }); _settingsFile = new SettingsFileHandler(); var savedSettings = _settingsFile.LoadSettings(); Messenger.Default.Send<Brush, MainViewModel>( savedSettings.ApplicationBackgroundBrush); _isLoading = true; var solidBrush = savedSettings.ApplicationBackgroundBrush as SolidColorBrush; if (solidBrush != null) { Red = solidBrush.Color.R; Green = solidBrush.Color.G; Blue = solidBrush.Color.B; } _isLoading = false; } }
The saving itself happens in SettingsFileHandler, which is also responsible for loading the settings when the application starts.
The MainViewModel is set as the DataContext of the application’s window (in Silverlight, of the Page), with the exception of the half-white area with the 3 sliders (which is bound to the SettingsViewModel). Clicking the X button executes a RelayCommand defined in the MainViewModel. This command is very simple: It just calls the RequestShutdown method.
ShutdownCommand = new RelayCommand(ShutdownService.RequestShutdown);
Another thing the MainViewModel does it define a bindable property called BackgroundBrush. This property, when updated, automatically sets the background of the application (through databinding). However, how do we know when the user changes the Brush?
In this simple application, we don’t really need two ViewModels. However, it makes sense to have them: If we decided to move the 3 sliders to a popup, for example, or to another window, it would be very easy to do, thanks to the separation of concerns: MainViewModel takes care of the MainWindow, SettingsViewModel takes care of the settings.
This poses a problem however: Since both ViewModels are separated, how can they communicate without being tightly coupled, which we really want to avoid. Well this is another usage for the Messenger: The MainViewModel registers for any message of type Brush:
public MainViewModel() { if (IsInDesignMode) { BackgroundBrush = new SolidColorBrush(Colors.Orange); } else { Messenger.Default.Register<Brush>( this, true, m => BackgroundBrush = m); ShutdownCommand = new RelayCommand(ShutdownService.RequestShutdown); } }
Whenever a message of type Brush is received, we just set the BackgroundBrush to it. The Messenger class can be used to send any message type, from simple values to complex objects!
Note that we use an overload of the Register application: The second parameter is set to true, indicating that we want to receive all messages of type Brush, and derived types too (SolidColorBrush, LinearGradientBrush, etc…).
If we are in design mode (running in Expression Blend for example), we set the brush to Orange, so that we have something to see (otherwise the window would be transparent).
Finally, we need someone to send updates! This is the SettingsViewModel’s task: When the user moves the sliders, we send a message of type SolidColorBrush, corresponding to the value of the 3 sliders. We do, however, send this message only to MainViewModel, thus avoiding too much unnecessary communication.
private void SendBrushUpdate() { if (_isLoading) { return; } Messenger.Default.Send<Brush, MainViewModel>( GetCurrentBrush()); } private Brush GetCurrentBrush() { return new SolidColorBrush( Color.FromArgb(255, Red, Green, Blue)); }
These few techniques (partial class with specific Silverlight extensions, extension methods, adding items as link) help to “bridge the gap” between WPF and Silverlight and to write less code while providing similar functionality.
This useful message type (allowing to send a notification and providing a callback to the recipient) is not yet part of the MVVM Light Toolkit V2, but will be included in future versions. An implementation is provided as part of the sample application, but is not final yet. Comments or feedback is welcomed!
The sample application’s source code can be downloaded from my site.
This sample application shows two usages of the Messenger class:
There are of course many other situations where a Messenger can be useful in an application. I hope to have awaken your interest for this very powerful yet simple to use component. For more information about the Messenger, RelayCommands or other parts of the MVVM Light Toolkit, please refer to the Get Started page.
Original Post: Clean shutdown in Silverlight and WPF applications
The content of the postings is owned by the respective author. Silverlight Feeds is not responsible for the contents of the postings. This site is automatically generated and cannot be reviewed for abusive content. If you find abusive content on Silverlight Feeds, please contact us. Designated trademarks and brands are the property of their respective owners. All rights reserved.