• 通过Attached Property给控件绑定Command(二)


      上一篇我们提到希望建立一个通用的Command绑定,本篇就这个问题来和各位进行讨论。也希望各位能指出不足,提出改进的建议。

      我希望最终实现的效果如下图所示,可以给任何一个Control绑定Command,通过提供EventName来区分不同的事件,同时由Parameter来绑定需要传递的参数。

      同样,这里的local指的是CommandBinder类所在的命名空间。

    <Rectangle Width="100" Height="100" Fill="Red" 
      local:CommandBinder.Command="{Binding ViewModel.MouseLeftDownCommand, ElementName=window}"
      local:CommandBinder.EventName="MouseLeftButtonDown"
      local:CommandBinder.Parameter="{Binding RelativeSource={RelativeSource Self}}">
    </Rectangle>

      很明显我们这里定义了3个附加属性。只是绑定的对象不同,EventName直接给了代表事件名的字符串,而Parameter没有绑定到ViewModel中的对象,而是通过ElementName绑定了我们希望传递的参数,这里是被点击的Rectangle。我们来看一下EventName和Parameter的定义。

            public static object GetParameter(DependencyObject obj)
            {
                return (object)obj.GetValue(ParameterProperty);
            }
    
            public static void SetParameter(DependencyObject obj, object value)
            {
                obj.SetValue(ParameterProperty, value);
            }
    
            // Using a DependencyProperty as the backing store for Parameter.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty ParameterProperty =
                DependencyProperty.RegisterAttached("Parameter", typeof(object), typeof(CommandBinder), new UIPropertyMetadata(null));
    
            public static string GetEventName(DependencyObject obj)
            {
                return (string)obj.GetValue(EventNameProperty);
            }
    
            public static void SetEventName(DependencyObject obj, string value)
            {
                obj.SetValue(EventNameProperty, value);
            }
    
            // Using a DependencyProperty as the backing store for EventName.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty EventNameProperty =
                DependencyProperty.RegisterAttached("EventName", typeof(string), typeof(CommandBinder), new UIPropertyMetadata(null));

      附加属性的代码段快捷键是键入propa,再按2次Tab键,各位一试便知。对EventName和Parameter我们并没有做过多的处理,仅仅是希望他们以附加属性的形式能在XAML出现,并提供绑定的能力。

      真正取得绑定数据并关联Command.Execute方法的,仍然是CommandProperty:

            public static ICommand GetCommand(DependencyObject obj)
            {
                return (ICommand)obj.GetValue(CommandProperty);
            }
    
            public static void SetCommand(DependencyObject obj, ICommand value)
            {
                obj.SetValue(CommandProperty, value);
            }
    
            // Using a DependencyProperty as the backing store for CommonCommandBinder.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty CommandProperty =
                DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(CommandBinder), new PropertyMetadata(ChangedCallback));
    
            private static void ChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                FrameworkElement element = d as FrameworkElement;
                if (element != null)
                {
                    string eventName = element.GetValue(EventNameProperty) as string;
                    if (eventName != null)
                    {
                        EventInfo eventInfo = element.GetType().GetEvent(eventName);
                        var handler = new MouseButtonEventHandler((sender, arg) => 
                        {
                            object obj = element.GetValue(ParameterProperty);
                            (e.NewValue as ICommand).Execute(obj);
                        });
                        var del = handler.GetInvocationList()[0];
    
                        eventInfo.AddEventHandler(element, del);
                    }
                }
            }

      在这里我们可以看到我们通过DependencyObject的GetValue方法,取到了绑定的EventName和Parameter。然后通过反射将EventInfo和具体的MouseButtonEventHandler关联起来。动态的通过字符串将具体的事件注册。

      但是这里有一个问题,针对事件不同的委托类型,比如这里的MouseButtonEventHandler,我没有办法创建一个通用的EventHandler。这就造成了针对不同的事件,我仍需要在这里写一个大大的switch case。这是这个本篇比较大的一个败笔。我暂时也没有更好的办法。在此也希望各位能给出好的建议。

      看到这里有人会说,WPF是有behavior的,即使是Silverlight也是可以用Blend来绑定Command的,不需要自己费力气去写这种不怎么好用的附加属性。确实是如此。本篇的起由还是对依赖属性的学习。在微软的Win8应用开发马拉松上,我曾苦恼过WinRT没有behavior如何绑定。这是一位胖胖的微软哥挺身而出以神一般的姿态出现,丢给我一个他写的CommandBinder,把依赖属性和数据绑定以全新的姿态展示在我面前,令我茅厕顿开啊!于是我就试图用WPF来重现该神奇的绑定。

      但是我却没有办法像Win8里一样实现如下图XAML的绑定,各位有兴趣可以在WPF试一试,或许可以使你对数据绑定有新的认识。

    <Rectangle Width="100" Height="100" Fill="Red"  >
      <local:Common.CommonCommand>
         <local:CommonCommand 
               Command="{Binding Path=MouseLeftDownCommand}"
               EventName="MouseLeftButtonDown"
                  Parameter="{Binding Path=Title}">
        </
    local:CommonCommand>
      </local:Common.CommonCommand>
    </Rectangle>

      看似相似的绑定却始终无法在WPF中取到Parameter和Command的值。这里主要是期望把联系并不紧密的三个依赖属性EventNameProperty、ParameterProperty和CmmandProperty放置到一个叫做CommonCommand的类中,提高可读性和易用性。

      在这里CommonCommandProperty是作为附加属性出现的,所以他可以写成Rectangle的属性,而该属性是CommonCommand类型。EventNameProperty、ParameterProperty和CmmandProperty作为依赖属性存在于CommonCommand类中,而该类继承自DependencyObject。所以才能以上面的语法形式存在于XAML中。

      这里说了我没有成功获取到值,那么在通过Attached Property给控件绑定Command(三)中,我会提供一个替代的解决方案,敬请期待。

      同时也希望各位能对本篇提出意见和建议。

      这里给出本文相关的代码:

      3个附加属性的绑定形式:https://files.cnblogs.com/manupstairs/TestDPWpf.7z

      CommonCommand:https://files.cnblogs.com/manupstairs/TestDPWithParameter.zip

  • 相关阅读:
    spring-mvc访问本地html文件
    好文收集
    jsp参数乱码解决
    ext window嵌jsp页面自适应
    正则学习(转)
    Error occurred during initialization of VM Incompatible initial and maximum heap sizes specified
    产品测试流程
    创建maven工程时报错,解决方案
    接口测试中如何利用cookies保持会话
    http协议基础
  • 原文地址:https://www.cnblogs.com/manupstairs/p/2763439.html
Copyright © 2020-2023  润新知