11

Is there a clever way to detect whether a window was closed by

  • A user pressing the (X) button in the upper right corner of the window or
  • window.Close() has been called programatically.

I would like to detect this in the window.Closing handler. I could set a flag whenever I call window.Close(), but this is not a very pretty solution.

tshepang
  • 12,111
  • 21
  • 91
  • 136
mola
  • 1,120
  • 1
  • 8
  • 22
  • while it might be possible, this does not seem like a good idea to treat them differently.. From a UX point of view, Alt-F4/File->Exit/Closing with close button/closing programmatically should all be the same.. – stijn Nov 13 '12 at 13:30
  • 1
    I think it makes sense when you are using the close button press to hide a child window, but like to close the child window when application close button is pressed. – resp78 Oct 31 '16 at 13:13

2 Answers2

15

I'm not sure I like this at all but it's a question that you obviously have a reason for asking. if you were to take a stack trace in the OnClosing event you could look up for the Window.Close event.

protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
   bool wasCodeClosed = new StackTrace().GetFrames().FirstOrDefault(x => x.GetMethod() == typeof(Window).GetMethod("Close")) != null;
   if (wasCodeClosed)
   {
       // Closed with this.Close()
   }
   else
   {
       // Closed some other way.
   }

   base.OnClosing(e);
}
Andy
  • 6,366
  • 1
  • 32
  • 37
4

The difference is the following:

Window.Close() causes WM_CLOSE to be send to window.

Alt+F4 and X button causes WM_SYSCOMMAND message with SC_CLOSE type. You can decide if you wish to route this message further ( and cause WM_CLOSE in the end ).

Here's a piece of code to catch this message. Return "True" from delegate if you wish to cancel default behaviour:

class SystemMenu : IDisposable
{
    const int WM_SYSCOMMAND = 0x0112;
    const int SC_CLOSE = 0xF060;

    public delegate bool HandleSystemCommand();
    HwndSource _source;
    HandleSystemCommand _handler;

    public SystemMenu(Window window, HandleSystemCommand handler )
    {
        _handler = handler;
        _source  = HwndSource.FromHwnd(new WindowInteropHelper( window ).Handle);
        _source.AddHook(WndProc);
    }

    public void Dispose() {
        _source.RemoveHook(WndProc);
    }

    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        switch (msg)
        {
            case WM_SYSCOMMAND:
                int command = wParam.ToInt32() & 0xfff0;
                if (command == SC_CLOSE)
                    handled = _handler();
                break;
            default:
                break;
        }
        return IntPtr.Zero;
    }
}
norekhov
  • 3,915
  • 25
  • 45