zoukankan      html  css  js  c++  java
  • 将WPF中控件的Event转换成Command执行(转)

    Wpf的DataBinding为我们将View与logic的分离提供了便利。特别是利用MVP模式,可以将大部分的UI交互逻辑通过绑定Command
    来转移到Presenter中来,使我们可以专注于操作业务数据。但是微软提供的控件中好像还有很多的控件根本没有Command属性,
    只有普通的Event。这样的话就没有办法利用绑定将逻辑转移到Presenter中。看到xaml.cs(每一个xaml文件的codebehind文件)文件
    还是有很多的事件处理代码感觉非常不爽,于是想了个办法来将Event的处理转换成执行Command,这样就可以在Presenter里来处理
    EventHandler的逻辑了。xaml.cs文件只有一个构造函数的感觉就是好,感觉视图和逻辑是真正的分离了。
    首先来看一下用我的这个方法在xaml中怎么写。
    <Button CommandBindingBehavior.BindingEventName="Click"
    CommandBindingBehavior.BindingCommand="{Binding CloseMainWindowCommand}" Content="EventToCommand"/>
    在Button中使用这两个AttachProperty,当Click事件触发的时候你所绑定的Command也会被执行,而你自己不用为Click事件写
    任何的处理程序。
    下面来看一下这两个AttachProperty具体是做了些什么事情。
    其实思路很简单:在BindingEventName附加属性的PropertyChanged事件中通过反射在使用该附加属性的DependencyObject中
    找到BindingEventName所指定Event,然后给它注册一个处理函数。Event处理函数的函数体是去执行BindingCommand属性所绑定
    Command。这样就能达到Event被执行的时候所绑定的Command也被执行,而从表面上看好像是将Event转换成了Command来执行。
    (看到这里你可能会觉得太简单了。其实不然,具体的实现过程还是没有那么的容易。)
    第一步:定义两个AttachProperty(BindingEventNameProperty和BindingCommandProperty);
    BindingEventNameProperty用来保存要转换成Command执行的Event名称;
    BindingCommandProperty用来绑定要执行的Command.
    public static class CommandBindingBehavior
    {
    public static DependencyProperty BindingEventNameProperty =
    DependencyProperty.RegisterAttached("BindingEventName", typeof(string),
    typeof(CommandBindingBehavior), new PropertyMetadata(null, OnBindingEventChanged));
    public static DependencyProperty BindingCommandProperty =
    DependencyProperty.RegisterAttached("BindingCommand", typeof(ICommand), typeof(CommandBindingBehavior), new PropertyMetadata(null));
    }
    第二步:写一个通用的事件处理方法。为什么说通用呢,因为他要被注册给所有类型的Event作为处理函数。
    private static void OnEventRaised<T>(object sender, T arg) where T : EventArgs
    {
    DependencyObject dependencyObject = sender as DependencyObject;
    if (dependencyObject != null)
    {
    ICommand command = GetBindingCommand(dependencyObject);
    if (command.CanExecute(null))
    {
    command.Execute(null);
    }
    }
    }
    据我不完全观察,UI控件的事件处理程序的签名几乎都是void EventHandlerMethodName(object sender, T arg) where T:EventArgs;
    为什么要定义这样一个函数呢,这个先不管,做完第三步就会明白。
    第三步:实现OnBindingEventChanged事件处理程序(最关键的步骤)
    private static void OnBindingEventChanged(DependencyObject sender, DependencyPropertyChangedEventArgs arg)
    {
    Type senderType = sender.GetType();
    EventInfo eventInfo = senderType.GetEvent(arg.NewValue.ToString());
    eventInfo.AddEventHandler(sender, GenerateDelegateForEventHandler(eventInfo, true));
    }
    方法说明:
    从sender中通过反射拿到BindingEventName所指定的Event,然后通过调用AddEventHandler为其添加一个处理程序。
    AddEventHandler的第二个参数是传递一个Delegate,这时就遇到了一个问题:不同的事件处理程序的签名不一样,
    如果写一个普通的方法(比如已经实现的OnEventRaised),即使定义为泛型,也不能成功的转换为特定的Delegate.
    貌似只能根据Event的类型动态的去获取其处理程序的签名,然后动态的创建一个方法出来才行。
    看看GenerateDelegateForEventHandler方法的实现:
    private static Delegate GenerateDelegateForEventHandler(EventInfo eventInfo)
    {
    Delegate result = null;
    MethodInfo methodInfo = eventInfo.EventHandlerType.GetMethod("Invoke");
    ParameterInfo[] parameters = methodInfo.GetParameters();
    if (parameters.Length == 2)
    {
    Type currentType = typeof(CommandBindingBehavior);
    Type argType = parameters[1].ParameterType;
    MethodInfo eventRaisedMethod =
    currentType.GetMethod("OnEventRaised", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(argType);
    result = Delegate.CreateDelegate(eventInfo.EventHandlerType, eventRaisedMethod);
    }
    return result;
    }
    方法说明:
    这个方法结束一个EventInfo对象,EventInfo对象包含了一个EventHandlerType的属性(即Event处理程序的delegate类型)。
    然后通过反射拿到delegate的Invoke方法。(每个delegate的Invoke方法的签名是和该delegate的签名一样的,因此我们可以通过
    这个方法的参数来判定该delegate的签名是怎样的)
    我们通过反射拿到Invoke方法的参数类型(第一个参数是object类型,第二个参数的类型才是有用的);
    Delegate类有一个CreateDelegate方法,他的原型如下:
    CreateDelegate(Type, MethodInfo);
    他通过接受一个Delegate的Type和一个MethodInfo描述的方法,可以创建一个Type所指定的Delegate对象.
    所以现在关键就是如何去生成这个MethodInfo对象了。
    我们之前创建的OnEventRaised泛型方法现在可以发挥作用了,我们通过反射可以拿到这个方法的MethodInfo。
    但是这个对象不是我们想要的,因为他的第二参数的类型不一定和目标Delegate的签名一致。
    MethodInfo的MakeGenericMethod可以帮助我们将OnEventRaised中的泛型参数替换成具体的参数,这样我们就可以用我们之前
    拿到的Delegate的Invoke方法的第二个参数的类型来生成一个有用的MethodInfo了。
    注:其实这里可能会有一个疑问:Delegate和MulticastDelegate类均没有Invoke这个方法,我们为什么能通过反射拿到一个
    Delegate类型的Invoke方法呢?其实这是编译器在作怪,编译器会为每个delegate对象创建Invoke,BeginInvoke和EndInvoke
    方法。

  • 相关阅读:
    Security and Cryptography in Python
    Security and Cryptography in Python
    Security and Cryptography in Python
    Security and Cryptography in Python
    Security and Cryptography in Python
    Security and Cryptography in Python
    Security and Cryptography in Python
    《EffectiveJava中文第二版》 高清PDF下载
    《MoreEffectiveC++中文版》 pdf 下载
    《啊哈c语言》 高清 PDF 下载
  • 原文地址:https://www.cnblogs.com/aoldman/p/2781034.html
Copyright © 2011-2022 走看看