Saturday, March 03, 2007

Dumping events

I'm often surprised (or is it dismayed) when questions pop up in news groups surrounding things like event orders for winforms or webforms applications... this isn't rocket science... we're given all the tools to make this easy to figure out!

Lets do a winforms 2.0 app as an example... first off, the earliest point at which can easily get involved is the constructor...  lets have a look:

public partial class Form1 : Form

{

    public Form1()

    {

        AttachToAllEvents();

        InitializeComponent();           

    }


So I'm going to attach to all the events before the forms components are initialized... now lets have a look at the "AttachToAllEvents" method.

private void AttachToAllEvents()

{           

    Type type = GetType();                      

 

    foreach (EventInfo info in type.GetEvents())

    {

        string eventName = info.Name;

 

        EventHandlerWrapper wrapper = new EventHandlerWrapper(new EventHandler(

            delegate

            {

                Console.WriteLine("{0}: EventName: {1}, IsVisible: {2}, IsHandleCreated: {3}, HasChildren: {4}, IsDisposed: {5}",

                                       DateTime.Now, eventName, this.Visible, this.IsHandleCreated, this.HasChildren,

                                       this.IsDisposed);                                                                       

            }));

 

        wrapper.Attach(this, info);               

    }

}


Only magic there is we're using a class called "EventHandlerWrapper" - what's that... well, it's used to create a strongly typed delegate for attaching to an event.

The reason we need this at all is because EventInfo.AddEventHandler(...) is fussy about the kind of delegate you supply, so if you pass in an "EventHandler" for a "CancelEventHandler" event, it'll throw an exception complaining about it's inability to cast between them... there might be an easier way to do this, but I haven't come across it so far.

public class EventHandlerWrapper

{

    private EventHandler _handler;

    private static readonly MethodInfo _methodInfo;

 

    static EventHandlerWrapper()

    {

        _methodInfo = typeof(EventHandlerWrapper).GetMethod("InvokeHandler", BindingFlags.NonPublic | BindingFlags.Instance);

    }

 

    public EventHandlerWrapper(EventHandler handler)

    {

        if (handler == null) throw new ArgumentNullException("handler");

        _handler = handler;

    }

 

    public void Attach(object target, EventInfo info)

    {

        Delegate wrappedHandler = Delegate.CreateDelegate(info.EventHandlerType, this, _methodInfo);

        info.AddEventHandler(target, wrappedHandler);       

    }

 

    private void InvokeHandler(object sender, EventArgs args)

    {

        _handler(sender, args);

    }

}


With a little brain power I'm sure I could've done this without the separate wrapper class, but this is probably a little easier to read at any rate.

Results..?

So.. onto the results - once we run the code and see exactly what order events are happening in, we can then make a pretty table that may not render in most browsers because I cut 'n pasted it from Excel 2007 ;o)

EventName  IsVisible  IsHandleCreated  HasChildren  IsDisposed
Resize  False  False  False  False
SizeChanged  False  False  False  False
ClientSizeChanged  False  False  False  False
ClientSizeChanged  False  False  False  False
ControlAdded  False  False  True  False
ControlAdded  False  False  True  False
StyleChanged  False  False  True  False
TextChanged  False  False  True  False
Move  False  True  True  False
LocationChanged  False  True  True  False
HandleCreated  False  True  True  False
Invalidated  False  True  True  False
StyleChanged  False  True  True  False
ChangeUICues  True  True  True  False
Invalidated  True  True  True  False
BindingContextChanged  True  True  True  False
Load  True  True  True  False
Layout  True  True  True  False
VisibleChanged  True  True  True  False
Activated  True  True  True  False
Shown  True  True  True  False
Paint  True  True  True  False
Paint  True  True  True  False
Paint  True  True  True  False
MouseCaptureChanged  True  True  True  False
Closing  True  True  True  False
FormClosing  True  True  True  False
Closed  True  True  True  False
FormClosed  True  True  True  False
Deactivate  True  True  True  False
HandleDestroyed  True  True  True  False
Disposed  False  False  False  False

The main thing to keep in mind when doing something like this is to avoid making assumptions - we may not be the first or last to attach to the events (depending on the complexity of the form) - and events can trigger other events... which could explain the ordering of some of this data i.e. changes in visibility and handle creation... if anything we are viewing the order of consequences, as opposed to the true order in which the events are invoked (to get that we'd need to override all the OnXXXX methods of the form class... which would be a good job for dynamic proxy :)
posted @ Saturday, March 03, 2007 12:54:32 AM (New Zealand Daylight Time, UTC+13:00)    Comments [0] | Trackback |
Search
FeedCount

Tags...
Who am I?
Alex Henderson
Alex Henderson
Auckland, New Zealand
Managing Director at Dev|Defined Limited

"Self Confessed Coding Junky for 15 years"
View Alex Henderson's profile on LinkedIn
 
Mobile: +64-21-402-969
Email: bittercoder 'at' gmail 'dot' com
MSN: bittercoder_nz@hotmail
Skype: alex.devdefined
Navigation