[英]Add event handler using reflection
我有一个带有所谓命令的.json
文件:
"Commands":[{
"EventName": "MouseLeftButtonUp",
"MethodToExecute": "NextJson",
"Args": "Next.json"
},{
"EventName": "MouseRightButtonUp",
"MethodToExecute": "CloseApp"
}
我将此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
是UIElement
类事件的名称。 MethodToExecute
是事件触发时要调用的方法的名称。 Args
是传递给MethodToExecute
的参数。
我不希望我的用户能够调用应用程序中的任何方法,所以我不使用反射来获取MethodInfo
,而是创建Dictionary
: Dictionary<string, Delegate> MethodsDictionary
。 这本字典中的key
是方法的名称(来自Command
类的MethodToExecute
),其值是这样的:
MethodsDictionary.Add(nameof(CloseApp), new Action(CloseApp));
MethodsDictionary.Add(nameof(NextJson), new Action<string>(NextJson));
在不使用反射的情况下,我添加了这样的事件处理程序:
button.MouseLeftButtonUp += (sender, args) => MethodsDictionary[command.MethodToExecute].DynamicInvoke(command.Args);
但我想对事件进行动态绑定。 好吧,当然我可以通过command.Name
属性上丑陋的switch-case
来实现,但我仍然想尝试使用反射的解决方案。 在我看来,解决方案应该是这样的:
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);
}
我搜索了几个非常相似的问题:
但是这些并不能帮助我以我想要的方式解决问题。 我尝试了上面的一些解决方案,但它们对我不起作用,因不同的例外而失败。
更新。
如上所述,我尝试通过switch-case
实现处理程序绑定。 它导致了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();
}
}
}
我不喜欢,添加新处理程序的那部分只是上一节的复制粘贴,但在这种情况下我没有看到其他解决方法。 我想在这种情况下使用反射,但我不知道是否可能。
如果你不能让反射工作,你可以使用另一个Dictionary
来存储支持的事件订阅者方法:
命令文件
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
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
}
}
IEnumerable<Command> commands = DeserializeJsonToCommands();
var eventMapper = new JsonEventMapper();
eventMapper.RegisterJsonCommands(commands, button);
您肯定希望根据您的特定场景调整细节。
不要忘记取消订阅事件(例如,通过定义另一个SupportedEventUnsubscriberMap
)。
我认为你想要做的是这样的:
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.