views:

90

answers:

3

When I utilize AddHandler in VB to add my own method to the Click event :

  AddHandler Button.Click, AddressOf myButton_Click

I see that my code executes last - after other event handlers for the Button_Click event. Is there a way to insert my event handler in front of other events so that it executes first?

I tagged this question as C# as well as VB, please feel free to use eitherlanguage if you have any suggestions.
Thanks!

+9  A: 

Not easily.

That being said, don't do it. Your code shouldn't care about what order it's called in - it should just care that the button in question was clicked. All of the handlers, including yours, will execute. If the order is important, you should rethink your design, and use some other mechanism to control that.

Reed Copsey
+1 for the good advice.
Kirk Woll
+2  A: 

It is more of an implementation detail of VB.NET, it has an alternate way of dealing with events using the WithEvents and Handles keywords. An event handler that uses Handles gets subscribed by auto-generated code in the form constructor. This code will run before any of your code, including InitializeComponent or your custom AddHandler statement. And will thus always run first.

Getting your code to guarantee to run first is possible. Derive your own class from Button and override the OnClick method:

Public Class MyButton
    Inherits Button

    Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
        '' Do your stuff here
        ''....

        '' Other event handlers will run now:
        MyBase.OnClick(e)
    End Sub
End Class
Hans Passant
+1  A: 

The order of execution of handlers of a single event cannot be controlled through the basic behavior of a built-in event itself. MulticastDelegates are "bags" of handlers, and they just grab them one at a time. Keep in mind that this is how most developers expect this to work, and it can be dangerous to allow order-dependent event handlers. Event handlers should normally not know about each other, because if they are dependent on being executed before or after another handler, they first have to know of the existence of the other handler (violating information hiding and several other design principles), and second, if that order changes, the behavior will be broken.

If you understand all this, and still want to control the order of execution of handlers of an event, the following will get you close.

  1. Create an ordered collection of delegates of the event handler type called MyHandlers. This will be a surrogate for the actual event's MulticastDelegate implementation.
  2. Create a "master" handler method that will actually be attached to the built-in event, and will iterate through MyHandlers and call each one.
  3. Define some means to add and remove handlers from the list. Some of this can be accomplished with a custom event "property", but that will define only add and remove behaviors, not insert.

The code might look like the following:

private List<EventHandler> MyHandlers = new List<EventHandler>()

private void MasterClickHandler(object sender, EventArgs e)
{
   foreach(var handler in MyHandlers)
      handler(sender, e); 
}

public event EventHandler MyControlButtonClick
{
   add { MyHandlers.Add(value); }
   remove { MyHandlers.Remove(value); }
}

public void InsertButtonClickHandler(EventHandler handler)
{
   MyHandlers.Insert(handler,0); //calling this to add a handler puts the handler up front
}

...

myForm.MyControl.Click += MasterClickHandler;

Notice that you're no longer attaching handlers other than MasterClickHandler to the actual event; you can't have your cake and eat it too, both overriding and keeping the basic event behavior. There also isn't an "insert" behavior built into the event "property"; you have to define a method that allows this. Lastly, you should never raise the event MyControlButtonClick directly (though as your control is the only one that can, this can be enforced by code inspection).

Now, when you click the button, the button's built-in Click event fires MasterEventHandler, which will execute the delegates in MyHandlers in the same order they were attached to MyControlButtonClick (with any that were inserted executed first, in the reverse order they were inserted). If you placed this code in a custom user control with the Button, you could even name the custom event on your control Click, and the control would look and work much like the Button it contains, except that it would have the extra control over inserting handlers. The beauty of the whole thing is that nothing about this code forces consumers to work with it as anything other than a plain ol' vanilla event.

KeithS