Wednesday, November 29, 2006
« Virtual PC Fun - Blasts From the Past | Main | SQL Server 2005 All-Day Recap »

I was going though some old code that I wrote a few years ago and stumbled upon a little nugget that might prove to be useful.  Back in my VB 2.0-6.0 days there was an event on a Form called QueryUnload.  This handy little event proved to be very useful because not only could it be cancelled (effectively stopping the form from unloaded), but it also provided some information as to how the form was closing (e.g. whether the user clicked the 'X', code closed it, or the OS was shutting down).

Unfortunately nothing exists in the .NET Framework out of the box that provides this functionality.  I had this need a long time back and drummed up some code that may prove to be useful out there, so here it is.

Essentially, I have a base form class (appropriately named FormBase) which exposes an UnloadMode property that you can query in the _OnClosing event (note that the OnClosing method is sealed so you need to add the handler rather that override the OnClosing method directly).  The UnloadMode property indicates how the form is closing down.

Here's the code:

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace Devstone.Demo.Forms {

#if ( DEBUG )
   public class FormBase : Form {
#else
   public abstract class FormBase : Form {
#endif

      public enum FormUnloadMode : int {
         None,
         FormControlMenu,
         Code,
         OperatingSystemShutDown
      }

      private bool _closeButton = false;
      protected FormUnloadMode ulMode = FormUnloadMode.None;

      public FormUnloadMode UnloadMode {
         get { return ulMode; }
      }

      protected sealed override void OnClosing(CancelEventArgs e) {
         // set the flag back to false so as to not prevent the WM_CLOSE message
         // from changing the UnloadMode

         _closeButton = false;

         // ensure that the event will be properly raised in the derived classes
         base.OnClosing(e);

         // reset the UnloadMode flag
         ulMode = FormUnloadMode.None;
      }

      protected override void WndProc(ref Message m) {
         
const int WM_CLOSE = 0x0010;
         const int WM_SYSCOMMAND = 0x0112;
         const int WM_ENDSESSION = 0x0016;
         const int SC_CLOSE = 0xF060;

         switch ( m.Msg ) {
            case WM_CLOSE:
               if ( !_closeButton ) ulMode = FormUnloadMode.Code;
               break;

            case WM_SYSCOMMAND:
               if ( m.WParam.ToInt32() == SC_CLOSE ) {
                  _closeButton = true;
                  ulMode = FormUnloadMode.FormControlMenu;
               }
               break;

            case WM_ENDSESSION:
               ulMode = FormUnloadMode.OperatingSystemShutDown;
               break;
         }

         base.WndProc(ref m);
      }

   }

}

I hope this proves helpful in some small way.  In terms of future enhancements, I may go ahead and implement a QueryUnload method on this to hearken back to the older programming model (which is perhaps a bit more clear in some instances).  If I do, I'll be sure to update the blog accordingly.