简体   繁体   English

使用反射添加事件处理程序

[英]Add event handler using reflection

I have a .json file with so-called commands:我有一个带有所谓命令的.json文件:

"Commands":[{
   "EventName": "MouseLeftButtonUp",
   "MethodToExecute": "NextJson",
   "Args": "Next.json"
},{
   "EventName": "MouseRightButtonUp",
   "MethodToExecute": "CloseApp"
}

I deserialize this json to this class:我将此json反序列化为此类:

    public class Command
    {
        [JsonPropertyName("EventName")]
        public string EventName { get; set; }

        [JsonPropertyName("MethodToExecute")]
        public string MethodToExecute { get; set; }

        [JsonPropertyName("Args")]
        public string Args { get; set; }

        /*Methods*/
    }

EventName is a name of UIElement class events. EventNameUIElement类事件的名称。 MethodToExecute is a name of method to call, when event triggered. MethodToExecute是事件触发时要调用的方法的名称。 Args are the args, passed to the MethodToExecute . Args是传递给MethodToExecute的参数。

I don't want my users to be able to call any method in the application, so I don't use reflection to get MethodInfo , instead I create Dictionary : Dictionary<string, Delegate> MethodsDictionary .我不希望我的用户能够调用应用程序中的任何方法,所以我不使用反射来获取MethodInfo ,而是创建Dictionary : Dictionary<string, Delegate> MethodsDictionary The key in this dictionary is the name of method ( MethodToExecute from Command class), and the value is something like this:这本字典中的key是方法的名称(来自Command类的MethodToExecute ),其值是这样的:

MethodsDictionary.Add(nameof(CloseApp), new Action(CloseApp));
MethodsDictionary.Add(nameof(NextJson), new Action<string>(NextJson));

Without using reflection, I'd added the event handler like this:在不使用反射的情况下,我添加了这样的事件处理程序:

button.MouseLeftButtonUp += (sender, args) => MethodsDictionary[command.MethodToExecute].DynamicInvoke(command.Args);

But I'd like to make a dynamic binding of events.但我想对事件进行动态绑定。 Well, of course I can make it through ugly switch-case on command.Name property, but I still would like to try the solution with reflection.好吧,当然我可以通过command.Name属性上丑陋的switch-case来实现,但我仍然想尝试使用反射的解决方案。 The solutuion, as I see it, should looke something like:在我看来,解决方案应该是这样的:

foreach (var command in commands)
{
    command.Bind(uielement, MethodsDictionary[command.MethodToExecute]);
}

//And command.Bind method is like:

public void Bind(UIElement uielement, Delegate methodToExecute)
{
    //I know there's no such method like GetEventHandler, just an example
    var handler = uielement.GetEventHandler(EventName);
    handler += (sender, args) => methodToExecute.DynamicInvoke(Args);
}

I searched through several pretty similar questions:我搜索了几个非常相似的问题:

Subscribe to an event with Reflection 使用反射订阅事件

https://social.msdn.microsoft.com/Forums/vstudio/en-US/d7f184f1-0964-412a-8659-6759a0e2db83/c-reflection-problem-subscribing-to-event?forum=netfxbcl https://social.msdn.microsoft.com/Forums/vstudio/en-US/d7f184f1-0964-412a-8659-6759a0e2db83/c-reflection-problem-subscribing-to-event?forum=netfxbcl

https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/how-to-hook-up-a-delegate-using-reflection https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/how-to-hook-up-a-delegate-using-reflection

AddEventHandler using reflection 使用反射的 AddEventHandler

Add Event Handler using Reflection ? 使用反射添加事件处理程序? / Get object of type? / 获取类型的对象?

But these doesn't help me to solve the problem the way I want to.但是这些并不能帮助我以我想要的方式解决问题。 I tried some of the solutions above, but they didn't work out for me, failing with different exceptions.我尝试了上面的一些解决方案,但它们对我不起作用,因不同的例外而失败。

UPD.更新。

I tried to implement handler binding through switch-case , as I mentioned above.如上所述,我尝试通过switch-case实现处理程序绑定。 It resulted in this method inside Command class:它导致了Command类中的这个方法:

public void Bind(UIElement element)
{
    switch (this.Name)
    {
        case "MouseRightButtonUp":
        {
            element.MouseRightButtonUp += (sender, args) => MethodsDictionary[this.MethodToExecute].DynamicInvoke(this.Args);
            break;
        }
        case "Click":
        {
            //UIElement doesn't have Click event
            var button = element as ButtonBase;
            button.Click += (sender, args) => MethodsDictionary[this.MethodToExecute].DynamicInvoke(this.Args);
            break;
        }
        /*And so on for each event*/
        default:
        {
            throw new NotSupportedException();
        }
    }
}

I don't like, how that part with adding new handlers is just a copy-paste of previous section, but I don't see another workaround in this situation.我不喜欢,添加新处理程序的那部分只是上一节的复制粘贴,但在这种情况下我没有看到其他解决方法。 I'd like to use the reflection in this case, but I don't know if it's possible.我想在这种情况下使用反射,但我不知道是否可能。

If you can't get reflection to work, you can use another Dictionary to store supported event subscriber methods:如果你不能让反射工作,你可以使用另一个Dictionary来存储支持的事件订阅者方法:

Command.cs命令文件

public class Command
{
    [JsonPropertyName("EventName")]
    public string EventName { get; set; }

    [JsonPropertyName("MethodToExecute")]
    public string MethodToExecute { get; set; }

    [JsonPropertyName("Args")]
    public string Args { get; set; }

    /*Methods*/
}

JsonEventMapper.cs JsonEventMapper.cs

class JsonEventMapper
{
  private Dictionary<string, Action<UIElement, EventHandler>>> SupportedEventSubscriberMap { get; }
  private Dictionary<string, EventHandler> RegisteredEventHandlerMap { get; }   

  public JsonEventMapper()
  {
    this.SupportedEventSubscriberMap = new Dictionary<string, Action<UIElement, EventHandler>>() 
    { 
      { 
        nameof(UIElement.MouseRightButtonUp), (uiElement, handler) => uiElement.MouseRightButtonUp += handler.Invoke 
      },
      { 
        nameof(UIElement.LostFocus), (uiElement, eventSubscriber) => uiElement.LostFocus += handler.Invoke 
      }
    };

    this.RegisteredEventHandlerMap = new Dictionary<string, EventHandler>()
    {
      {
        nameof(UIElement.MouseLeftButtonUp), CloseApp
      },
      {
        nameof(UIElement.LostFocus), NextJson
      }
    };
  }

  public void RegisterJsonCommands(IEnumerable<Command> commands, UIElement uiElement)
  {    
    foreach (var command in commands)
    {
      BindCommandToEvent(uiElement, command);
    }
  }

  private void BindCommandToEvent(UIElement uiElement, Command command)
  {    
    if (this.SupportedEventSubscriberMap.TryGetValue(command.EventName, out Action<UIElement, EventHandler> eventSubscriber)
      && this.RegisteredEventHandlerMap.TryGetValue(command.EventName, out EventHandler eventHandler))
    {
      eventSubscriber.Invoke(uiElement, eventHandler);
    }
  }

  private void CloseApp(object sender, EventArgs args)
  {    
    // Handle event
  }

  private void NextJson(object sender, EventArgs args)
  {    
    // Handle event
  }
}

Usage用法

IEnumerable<Command> commands = DeserializeJsonToCommands();
var eventMapper = new JsonEventMapper();
eventMapper.RegisterJsonCommands(commands, button);

You surely want to adjust details to your specific scenario.您肯定希望根据您的特定场景调整细节。

Don't forget to unsubscribe from the events (eg, by defining another SupportedEventUnsubscriberMap ).不要忘记取消订阅事件(例如,通过定义另一个SupportedEventUnsubscriberMap )。

I think what you're trying to do is something like that:我认为你想要做的是这样的:

public class CommandModel
{
    /// properties

    public void Bind(UIElement element)
    {
        EventBinder.Bind(() => MainViewModel.RunCommand(MethodToExecute, Args), element, EventName);
    }
}

public static class EventBinder
{
    public static void Bind(Action action, object eventSource, string eventName)
    {
        var eventInfo = eventSource.GetType().GetEvent(eventName);

        EventHandler eventHandler = (s, e) => action();

        eventInfo.AddEventHandler(eventSource, ConvertDelegate(eventHandler, eventInfo.EventHandlerType));
    }

    private static Delegate ConvertDelegate(Delegate originalDelegate, Type targetDelegateType)
    {
        return Delegate.CreateDelegate(
            targetDelegateType,
            originalDelegate.Target,
            originalDelegate.Method);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用反射将事件处理程序附加到事件? - How to attach event handler to an event using reflection? 将处理程序添加到具有反射的任何事件中,动态处理 - Add handler to any event with reflection, handle dynamically 如何使用Reflection动态设置事件处理程序 - How to set an event handler dynamically using Reflection 使用反射调用匿名事件处理程序? - Invoke anonymous event handler using reflection? 当无法访问EventArgs类型时,如何使用反射添加事件处理程序? - How to add an event handler using reflection when do not have access to EventArgs type? 使用反射将事件处理程序绑定到任何类型的事件 - Binding an event handler to any type of event using reflection 通过反射C#添加和删除事件处理程序 - Add and remove event handler via reflection c# 使用 System.Reflection 将参数传递给事件处理程序 - Passing parameter to event handler using System.Reflection 尝试使用反射为 dll 操作添加事件处理程序时,“类型为‘System.EventHandler’的对象无法转换为类型‘System.Action’” - "Object of type 'System.EventHandler' cannot be converted to type 'System.Action'" when trying to add event handler for dll action using reflection 为什么我不能使用反射来获取LinkBut​​ton事件的事件处理程序? - Why can't I get the event handler of a LinkButton's event using reflection?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM