Event orders...
Dumping events
I'm often surprised (or is it dismayed) when questions pop upin 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 exactlywhat 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)
cellpadding="0" cellspacing="0" width="563">
height="20" width="162"> EventName
width="81"> IsVisible
width="128"> IsHandleCreated
width="100"> HasChildren
width="92"> IsDisposed
height="20"> Resize
height="20"> SizeChanged
height="20"> ClientSizeChanged
height="20"> ClientSizeChanged
height="20"> ControlAdded
height="20"> ControlAdded
height="20"> StyleChanged
height="20"> TextChanged
height="20"> Move
height="20"> LocationChanged
height="20"> HandleCreated
height="20"> Invalidated
height="20"> StyleChanged
height="20"> ChangeUICues
height="20"> Invalidated
height="20"> BindingContextChanged
height="20"> Load
height="20"> Layout
height="20"> VisibleChanged
height="20"> Activated
height="20"> Shown
height="20"> Paint
height="20"> Paint
height="20"> Paint
height="20"> MouseCaptureChanged
height="20"> Closing
height="20"> FormClosing
height="20"> Closed
height="20"> FormClosed
height="20"> Deactivate
height="20"> HandleDestroyed
height="20"> Disposed
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 :)