views:

480

answers:

3

How does the e.Cancel event work in the FormClosing event on a WinForm? I know you set it to True to cancel the closing, but at what point does the form process this? Is there a secondary action taken by the property?

How could I implement a similar action in a custom control? (C# or VB)

Note: I've looked for about 30 minutes now and couldn't find any answers in Google or the SO search, so if it's a duplicate, my bad.

A: 
function OnMyCancelableEvent()
{
   var handler = CancelableEvent;
   var args = new CancelEventArgs()
   if(handler != null)
   {
        handler(this, args)
        if(args.Canceled)
           // do my cancel logic
        else
           // do stuff
   }
}
Alex Reitbort
+1  A: 

Following the standard event generating pattern used in Windows Forms:

public event CancelEventHandler MyEvent;

protected void OnMyEvent(CancelEventArgs e) {
  CancelEventHandler handler = MyEvent;
  if (handler != null) {
    handler(this, e);
  }
}

private void button1_Click(object sender, EventArgs e) {
  CancelEventArgs args = new CancelEventArgs();
  OnMyEvent(args);
  if (!args.Cancel) {
    // Client code didn't cancel, do your stuff
    //...
  }
}
Hans Passant
Thanks for showing me how to implement this.
Stevoni
+3  A: 

I think the original poster might be wondering what happens when some subscribers set Cancel = false and some subscribers set Cancel = true. If this is the case, then the question "when does the form process this" takes on more importance.

At first I wondered whether the setter was implemented to OR or AND each value. Using Reflector to inspect the setter for CancelEventArgs.Cancel shows it simply sets a private field:

public bool Cancel
{
    get{ return this.cancel; }
    set{ this.cancel = value; }
}

So I figured peeking into 'Form.OnClosing(CancelEventArgs args)' would show when the value is checked, like the previous answers, but that is not what Reflector shows:

[EditorBrowsable(EditorBrowsableState.Advanced)]
protected virtual void OnClosing(CancelEventArgs e)
{
    CancelEventHandler handler = (CancelEventHandler) base.Events[EVENT_CLOSING];
    if (handler != null)
    {
        handler(this, e);
    }
}

So I enabled source debugging and found getting the EVENT_CLOSING delegate from the Events collection drops deep down into the windowing API such that handler in the first line of OnClosing is null when the form sets Cancel = true, meaning the managed code never really tests whether CancelEventArgs.Cancel == true. If you want the ugly guts of what happens inside the EventHandlerList, you get this:

get { 
    ListEntry e = null;
    if (parent == null || parent.CanRaiseEventsInternal) 
    {
        e = Find(key);
    }
    if (e != null) { 
        return e.handler;
    } 
    else { 
        return null;
    } 
}

While debugging, parent.CanRaiseEventsInternal is false if the closing was cancelled.

So ... the actual implementation of canceling the closing of a form is more complicated than the previous answers, but their suggestions for how to cancel your own events correctly show how to do it in managed code. Call the CancelEventHandler and then test the value of CancelEventArgs.Cancel after all the subscribers have had a chance to set the value to true. This still does not answer what happens if some subscribers set Cancel = false and some set Cancel = true. Does anyone know? Would something like the following be required?

public bool Cancel
{
   get{ return this.cancel; }
   set{ this.cancel = this.cancel || value; } 
}
flipdoubt
Thanks for explaining the process flow. Event if it is way more convoluted than it may need to be.
Stevoni