A Windows Forms application exists, when the main window is closed. On applications with multiple windows and no main window, this behavior is unwanted. In such a case the application should quit when the last window is closed.

Solution

A simple solution is to avoid the application start with the call Application.Run(Form). Instead an instance of ApplicationContext can be created and used. When the main form of the application context is closed, some other form has to be assigned to the MainForm property. The application will end, when the MainForm of the application context is closed without setting another form.

The Application class already has a collection of all open forms so there is no requirement to track the creation / closing of all form. Only the main form is of interest.

Algorithm suggestion

  • Create an ApplicationContext instance
  • Assign a Form to MainForm and subscribe to the ClosedEvent
  • When the closed event is triggered, check Application.OpenForms for any other open forms. If possible assign one to MainForm and subscribe to Closed.

Possible Implementation

The following implementation gives an example implementation.

FormsApplication class

using System;
using System.Windows.Forms;
 
namespace CloseApplicationAfterLastFormClosed
{
    /// <summary>
    /// Manages open Forms and makes sure that the ApplicationContext of the main loop always has
    /// an open form till the last form is closed.
    /// </summary>
    /// <remarks>
    /// To use this class: Simply replace the Application.Run call in program.cs with
    /// FormsApplication.Run.
    /// </remarks>
    public static class FormsApplication
    {
        /// <summary>
        /// Application context
        /// </summary>
        private static ApplicationContext _appContext;
 
        /// <summary>
        /// Run the Application Loop
        /// </summary>
        public static void Run()
        {
            _appContext = new ApplicationContext();
            Application.Run(_appContext);
        }
 
        /// <summary>
        /// Run the Application Loop with a given context.
        /// </summary>
        public static void Run(ApplicationContext context)
        {
            // Validate arguments
            if (context == null) throw new ArgumentNullException(nameof(context));
 
            // Set the application context
            _appContext = context;
 
            // add us to the closed event.
            if (context.MainForm != null)
                context.MainForm.Closed += ClosedEventHandler;
 
            // Start the main loop.
            Application.Run(context);
        }
 
        /// <summary>
        /// Run the Application Loop with a given form.
        /// </summary>
        public static void Run(Form form)
        {
            // Validate arguments
            if (form == null) throw new ArgumentNullException(nameof(form));
 
            form.Closed += ClosedEventHandler;
            _appContext = new ApplicationContext { MainForm = form };
            Application.Run(_appContext);
        }
 
        /// <summary>
        /// Event handler that removes a form when it is closed
        /// </summary>
        /// <remarks>
        /// The Application.OpenForms collection contained the window that was closing in my tests. But
        /// that is something that I do not trust because that might change depending on implementation
        /// details. So this code is safe.
        ///
        /// Important: This code is not thread safe! I trust that all changes are done by the UI thread
        /// as it should be done in all Windows Forms actions!
        /// </remarks>
        private static void ClosedEventHandler(object sender, EventArgs e)
        {
            // Validate arguments
            if (sender == null) throw new ArgumentNullException(nameof(sender));
            if (e == null) throw new ArgumentNullException(nameof(e));
 
            // Do nothing if the closed form is not the main form or if the count of open forms is 0.
            if (_appContext.MainForm != sender || Application.OpenForms.Count == 0)
                return;
 
            // Search for a form which is not
            foreach (Form form in Application.OpenForms)
            {
                // Continue search if form is sender.
                if (form == sender) continue;
                 
                // Set the MainForm, add listener to Closed Event and leave method.
                _appContext.MainForm = form;
                _appContext.MainForm.Closed += ClosedEventHandler;
                return;
            }
 
            // no other form found, nothing to do.
        }
    }
}

Using FormsApplication example

To use the given example, only a small change must be done to the default Program.cs
01.using System;
02.using System.Windows.Forms;
03. 
04.namespace CloseApplicationAfterLastFormClosed
05.{
06.    static class Program
07.    {
08.        /// <summary>
09.        /// The main entry point for the application.
10.        /// </summary>
11.        [STAThread]
12.        static void Main()
13.        {
14.            Application.EnableVisualStyles();
15.            Application.SetCompatibleTextRenderingDefault(false);
16.            FormsApplication.Run(new ExampleForm());
17.        }
18.    }
19.}
The only change is in line 16 - Application.Run was changed to FormsApplication.Run.

References