Dynamic Event Subscription and 1 handler

2019-05-10 21:24发布

I have seen several answers already but somehow I can't get mine to work. I want to dynamically use any of the events of various controls (textbox, checkbox, button, etc) and, preferably, assign them to one event handler. The handler should be assigned at runtime. Furthermore, I want to know in the handler which event triggered the handler.

I got this to work partially. Using a lambda expression I call my handler (EventAssistant) and pass an extra parameter (command) which contains the name of the event. It works for events that use type EventHandler. However, it won't work for events that expect a different handler such as type MouseEventHandler. It will fail to subscribe at AddEventHandler.

private void RegisterEventHandlers(Control ctl)
{
  foreach (Command command in CommandList)
  {
    EventInfo eventInfo = ctl.GetType().GetEvent(command.Name);
    EventHandler handler = (sender, args) =>
    {
      EventAssistant(sender, args, command);
    };
    eventInfo.AddEventHandler(ctl, handler);
  }
}

public void EventAssistant(object sender, EventArgs e, Command c)
{
  //do lots of other fun stuff
}

Based on C# passing extra parameters to an event handler?


As an alternative I tried to solve the problem with an Expression Tree as shown here: Why am I getting an Argument exception when creating event handler dynamically? Apparently, the EventHandlerType can be retrieved from the EventInfo and used in a lambda expression.

But, whatever I do I always get an InvalidOperationException "Lambda Parameter not in scope".

private void RegisterEventHandlers(Control ctl)
{
  foreach (Command command in CommandList)
  {
    EventInfo eventInfo = ctl.GetType().GetEvent(command.Name);

    var sender = Expression.Parameter(typeof(object), "sender");
    var e = Expression.Parameter(typeof(EventArgs), "e");
    var c = Expression.Parameter(typeof(Command), "command");
    Expression[] arg = new Expression[] { sender, e, c };
    MethodInfo mi = this.GetType().GetMethod("EventAssistant");
    var body = Expression.Call(Expression.Constant(this), mi, arg);
    var lambda = Expression.Lambda(eventInfo.EventHandlerType, body, sender, e);

    eventInfo.AddEventHandler(ctl, lambda.Compile());
  }
}

What am I doing wrong with the Expression Tree?

Also, the first piece of code looks a lot more clean. Is it possible to get what I want using the first code sample?

1条回答
forever°为你锁心
2楼-- · 2019-05-10 22:02

In your second attempt, the variable c shouldn't be a ParameterExpression, but a ConstantExpression with the value set to the current command instead. With the current code, you are creating a handler, which essentially looks like this:

(_sender, _e) => this.EventAssistant(_sender, _e, _c)
// The expression expects "_c" to be a parameter of the lambda, which is why
// you're getting that exception

However, if you change

var c = Expression.Parameter(typeof(Command), "command");

to

var c = Expression.Constant(command);

the generated code will look (and work, of course) as expected:

(_sender, _e) => this.EventAssistant(_sender, _e, command)
查看更多
登录 后发表回答