Matthew Layton
Matthew Layton

Reputation: 42260

Binding an event handler to any type of event using reflection

I have some code where I need to dynamically bind events to an event handler:

foreach (string evnt in attribute.Events)
{
    EventInfo ei = control.GetType().GetEvent(evnt);
    if(ei != null)
    {
        ei.AddEventHandler(control, new EventHandler((s, e) =>
        {
            // More awesomeness here...
        }));
    }
}

So for each string in the event list, get the event from the control and bind a handler.

The problem is that not all events are EventHandler, some for example might be KeyEventHander or MouseEventHandler etc.

I don't want a massive if/else list of EventHandler derived types, I just want to bind the same handler regardless of what type of EventHandler it is.

How can I do this?

Upvotes: 3

Views: 1118

Answers (2)

Radin Gospodinov
Radin Gospodinov

Reputation: 2323

You can use dynamic key word to attach the handler. More explanation you can find here: https://msdn.microsoft.com/en-us/library/ms228976(v=vs.110).aspx

Upvotes: 0

Yacoub Massad
Yacoub Massad

Reputation: 27861

Here is one way to do it:

First create this helper class:

public class HandlerHelper<T> where T : EventArgs
{
    private readonly EventHandler m_HandlerToCall;

    public HandlerHelper(EventHandler handler_to_call)
    {
        m_HandlerToCall = handler_to_call;
    }

    public void Handle(object sender, T args)
    {
        m_HandlerToCall.Invoke(sender, args);
    }
}

This generic class has a Handle method that will be used to be our delegate for the many event handler types. Note that this class is generic. T will be one of the many EventArgs derived classes for different event types.

Now let's say you define the following event handler:

var event_handler = new EventHandler((s, args) =>
{
    // More awesomeness here...
});

Here is how you can use the helper class to create different delegates for the different event handler types that will invoke event_handler:

foreach (var event_name in event_names)
{
    var event_info = control.GetType().GetEvent(event_name);

    var event_handler_type = event_info.EventHandlerType;

    var event_args_type = event_handler_type.GetMethod("Invoke").GetParameters()[1].ParameterType;

    var helper_type = typeof(HandlerHelper<>).MakeGenericType(event_args_type);

    var helper = Activator.CreateInstance(helper_type, event_handler);

    Delegate my_delegate = Delegate.CreateDelegate(event_handler_type, helper, "Handle");

    event_info.AddEventHandler(button, my_delegate);
}

For each event, we obtain the EventHandlerType which is like EventHandler or MouseEventHandler.

Then we use reflection to get the type of the second parameter which is like EventArgs or MouseEventArgs.

Then we create an instance of HandlerHelper<> based on the type of the EventArgs parameter. For example HandlerHelper<EventArgs> or HandlerHelper<MouseEventArgs>.

We give event_handler to the constructor of HandlerHelper so that it can call it when it's Handle method is invoked.

This makes the signature of the Handle method as we want. For example, in the case of HandlerHelper<MouseEventArgs>, the signature of the Handle method is:

void Handle(object sender, MouseEventArgs args)

Now, we use Delegate.CreateDelegate to create a delegate based on the Handle method for the specific HandlerHelper object that we created, and we give such delegate to the AddEventHandler method.

For performance reasons, you can cache the delegate (instead of creating one every time) based on event_args_type.

Upvotes: 3

Related Questions