C# 使用反射添加事件处理程序
我有一个带有所谓命令的C# 使用反射添加事件处理程序,c#,.net,wpf,events,reflection,C#,.net,Wpf,Events,Reflection,我有一个带有所谓命令的.json文件: "Commands":[{ "EventName": "MouseLeftButtonUp", "MethodToExecute": "NextJson", "Args": "Next.json" },{ "EventName": "MouseRightButtonUp", "MethodToExecute": "CloseApp" } 我将此json反序列化到此类: 公共类命令 { [JsonPropertyName(“E
.json
文件:
"Commands":[{
"EventName": "MouseLeftButtonUp",
"MethodToExecute": "NextJson",
"Args": "Next.json"
},{
"EventName": "MouseRightButtonUp",
"MethodToExecute": "CloseApp"
}
我将此json反序列化到此类:
公共类命令
{
[JsonPropertyName(“EventName”)]
公共字符串EventName{get;set;}
[JsonPropertyName(“MethodToExecute”)]
公共字符串方法执行{get;set;}
[JsonPropertyName(“Args”)]
公共字符串Args{get;set;}
/*方法*/
}
EventName
是UIElement
类事件的名称。
MethodToExecute
是事件触发时要调用的方法的名称。
Args
是传递给MethodToExecute
的Args
我不希望我的用户能够调用应用程序中的任何方法,因此我不使用反射来获取MethodInfo
,而是创建Dictionary
:Dictionary methods Dictionary
。本词典中的键是方法的名称(MethodToExecute
fromCommand
class),其值如下:
MethodsDictionary.Add(名称(CloseApp)、新操作(CloseApp));
Add(nameof(NextJson),newaction(NextJson));
在不使用反射的情况下,我添加了如下事件处理程序:
button.MouseLeftButtonUp+=(发送方,args)=>MethodsDictionary[command.MethodToExecute].DynamicInvoke(command.args);
但我想对事件进行动态绑定。当然,我可以在command.Name
属性上通过难看的开关盒
,但我仍然想尝试反射解决方案。
在我看来,解决方案应该是这样的:
foreach(命令中的var命令)
{
Bind(uielement,MethodsDictionary[command.MethodToExecute]);
}
//和command.Bind方法类似:
公共void绑定(UIElement UIElement,委托方法执行)
{
//我知道没有像GetEventHandler这样的方法,只是一个例子
var handler=uielement.GetEventHandler(EventName);
handler+=(发送方,args)=>methodToExecute.DynamicInvoke(args);
}
我搜索了几个非常相似的问题:
但这些并不能帮助我以我想要的方式解决问题。我尝试了上面的一些解决方案,但都不适合我,除了不同的例外,都失败了
UPD
如上所述,我试图通过开关大小写实现处理程序绑定。它在命令
类中产生了此方法:
公共void绑定(UIElement)
{
开关(此.Name)
{
案例“MouseRightButtonUp”:
{
element.MouseRightButtonUp+=(发件人,args)=>MethodsDictionary[this.MethodToExecute].DynamicInvoke(this.args);
打破
}
案例“点击”:
{
//UIElement没有单击事件
var button=作为ButtonBase的元素;
按钮。单击+=(发件人,参数)=>MethodsDictionary[this.MethodToExecute].DynamicInvoke(this.args);
打破
}
/*等等*/
违约:
{
抛出新的NotSupportedException();
}
}
}
我不喜欢,添加新处理程序的部分只是上一节的复制粘贴,但在这种情况下,我看不到其他解决方法。我想在这种情况下使用反射,但我不知道这是否可行。如果反射无法正常工作,可以使用另一个字典
来存储支持的事件订阅方方法:
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*/
}
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
}
}
JsonEventMapper.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*/
}
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
}
}
JsonEventMapper类
{
私有字典>支持的EventSubscriberMap{get;}
专用字典注册表deventhandlermap{get;}
公共JsonEventMapper()
{
this.SupportedEventSubscriberMap=新字典()
{
{
nameof(UIElement.MouseRightButtonUp),(UIElement,handler)=>UIElement.MouseRightButtonUp+=handler.Invoke
},
{
nameof(UIElement.LostFocus),(UIElement,eventSubscriber)=>UIElement.LostFocus+=handler.Invoke
}
};
this.RegisteredEventHandlerMap=新字典()
{
{
CloseApp的名称(UIElement.mouseleftbuttonnup)
},
{
nameof(UIElement.LostFocus),NextJson
}
};
}
公共无效注册表JsonCommand(IEnumerable命令,UIElement UIElement)
{
foreach(命令中的var命令)
{
BindCommandToEvent(uiElement,command);
}
}
私有void BindCommandToEvent(UIElement UIElement,Command)
{
if(this.SupportedEventSubscriberMap.TryGetValue(command.EventName,out Action eventSubscriber)
&&this.RegisteredEventHandlerMap.TryGetValue(command.EventName,out EventHandler EventHandler))
{
Invoke(uiElement,eventHandler);
}
}
私有void CloseApp(对象发送方、事件args args)
{
//处理事件
}
私有void NextJson(对象发送方、事件args args)
{
//处理事件
}
}
用法
IEnumerable commands=反序列化JSONTOCommands();
var eventMapper=new JsonEventMapper();
RegisterJsonCommands(命令、按钮);
您肯定想根据您的特定场景调整细节
不要忘记取消订阅活动(例如,通过定义另一个supportedEventSubscriberMap
)。我认为您尝试的是这样的:
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);
}
}
谢谢你详细的回答!正如我在OP中提到的,我试图通过开关案例
实现处理程序绑定。它在命令
类(更新的OP)中产生了这个方法Bind
。我不喜欢,添加新处理程序的部分只是上一节的复制粘贴,但在这种情况下,我看不到其他解决方法。我还没有尝试你的解决方案,但是