How do I show an unsaved changes dialog box before application closing?

1.5k Views Asked by At

I have a C# Windows Forms Application

I want to show a "There are unsaved changes. Save/Quit/Cancel" style confirmation dialog before the Application shuts down. Potentially also while prevent the Application Shutdown. How do I do that?

Also if the application does not close, what wil happen with the Shutdown Process on a Windows 10?

2

There are 2 best solutions below

0
Christopher On BEST ANSWER

Depends on the Display technology (WPF/UWP or WinForms), but there is usually a "Closing" Event for each form. Where you can show that MessageBox and even cancel the Form closing.

They are both on the basic class for the Form/Windows:

https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.form.closing

https://learn.microsoft.com/en-us/dotnet/api/system.windows.window.closing

The application itself will only close after all forms have closed (this can include hidden ones), so this will prevent the application from closing. However sepereate Forms might still be closed if they do not resist.

If your application will not close and the user does not pick "Force Close", the Shutdown Process of Windows will stop eventually with a silent failure. However that is considerd bad behavior. Indeed a few times I thought it had restarted, but in reality the Shutdown had stalled because of someone.

Especialy Disk operations can cause relevant stalls on application closure. Many Word Processors work on a hidden, temprary file for crash recovery purposes. And thus can always just default writing the state there, if the user does not repsond.

0
AudioBubble On

You can use the FormClosing event like this:

private void FormTest_FormClosing(object sender, FormClosingEventArgs e)
{
  if ( ThereAreUnsavedChanges )
    switch ( MessageBox.Show("Message", 
                             Text,
                             MessageBoxButtons.YesNoCancel,
                             MessageBoxIcon.Question) )
    {
      case DialogResult.Yes:
        DoSave();
        break;
      case DialogResult.No:
        break;
      case DialogResult.Cancel:
        e.Cancel = true;
        break;
    }
}

You can use a custom form instead of a message box:

private void FormTest_FormClosing(object sender, FormClosingEventArgs e)
{
  if ( ThereAreUnsavedChanges )
    switch ( new SaveDataQueryBox().ShowDialog() )
    {
      case DialogResult.Yes:
        DoSave();
        break;
      case DialogResult.No:
        break;
      case DialogResult.Cancel:
        e.Cancel = true;
        break;
    }
}

In this form you will assign the DialogResult property depending of the user choice.

  • In the button Save click event:

    DialogResult = DialogResult.Ok;
    Close();
    
  • In the button Quit click event:

    DialogResult = DialogResult.No;
    Close(); 
    
  • In the button Cancel click event:

    DialogResult = DialogResult.Cancel; 
    Close(); 
    

For the ending session, you can add this in the constructor after InitializeComponents:

SystemEvents.SessionEnding += SessionEnding;

Adding in the class:

private bool IsSessionEnding;

private void SessionEnding(object sender, SessionEndingEventArgs e)
{
  IsSessionEnding = true;
  Close();
}

Hence the FormClosing will be:

private void FormTest_FormClosing(object sender, FormClosingEventArgs e)
{
  if ( IsSessionEnding)
    // Decide if you want to auto save or not
  else
  if ( ThereAreUnsavedChanges )
    switch ( MessageBox.Show("Message", 
                             Text,
                             MessageBoxButtons.YesNoCancel,
                             MessageBoxIcon.Question) )
    {
      case DialogResult.Yes:
        DoSave();
        break;
      case DialogResult.No:
        break;
      case DialogResult.Cancel:
        IsSessionEnding = false;
        e.Cancel = true;
        break;
    }
}

If you don't do that, when session is ending the user will be prompted and the operating system blocks, seeing an app non responding message box unless it was setup to force process termination.

To ensure all forms will be closed you can use this method:

private void CloseForms()
{
  foreach ( Form form in Application.OpenForms )
    if ( form != this && form.Visible )
      try
      {
        form.Close();
      }
      catch
      {
      }
}

You can call it from the FormClosed event for example.