博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

原文:http://www.chrisholmesonline.com/2008/04/10/unsaved-changes-when-cab-application-closes-the-notification-pattern/

 

Occasionally this question pops up on the CAB message boards: How do I prevent my application from closing if the user has unsaved changes?

Turns out that there’s a very simple pattern you can utilize to handle this situation. It’s called the Notification Pattern. Jeremy Miller, .Net guru, has a very good blog post on this pattern. He uses it to illustrate a standard way to handle validation on domain objects, but it’s a valuable pattern in other cases too, as I’ll show.

As Martin Fowler writes in his article, the Notification Pattern can be as simple as “a collection of strings which are error messages that the domain generates while it’s doing its work.” This is, in fact, how simple our implementation is where I work. A collection of strings, nothing more. You can use something else besides strings - a rich object with many properties and a public interface - if you like. But for this demonstration I’ll stick to strings.

The easiest way to implement this in CAB is with a Service class. All that is required is an event and a method to call to get notifications. The interface to the service basically looks like this:

public delegate void ApplicationClosingEventHandler(List<string> notifications);
public interface INotificationService
{
event ApplicationClosingEventHandler ApplicationClosing;
List<string> GetNotifications();
}

And the Service itself:


public class NotificationService : INotificationService
{
public virtual event ApplicationClosingEventHandler ApplicationClosing;
public virtual List<string> GetNotifications()
{
List<string> notifications = new List<string>();
if (ApplicationClosing != null)
{
foreach (ApplicationClosingEventHandler handler in ApplicationClosing.GetInvocationList())
{
handler.Invoke(_unsavedChangesNotifications);
}
}
return notifications ;
}
}

WorkItems, Presenters and other classes that know about the dirty state of their models can take a dependency on this Service and subscribe to the ApplicationClosing event. When the event is fired, their handler adds a notification to the list, if it needs to.


public MyPresenter : Presenter<IMyView>
{
private INotificationService _notificationService;
public MyPresenter([ServiceDependency] INotificationService notificationService)
{
_notificationService = notificationService;
_notificationService.ApplicationClosing += new ApplicationClosingEventHandler(AppClosingHandler);
}
private void AppClosingHandler(List<string> notifications)
{
if(myModelIsDirty)
notifications.Add("My object is dirty");
}
}

The next step is to hook the whole thing into the Shell.Closing event. Back in the ShellApplication class, typically in the AfterShellCreated override, you can wire up to the Shell.Closing event:

public override AfterShellCreated()
{
base.AfterShellCreated();
Shell.Closing += new CancelEventHandler(Shell_Closing);
}

In your handler, you can query the INotificationService and get any notifications. If the notification list is empty, you can safely exit the application. Otherwise, pop a MessageBox and alert them.

private void Shell_Closing(object sender, CancelEventArgs ee)
{
INotificationService notificationService = RootWorkItem.Services.Get<INotificationService>();
List<string> notifications = notificationService.GetNotifications();
if(notifications.Count > 0)
{
e.Cancel = true;
// Alert the user
}
}

That’s it. Simple, right? The nice thing about this pattern is that even with simple notifications, like strings, you can still give the user some very useful information on where unsaved changes exist. In our application, for instance, we let the user know which module and “use case” (very broad term here) contains the unsaved changes, so they can find those unsaved changes faster and make the save.

The Notification Pattern is one of the simplest, yet most useful, patterns that you’ll run into in any application, not just CAB. Now go forth and prevent your users from losing data when they close your app!