本文说明怎样把 DoubleClick 连接至 ICommand。方法很多。推荐使用 Attach Property 方式,因为它能把任何 RoutedEvent 接上任何 ICommand。

之前写过一篇博文关于 MVVM 中双击事件触发 ICommand 的办法,我说要么你自己写 Attached Property,要么下载别人写好的,比如支持 Collections 的 CommandBehaviors。我认为这两个办法是比较好的。有网友说我没有解释清楚,因为我觉得 Attached Property 有点离题,跟 MVVM 关系不太大。反正有得用就行了。

下面以 ListView 为例。

1. InputBindings

先不说 Attached Property,看看有什么办法可以把双击绑定到 ICommand。最简单的办法是 InputBindings。

XAML:

<ListView.InputBindings><MouseBinding Gesture="LeftDoubleClick" Command=""/></ListView.InputBindings>

支持 KeyBinding (键盘),和 MouseBinding (鼠标)。能做到,如果只需要管键盘或鼠标,这是比较简单。

2. 隐形 Button (不建议)

我见过第二个办法,隐形 Button, (Visibility=”Collapsed”),ICommand 绑定进去,ListView MouseDoubleClick 在视图建立句柄,由它再触发 Button 的 Command.Execute(object)。

XAML:

<Button Name="button1" Visibility="Collapsed" Command=""/><ListView  MouseDoubleClick="ListView_MouseDoubleClick"/>

Code:

privatevoid ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e) {button1.Command.Execute(null);
}

这比较傻,不建议。

3. Attached Property

MSDN 有介绍怎样为控件添加新的属性,这里不详细说了。关键是静态方法 Set,和静态 DependencyProperty。(MSDN 说 GET SET 都要,但其实写 XAML 时只用到 SET,后续启动后,你需要拿回属性值才需要 GET)。

先看一下,Attached Property 是怎样写的,热热身:

CODE:

publicstaticclass MyProperty {publicstaticreadonly DependencyProperty ParameterProperty = DependencyProperty.RegisterAttached("Parameter",typeof(Object),typeof(MyProperty),new FrameworkPropertyMetadata(null));publicstatic Object GetParameter(UIElement obj) {return obj.GetValue(ParameterProperty);}publicstaticvoid SetParameter(UIElement obj, Object value) {obj.SetValue(ParameterProperty, value);}
}

get、set 参数 UIElement 类型是为了确保所有控件能用它。这 Parameter 没有配置CallBack,这个MyProperty不对值变化做什么动作,也不设置默认值,所以 RegisterAttached 时候 FrameworkPropertyMetadata是 null。

命名规范必须跟从,MSDN 有说明。当你希望在 XAML 这属性叫做 Parameter 的时候(RegisterAttached 的第一个参数),它的get、set 方法必须命名为 GetParameter 和 SetParameter。编译后 XAML 可用。

XAML:

<Window x:Class="WpfApplication1.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:y="clr-namespace:WpfApplication1"Title="MainWindow" Height="350" Width="525"><Grid><ListView y:MyProperty.Parameter="ABC"/></Grid></Window>

新手记得加上正确的 XML namespace,xmlns:y="clr-namespace:WpfApplication1" 是因为我把MyProperty类放在这 WpfApplication1 项目的最外层。

知道了怎么写 Attached Property 之后,入正题,加入 ICommand。为灵活性,做法是让程序员配置要绑的 RoutedEvent ,和对应要触发的 ICommand 同时作为 DependencyProperty,让程序员自己配置哪个Event 接哪个 ICommand。(注:handler 那 Dictionary 的做法,和 Detach Attach 是参考某大神的)。为缩短代码,只写 ICommand 和 Event,没写 ICommand 的命令参数。

(以下代码网上其实很多,也有很多版本,大同小异)

CODE:

using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;namespace WpfApplication1 {publicstaticclass CommandBehavior {// UI,Handler Listprivatestatic Dictionary<UIElement, RoutedEventHandler> handlers =new Dictionary<UIElement, RoutedEventHandler>();#region Command Propertypublicstaticreadonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command",typeof(ICommand),typeof(CommandBehavior),new FrameworkPropertyMetadata() {DefaultValue =null,PropertyChangedCallback =new PropertyChangedCallback(OnCommandPropertyChanged)});publicstatic ICommand GetCommand(UIElement obj) {return (ICommand)obj.GetValue(CommandProperty);}publicstaticvoid SetCommand(UIElement obj, ICommand value) {obj.SetValue(CommandProperty, value);}#endregion#region Event Propertypublicstaticreadonly DependencyProperty EventProperty = DependencyProperty.RegisterAttached("Event",typeof(RoutedEvent),typeof(CommandBehavior),new FrameworkPropertyMetadata() {DefaultValue =null,PropertyChangedCallback =new PropertyChangedCallback(OnEventPropertyChanged)});publicstatic RoutedEvent GetEvent(DependencyObject obj) {return (RoutedEvent)obj.GetValue(EventProperty);}publicstaticvoid SetEvent(DependencyObject obj, RoutedEvent value) {obj.SetValue(EventProperty, value);}#endregion#region CallBacksprivatestaticvoid OnCommandPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) {UIElement element = obj as UIElement;ICommand oldCommand = args.OldValue as ICommand;ICommand newCommand = args.NewValue as ICommand;RoutedEvent routedEvent = element.GetValue(EventProperty) as RoutedEvent;Detach(element, routedEvent, oldCommand);Attach(element, routedEvent, newCommand);}privatestaticvoid OnEventPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) {UIElement element = obj as UIElement;RoutedEvent oldEvent = args.OldValue as RoutedEvent;RoutedEvent newEvent = args.NewValue as RoutedEvent;ICommand command = element.GetValue(CommandProperty) as ICommand;Detach(element, oldEvent, command);Attach(element, newEvent, command);}#endregionprivatestaticvoid Attach(UIElement element, RoutedEvent Event, ICommand command) {if (Event !=null&& element !=null&& command !=null) {RoutedEventHandler InvokeCommandHandler =new RoutedEventHandler(delegate {command.Execute(null);});handlers.Add(element, InvokeCommandHandler);element.AddHandler(Event, InvokeCommandHandler);}}privatestaticvoid Detach(UIElement element, RoutedEvent Event, ICommand command) {if (Event !=null&& element !=null&& command !=null) {RoutedEventHandler handler = handlers[element];if (handler !=null) {element.RemoveHandler(Event, handler);handlers.Remove(element);}}}}
}

跟之前那个 Parameter 例子很像,只是同一个静态类,做了两个属性,一个叫做 Event,一个叫做 Command。另外,多了一个 Dictionary,还有,这次 Event 和 Command 的变化,都注册了 PropertyChangedCallback 的句柄。最下面的 Attach Detach 的 private 帮助方法,只是重构时从PropertyChangedCallBack 的句柄抽出来而已。

控件、事件、命令,三者是一起的组合,某 UIElement 的某 RoutedEvent 触发到某 ICommand 的 Execute。但RoutedEvent 触发的是 RoutedEventHandler 句柄,不是 ICommand。所以这个静态类所做最重要的事,见 private static void Attach(),就是创建新的 RoutedEventHandler,让它执行委托运行 command 的 Execute,然后把准备好 RoutedEventHandler 之后粘上 UIElement,即 AddHandler(RoutedEvent,RoutedEventHandler)。把这搭配,UIElement 和已做好ICommand委托的 RoutedEventHandler,放在 Dictionary,是为了 Detach 时候找回。

要做 Detach 是因为,DependencyProperty 的值是能变化的(上例中是 Event和Command这两个,都能在运行时变),不一定是写死在 XAML,比如 {Binding Path=XXX} 这情况。万一 Command 变了,或者 RoutedEvent 变了,上述做好了的搭配就失效,是需要 RemoveHandler 然后重新组合。所以,PropertyChangedCallBack 所做的,都是先 Detach 旧值(args.OldValue),然后再 Attach 粘上新值(args.NewValue)。不管 Event 变还是 Command 变,都需要如此。

这静态类的解释到此为止。不复杂。用法如下:

XAML:

<Window x:Class="WpfApplication1.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:y="clr-namespace:WpfApplication1"Title="MainWindow" Height="350" Width="525"><Grid><ListView y:CommandBehavior.Command="{Binding Path=TestCommand}"y:CommandBehavior.Event="ListView.MouseDoubleClick"></ListView></Grid></Window>

因为一开始设置了Command 和 Event 的默认值为 null (RegisterAttached 时候的 FrameworkPropertyMetadata 内,DefaultValue),所以 XAML 运行写入值时,值变化触发 CallBack,完成了我们需要的连接。

最后,改一下 CommandBehavior,让它能接受参数,传过去 ICommand。因为 ICommand 的命令参数类型是 object,所以写的 CommandParameter 类型也是 object。

完整版本 CODE:

using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;namespace WpfApplication1 {publicstaticclass CommandBehavior {// UI,Handler Listprivatestatic Dictionary<UIElement, RoutedEventHandler> handlers =new Dictionary<UIElement, RoutedEventHandler>();#region Command Propertypublicstaticreadonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command",typeof(ICommand),typeof(CommandBehavior),new FrameworkPropertyMetadata() {DefaultValue =null,PropertyChangedCallback =new PropertyChangedCallback(OnCommandPropertyChanged)});publicstatic ICommand GetCommand(UIElement obj) {return (ICommand)obj.GetValue(CommandProperty);}publicstaticvoid SetCommand(UIElement obj, ICommand value) {obj.SetValue(CommandProperty, value);}#endregion#region Event Propertypublicstaticreadonly DependencyProperty EventProperty = DependencyProperty.RegisterAttached("Event",typeof(RoutedEvent),typeof(CommandBehavior),new FrameworkPropertyMetadata() {DefaultValue =null,PropertyChangedCallback =new PropertyChangedCallback(OnEventPropertyChanged)});publicstatic RoutedEvent GetEvent(DependencyObject obj) {return (RoutedEvent)obj.GetValue(EventProperty);}publicstaticvoid SetEvent(DependencyObject obj, RoutedEvent value) {obj.SetValue(EventProperty, value);}#endregion#region CommandParameter Propertypublicstaticreadonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached("CommandParameter",typeof(object),typeof(CommandBehavior),new FrameworkPropertyMetadata(null));publicstaticobject GetCommandParameter(UIElement obj) {return obj.GetValue(CommandParameterProperty);}publicstaticvoid SetCommandParameter(UIElement obj, object value) {obj.SetValue(CommandParameterProperty, value);}#endregion#region CallBacksprivatestaticvoid OnCommandPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) {UIElement element = obj as UIElement;ICommand oldCommand = args.OldValue as ICommand;ICommand newCommand = args.NewValue as ICommand;RoutedEvent routedEvent = element.GetValue(EventProperty) as RoutedEvent;object commandParameter = element.GetValue(CommandParameterProperty);Detach(element, routedEvent, oldCommand);Attach(element, routedEvent, newCommand, commandParameter);}privatestaticvoid OnEventPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) {UIElement element = obj as UIElement;RoutedEvent oldEvent = args.OldValue as RoutedEvent;RoutedEvent newEvent = args.NewValue as RoutedEvent;ICommand command = element.GetValue(CommandProperty) as ICommand;object commandParameter = element.GetValue(CommandParameterProperty);Detach(element, oldEvent, command);Attach(element, newEvent, command, commandParameter);}#endregionprivatestaticvoid Attach(UIElement element, RoutedEvent Event, ICommand command, object commandParameter) {if (Event !=null&& element !=null&& command !=null) {RoutedEventHandler InvokeCommandHandler =new RoutedEventHandler(delegate {command.Execute(commandParameter);});handlers.Add(element, InvokeCommandHandler);element.AddHandler(Event, InvokeCommandHandler);}}privatestaticvoid Detach(UIElement element, RoutedEvent Event, ICommand command) {if (Event !=null&& element !=null&& command !=null) {RoutedEventHandler handler = handlers[element];if (handler !=null) {element.RemoveHandler(Event, handler);handlers.Remove(element);}}}}
}

完整版本的 CommandBehavior 在 XAML 用法:

XAML:

<Window x:Class="WpfApplication1.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:y="clr-namespace:WpfApplication1"Title="MainWindow" Height="350" Width="525"><Grid><ListView y:CommandBehavior.Command="{Binding Path=TestCommand}"y:CommandBehavior.Event="ListView.MouseDoubleClick"y:CommandBehavior.CommandParameter="TestParameter"/></Grid></Window>

Attach Property 方法介绍到此为止。点击这里下载最终版本的代码。

这类简单,用来解释工作原理比较合适。但我之前博文没用这个类,因为以上代码,有一个明显缺陷。源于 Dictionary<UIElement, RoutedEventHandler> 这样的简单搭配,UIElement 作为 Key。而且 CommandBehavior 这静态类,没有集合暴露给 XAML。这意味着,一个控件,只能设置一次。比如,当一个控件你有两个 RoutedEvent 希望绑定到两个ICommand,这代码不支持。

为了解决这问题,网上已经有很多人写好了一个叫做 CommandBehaviorCollection 的类(懒到搜索都不想搜的,点击这里),很多不同的版本,功能其实都一样,让你在 XAML 内一个控件能同时配置多个 Event 和 Command 的组合。这个类就是我在之前博文上用到的那个。我不打算解释里面内容,其工作基本原理,与上述代码一摸一样,只是它暴露了集合让你在 XAML 内填多个组合。

我在这群里,欢迎加入交流:
开发板玩家群 578649319
硬件创客 (10105555)

转载于:https://www.cnblogs.com/leptonation/p/3279190.html

C# WPF – 利用“Attached Property” 把 RoutedEvent 接上 ICommand相关推荐

  1. WPF中的Attached Property

    Attached Property是Dependency Property的一种特殊形式,它是用DependencyProperty.RegisterAttached方法注册的,可以被有效地添加到任何 ...

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

    上一篇我们提到希望建立一个通用的Command绑定,本篇就这个问题来和各位进行讨论.也希望各位能指出不足,提出改进的建议. 我希望最终实现的效果如下图所示,可以给任何一个Control绑定Comman ...

  3. 照书学WPF之 Dependency Property 1

    简介 Dependency Property: 它们依赖一些其他的property和外在的影响,引起自身的变化. 如: WPF框架的编程经常和界面打交道,经常遇到的一个情况是某个属性的值的变化会影响到 ...

  4. WPF利用动画实现圆形进度条

    WPF利用动画实现圆形进度条 原文:WPF利用动画实现圆形进度条 这是我的第一篇随笔,最近因为工作需要,开始学习WPF相关技术,自己想实现以下圆形进度条的效果,逛了园子发现基本都是很久以前的文章,实现 ...

  5. UWP开发入门(十一)——Attached Property的简单应用

    UWP中的Attached Property即附加属性,在实际开发中是很常见的,比如Grid.Row: <Grid Background="{ThemeResource Applica ...

  6. WPF 利用键盘钩子来捕获键盘,做一些不为人知的事情...完整实例

    原文:WPF 利用键盘钩子来捕获键盘,做一些不为人知的事情...完整实例 键盘钩子是一种可以监控键盘操作的指令. 看到这句话是不是觉得其实键盘钩子可以做很多事情. 场景 当你的程序需要一个全局的快捷键 ...

  7. php 输出图片给js,如何在php中利用croppic.js对图片进行剪切并上传

    如何在php中利用croppic.js对图片进行剪切并上传 发布时间:2021-01-30 16:41:02 来源:亿速云 阅读:92 作者:Leah 这篇文章将为大家详细讲解有关如何在php中利用c ...

  8. DL之DCGNN:基于TF利用DCGAN实现在MNIST数据集上训练生成新样本

    DL之DCGNN:基于TF利用DCGAN实现在MNIST数据集上训练生成新样本 目录 输出结果 设计思路 实现部分代码 说明:所有图片文件丢失 输出结果 更新-- 设计思路 更新-- 实现部分代码 更 ...

  9. c# 联合halcon 基于相关性 模板匹配_宣城seo公司_有效利用及时相关性解决关键词排名上不到首页的难题...

    发布时间:2020-11-04 09:11:33 当你写一条热门新闻时,百度总是能很快地收录和显示你.这就是即时新闻的魅力所在,考虑到全站的权重不算太多. 它完全基于即时新闻和页面相关性,因此,如果你 ...

  10. 利用FormData对象实现AJAX文件上传功能及后端实现

    包括HTML基础设置.CSS界面优化.JS利用FormData对象和AJAX进行上传.后端接收文件并存储到指定路径以及删除文件操作. FE HTML 基础的设置: <form enctype=& ...

最新文章

  1. undefined reference to symbol ‘_ZN2cv7imwriteERKNS_6StringERKNS_11_InputArrayERKSt6vectorIiSaIiEE‘
  2. 《Python编程快速上手》7.18 实践项目
  3. Hadoop学习之HDFS架构(一)
  4. 详析 Kubernetes 在边缘计算领域的发展
  5. 仲裁服务器装什么系统,Windows 2008故障转移集群之仲裁配置
  6. docker删除为none的镜像
  7. 【MS SQL】通过执行计划来分析SQL性能
  8. 学计算机程序ui设计,学习UI设计需要什么样的电脑配置
  9. 服务器开启虚拟化有什么好处
  10. 菜鸟谈VBA最最基础入门
  11. 【UIAutomator2】实现微信自动加好友功能
  12. Rockchip Linux PCIe 开发指南
  13. 配置oem 13c以监控管理数据库
  14. 基于JSP的“爱心宠物诊所”系统课程设计
  15. 汇文从marc_idx根据书名检索图书
  16. anaconda的令牌是啥_Anaconda是什么?香吗?
  17. Android Studio打包APK安装失败:应用是非正式版本,当前设备不支持安装
  18. 宇宙的电网模型之太阳实验起源谭
  19. Field xxxMapper in xxxServiceImpl required a bean of type XxxMapper解决方法
  20. saiku 3.7.4 构建过程

热门文章

  1. layui layer 自定义皮肤真香警告
  2. eclipse修改自定义皮肤
  3. 龙须酥nbsp;[我的饮食情节#9352;]
  4. 分治算法 循环比赛日程表
  5. setcpu_SetCpu Android超频工具
  6. 《你的降落伞是什么颜色》书摘
  7. 让 Linux 更安全
  8. sigmoid二分类
  9. echarts scatter3D 图标陷进地图
  10. 违反充足理由律的诡辩术