Click here to Skip to main content
Click here to Skip to main content

Closing Windows and Applications with WPF and MVVM

, 10 Aug 2012
Rate this:
Please Sign up or sign in to vote.
This article samples a MVVM conform implementation of startup and shutdown sequences for an application and its dialogs.

Introduction

This article presents an MVVM conform implmentation of an application that demonstrates how:

  • Application Startup/Shutdown and
  • Window Open/Close

can be implemented:

I have researched different solutions for closing windows in WPF for a while and I found a number of approaches (which was at first confusing). There are suggestions on the Internet [2] where an attached behaviour in the MainWindow of an application is used to close a window via an attached property [3] which is bound to a ViewModel. My sample application takes advantage of that solution and adds on to it. The sample implements:

  • Closing an Application's MainWindow (it makes no difference whether this is initiated by the MainWindow, the ViewModel, or the Windows Operating System - the App object is always in full control of the situation) 
  • A dialog specific viewmodel that can be used to drive a dialog and receive a DialogResult
    The dialog viewModel also sports a facitility for evaluating user input and producing corresponding messages (information, warning, error etc...) if some input is not of the expected quality.  

Using the code   

Application Startup and Shutdown 

The sequence for the application start-up is to:

  • Instantiate an object of the App class (which is generated by default in a WPF project) and use a Application_Startup method in that object to: 
    • Instantiate an AppViewModel (sometimes also referred to as workspace)
    • Instantiate an application MainWindow
    • Attach the ViewModel to the DataContext of the MainWindow
    • and Show the MainWindow
private void Application_Startup(object sender, StartupEventArgs e)
{
  AppViewModel tVM = new AppViewModel();  // Construct ViewModel and MainWindow
  this.win = new MainWindow();

  this.win.Closing += this.OnClosing;

  // When the ViewModel asks to be closed, it closes the window via attached behaviour.
  // We use this event to shut down the remaining parts of the application
  tVM.RequestClose += delegate
  {
    // Make sure close down event is processed only once
    if (this.mRequestClose == false)
    {
      this.mRequestClose = true;

      // Save session data and close application
      this.OnClosed(this.win.DataContext as ViewModel.AppViewModel, this.win);
    }
  };


  this.win.DataContext = tVM; // Attach ViewModel to DataContext of ViewModel
  this.InitMainWindowCommandBinding(this.win);  // Initialize RoutedCommand bindings
  this.win.Show(); // SHOW ME DA WINDOW!
}

This startup sequence has the advantage that View and ViewModel have no references of each other but can still work together perfectly. This is especially advantageous if you have to maintain more than one MainWinodw and you have to instantiate a different MainWindow based on the currently used configuration or a command line option. Command line parameters can also be evaluated in the Application_Startup method. The methode can then prepare the AppViewModel accordingly, and start-up the application in the expected manner (evaluating command line parameters is not implemented in the sample though).

The App class represents an outer shell that is wrapped around the instances of Views and ViewModels of an application. That outer shell should becomes active whenever the application starts up or shuts down.

The application shut down sequence is the inverse of the start-up sequence. That is, the MainWindow tells the AppViewModel that it has received a request for closing the application, the ViewModel evaluates this based on the current situation and either rejects it or confirms the request. 

Whether the shutdown sequence can take place or not is routed through the App class:

this.win.Closing += this.OnClosing;

private void OnClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
  e.Cancel = this.OnSessionEnding();
} 

private bool OnSessionEnding()
{
  ViewModel.AppViewModel tVM = this.MainWindow.DataContext as ViewModel.AppViewModel;
 
  if (tVM != null)
  {
    if (tVM.IsReadyToClose == false)
    {
      MessageBox.Show("Application is not ready to exit.\n" +
                      "Hint: Check the checkbox in the MainWindow before exiting the application.",
                      "Cannot exit application", MessageBoxButton.OK);
 
      return !tVM.IsReadyToClose; // Cancel close down request if ViewModel is not ready, yet
    }
 
    tVM.OnRequestClose(false);
 
    return !tVM.IsReadyToClose; // Cancel close down request if ViewModel is not ready, yet
  }
 
  return true;
}

This cascade of events takes place when the user clicks the MainWindow close button. It is slightly, different when the user selects the File>Exit menu option or presses the ALT+F4 Keys. In this case, the routed command binding in App.xaml.cs is executed:

win.CommandBindings.Add(new CommandBinding(AppCommand.Exit,
    (s, e) =>
  {
    e.Handled = true;

    ((AppViewModel)win.DataContext).ExitExecuted();
  }));

...which invokes the ExitExecuted() method in the AppViewModel class:

if (this.mShutDownInProgress == false)
{
  this.mShutDownInProgress = true;

  if (this.OnSessionEnding != null)
  {
    if (this.OnSessionEnding() == true)
    {
      this.mShutDownInProgress = false;
      return;
    }
  }

  this.WindowCloseResult = true;              // Close the MainWindow and tell outside world
                                             // that we are closed down.
  EventHandler handler = this.RequestClose;

  if (handler != null)
    handler(this, EventArgs.Empty);
}

...the above code calls the OnSessionEnding method in the App class via the delegate method property of the same name. This is done in such a convoluted way because I want to make sure that all shut down requests are handled by one method (App.OnSessionEnding()).

The shutdown progresses by setting the this.WindowCloseResult = true; which in turn invokes the attached DialogCloser property, which in turn will closes the MainWindow via the bound boolean property (see MainWindow.xaml). 

There is also a third path of execution towards the shutdown of the application, which is to shutdown the Windows Operating system. This event iterates through each application and asks them whether they can shutdown or not. We have set the SessionEnding="App_SessionEnding" property in the App.xaml code to invoke the OnSessionEnding method when Windows shuts down. This way, Windows will no longer shut down if the OnSessionEnding method signals that we still have things to save on disk (unless user forces Windows shut down of course).

To summarize this section. There are several execution paths for shutting down the main window, and through that, the application. All these paths are based on the same evaluation function (OnSessionEnding in App class) and end in one shutdown function OnClosed to make sure that session data can be saved before the application is down for good.

  • The main window can send the Closing event to check whether close down is OK via OnClosing method in the App class.
  • The main window can execute the AppCommand.Exit routed command (via File>Exit or Alt+F4) to shutdown the application through the ExitExecuted() method in the AppViewModel class.
  • The Windows operating system can invoke the App_SessionEnding method in the App class to gracefully shutdown our application

Dialog Open and Close

The previous section has described one case in which the attached property DialogCloser is used to close the MainWindow through setting the WindowCloseResult property to true. This attached behaviour (AB) is actually designed such that it can be used like a DialogResult. It is null by default and is set to true or false when the user closes the application via Cancel or OK.

I am using this behaviour to implement a viewmodel that drives a dialog (or any other view) in an MVVM conform way such that the dialog will not close unless the user has input data of the expected quality. The important theme here is that we have a viewmodel which drives the view through its complete life cycle without using any code behind. To do this, we have to construct a ViewModel and View object, attach the former to the DataContext of the later and subscribe to the Closing event of the view. Further more, View and ViewModel should implement an OK and Cancel Command to do the corresponding processing when the user clicks either button.

That seems to be a lot of work - so we have to do this for every ViewModel that drives a dialog? Too painful I hear you saying. Too convoluted some already said [2] claiming that code behind is OK in some cases. Some of you have a point there but if the complexity could by abstracted away into a neat little class which then could be used in a viewmodel whenever a dialog (or any other view) is needed to process input based on a OK or Cancel?

The DialogViewModelBase class implements the above items (and a bit more) needed to drive a dialog through its life cycle. The class can be used to make user input validation a trivial task.

This dialog is shown through the ShowDialog routed command in the AppCommand class, which is bound to the ShowDialog method in the AppViewModel class. This method invokes the ShowDialog method in the Util class. Here, we instantiate the dialog, attach a copy of the UsernameViewModel object and initiate the OpenCloseView property.

The UsernameViewModel class instantiates the DialogViewModel class in its OpenCloseView property. The OpenCloseView property is bound in the UsernameDialog.xaml

So, when you click OK or Cancel (or use the Escape key) in the UsernameDialog then you are actually executing a corresponding command in the DialogViewModel class. Now, executing the OK command invokes the PerformInputDataEvaluation method, which in turn, invokes an external method (ValidateData) bound to the EvaluateInputData delegate property.

The signature of that method is to return true or false depending on whether there are problems in the user input or not. If there are problems, these can be detailled, classified, and returned with Msg objects in the out List<msg> parameter. This list of messages is then fed into the ListMessages property of the ObservableCollection<msg> type. These messages are then visible with icons because the XAML of the dialog makes use of the CountToVisibilityHiddenConverter to hide the messages StackPanel when there are no messages. And it also uses the MsgTypeToResourceConverter to convert the category of a message into an image resource that can be displayed in the list of messages [4].

This input evaluation and message listing can be disabled by simple setting the EvaluateInputData delegate property to null. The dialog will then close via ViewModel when the IsReadyToClose is true.

Part 2 Using a DialogService

I received controversial feedback when I published the first version of this article. Some people were missing a dialog service implementation (I get to that in this section) and others were saying this approach of implementing dialogs is an over-design. Looking back on it, and having seen some other articles on similar topics [5], - I have realized that I stumbled into a "CodeProject trap" that snaps shut because some topics, such as, MVVM, are not so well defined and some patterns are quit different when being applied to solve different requirements.

My initial idea was to simply publish a small code example that could be used to implement simple WPF applications. Although, the requirements:

  • unit test
  • code re-use, and
  • seperation of concerns

[6] are more than useful problems to solve, they were not part of my initial concerns. I have reviewed a lot of articles on MSDN, CodeProject, and elsewhere since then and have come to the conclusion that a dialog service is a more than a useful exercise.

The key-terms to know are "Inversion of Control" (IOC) and Dependency Injection (DI). Just enter "IOC", "Dependency Injection", or "DialogService" into the search engine here at CodeProject or elsewhere and you will find numerous explanations on that topic.

I followed the IOC track and found there are lots of frameworks on that topic: Unity, MEF, StructureMap, Castle.Windsor, AutoFac, Chinch, LinFu, Hiro, and Ninject, which explains why people are so tired of being shown yet another framework Smile | <img src= " />  

I am not going to implement and document yet another framework in this article. Instead, we are going to look at a simplified implementation of a DialogService to explain and document the IOC service pattern with a sample implementation. I found such a sample implementation here at codeproject [5], simplified it a little bit more, and applied it to my sample code as described next.

Disore's original implementation [5] implements a few services to define interfaces for:

  • Reading Person information from XML,
  • Displaying MessageBox contents,
  • Using common dialogs, such as, Open File or Folder Browsing, and
  • Using ViewModels with a DialogService.

I was interested in the last point 'Using ViewModels with a "DialogService", only. So, I downloaded his source code and removed everything that was unnecessary for the DialogService implementation. This left me with the classes in the "Service" and "WindowViewModelMapping" folder:

We like simple things, right? It turns out that using these classes for the first time is simple (thanks to Disore's article and source code). But understanding their correct application in a non-intimidating way is all but simple. So, lets walk through the code to see what we can learn here.

The ServiceLocator class is static which means that it is initialized and loaded as soon as the .Net Framework loads the corresponding namespace. The dialog service starts its timely existence in the App.Application_Startup of the App class. The following lines:

// Configure service locator
ServiceLocator.RegisterSingleton<IDialogService, DialogService>();
ServiceLocator.RegisterSingleton<IWindowViewModelMappings, WindowViewModelMappings>(); 

initialize two services:  

  • A DialogService - which is used to create and show a View for a ViewModel, and
  • A WindowViewModelMappings service to associate (map) a ViewModel class with its corresponding View class.

We can register these services in any way we would like but we can (obviously) not instantiate a View for a ViewModel unless there is a map (association) for both classes. Disore [5] shows different ways for creating this map in his article. In our case, though, we use the below map:  

public WindowViewModelMappings()
{
  mappings = new Dictionary<type,>
  {
////{ typeof(AppViewModel), typeof(string) } ////,
{ typeof(UsernameViewModel), typeof(UsernameDialog)}
  };
} 

to associate the UsernameViewModel class with the UsernameDialog class at run-time. This comes to life in the below AppViewModel function:

public void ShowUserNameDialog()
{
  UsernameViewModel dlgVM = null;

  try
  {
    dlgVM = new UsernameViewModel(this.mTestDialogViewModel);

    // It is important to either:
    // 1> Use the InitDialogInputData methode here or
    // 2> Reset the WindowCloseResult=null property
    // because otherwise ShowDialog will not work twice
    // (Symptom: The dialog is closed immeditialy by the attached behaviour)
    dlgVM.InitDialogInputData();

    // Showing the dialog, alternative 1.
    // Showing a specified dialog. This doesn't require any form of mapping using 
    // IWindowViewModelMappings.
    dialogService.ShowDialog(this, dlgVM);

    // Copy input if user OK'ed it. This could also be done by a method, equality operator, or copy constructor
    if (dlgVM.OpenCloseView.WindowCloseResult == true)
    {
      Console.WriteLine("Dialog was OK'ed.");
      this.mTestDialogViewModel.FirstName = dlgVM.FirstName;
      this.mTestDialogViewModel.LastName = dlgVM.LastName;
    }
    else
      Console.WriteLine("Dialog was Cancel'ed.");
  }
  catch (Exception exc)
  {
    MessageBox.Show(exc.ToString());
  }
}

The call to InitDialogInputData() could be abstracted away into the interface of the dialog service but I am not going into that because, you feel comfortable with this concept, you should just use one of the 10 IOC containers listed above.

Summarizing the concept - there is one static class, the ServiceLocator [7] which is used to register the DialogService class and the WindowViewModelMappings class. The result of that registration is stored in a dictionary object in the ServiceLocator class:

private static Dictionary<Type, ServiceInfo> services = new Dictionary<Type, ServiceInfo>(); 

The call to the dialogService dialogService.ShowDialog(this, dlgVM); then pulls out the mapping service from the ServiceLocator dictionary which in turn pulls out the corresponding view from the WindowViewModelMappings dictionary:  

public bool? ShowDialog(object ownerViewModel, object viewModel)
{
  Type dialogType = windowViewModelMappings.GetWindowTypeFromViewModelType(viewModel.GetType());
  return ShowDialog(ownerViewModel, viewModel, dialogType);
}

Now we have an implementation that makes sure that Views and ViewModels are not aware of each other. Thats good for unit testing - which turns out to be simpler than you think. Just get, for example, Disore's source code, download and install the NUnit setup and use the Graphical GUI to get started with unit testing in 5 minutes.

Using an implementation like this uncovers other possebilities as well. Consider, for example, an application with an advanced and a basic view, -or an application in which you need a viewing mode or an editing mode. You could simply implement such run-time changes by mapping/re-mapping views at run-time.

Points of Interest

I have learned to use delegate methods to execute a flexible piece of code within a complex system, exposing thus, exactly what I need to expose while keeping the complexity of the overall system hidden. I also learned that common functions, such as, driving a dialog through its lifecycle can be encapsulated in a class, which can be instantiated when that dialog (or view) is being shown. Using a ServiceLocator can drive this development even further towards a professional implementation. I now have pattern that I can simply apply without having to care about Window Close, Closing, or any other events, because I could just implement:

  • the OpenCloseView property, the Input Evaluation method, and the ShowDialog method
  • plus the required XAML

and I am done with another dialog. Oh, and all that is done in an MVVM conform fashion. Now why would that not be a good approach?

References 

History   

  • 09. August 2012 Added DialogService implementation section and advanced code sample
  • 30 June 2012: Initial version
  • 3rd of July 2012 (fixed a bug in OnClosing method and moved util.ShowDialog out of ViewModel namespace

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Dirkster99

Germany Germany
The Windows Presentation Foundation (WPF) and C# are one of my favorite past time hobbies since I started developing Edi:
 
https://edi.codeplex.com/
 
and a few other sub-projects on Codeplex. I am normally an algorithms and structure type person but WPF has such interesting sides that I cannot help myself but get into this and MVVM.

Comments and Discussions

 
QuestionDownload Code PinmemberJose Betances3-Nov-12 12:09 
AnswerRe: Download Code PinmemberDirkster994-Nov-12 9:33 
GeneralGood article! PinmemberVolynsky Alex9-Aug-12 21:33 
GeneralRe: Good article! PinmemberDirkster9910-Aug-12 0:28 
GeneralRe: Good article! PinmemberVolynsky Alex10-Aug-12 0:30 
QuestionMessage Automatically Removed Pinmemberkuif8sa9s9-Aug-12 14:54 
General[OT]Where does your "Convert XAML Vector Graphic to PNG" article go? PinmemberShuqian Ying16-Jul-12 17:34 
GeneralRe: [OT]Where does your "Convert XAML Vector Graphic to PNG" article go? PinmemberDirkster9917-Jul-12 3:41 
GeneralRe: [OT]Where does your "Convert XAML Vector Graphic to PNG" article go? PinmemberShuqian Ying17-Jul-12 9:33 
GeneralRe: [OT]Where does your "Convert XAML Vector Graphic to PNG" article go? PinmemberDirkster9917-Jul-12 12:09 
GeneralRe: [OT]Where does your "Convert XAML Vector Graphic to PNG" article go? PinmemberShuqian Ying17-Jul-12 13:15 
QuestionI have done this quite a bit in the past and just use a simple service PinmvpSacha Barber2-Jul-12 22:46 
AnswerRe: I have done this quite a bit in the past and just use a simple service PinmemberDirkster993-Jul-12 6:40 
GeneralRe: I have done this quite a bit in the past and just use a simple service PinmemberKevin Marois3-Jul-12 9:09 
SuggestionOverdesign Pinmemberabdurahman ibn hattab2-Jul-12 21:20 
GeneralRe: Overdesign PinmemberDirkster993-Jul-12 5:56 
GeneralRe: Overdesign Pinmemberabdurahman ibn hattab3-Jul-12 7:01 
QuestionHere's a REALLY Simple Solution PinmemberKevin Marois2-Jul-12 8:12 
AnswerRe: Here's a REALLY Simple Solution PinmemberDirkster992-Jul-12 8:58 
GeneralRe: Here's a REALLY Simple Solution PinmemberKevin Marois2-Jul-12 9:06 
GeneralRe: Here's a REALLY Simple Solution PinmemberDirkster992-Jul-12 10:23 
GeneralRe: Here's a REALLY Simple Solution PinmemberKevin Marois2-Jul-12 10:45 
GeneralRe: Here's a REALLY Simple Solution PinmemberDirkster993-Jul-12 7:26 
GeneralRe: Here's a REALLY Simple Solution PinmemberKevin Marois3-Jul-12 9:12 
QuestionVote of 4 PinmemberGanesanSenthilvel2-Jul-12 7:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 10 Aug 2012
Article Copyright 2012 by Dirkster99
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid