之前做WPF开发时曾经遇到这样一个需求:为一个基于 .NET Framework 3.5开发的老旧WPF程序添加触控支持,以便于大屏触控展示。

接手之后发现这是一个大坑。

项目最初的时候完全没考虑过软件架构设计,业务逻辑基本都写在后台代码中,经过两代程序员的开发维护(初代开发者已离职,文档这种东西不存在的),主界面cs代码已经有上万行,各种事件注册的非常杂乱。由于是做给政府部门用的,稳定性很重要,修修补补不断的打补丁,程序已经非常难维护了。

而且不像最新.net框架下的WPF以及UWP开发中,我们有Pointer开头的系列事件可以统一处理鼠标点击和触控。在基于.net框架 4.7以下版本构建的WPF应用里,鼠标点击和触控是独立的,需要分别处理。

这里有一点需要说明:在单点电阻式触控屏(除了ATM机之类的特殊用途,基本要被淘汰掉了)下,系统对单点触控的处理是模拟的鼠标操作,这种情况下即使不处理触控事件,程序也可以正常运行,需要处理触控事件特指的是支持多点触控的电容式触摸屏。

当时我接手的WPF应用之前是完全没有做过触控事件处理的,我粗略的查找统计了一下,需要处理的按钮点击事件大概有上千个,如果手动处理,将是非常难以接受的重复工作,另外修改后的应用程序也必须完整走一遍测试流程,以防带来灾难性BUG。

那么有没有一种简单的方法可以快速处理呢?

我们知道WPF开发中,所有的用户交互事件都是路由事件,其中带有Preview前缀的为隧道路由事件,不带前缀的为冒泡路由事件。其区别是:隧道路由事件由根元素传递到触发事件的元素,而冒泡路由事件传递方向正好相反。那么,尽管程序中需要处理触控事件的地方很多,但是我们都可以在应用顶层元素中通过冒泡路由事件拦截到。是不是可以利用这一点做文章呢?

我的想法是这样的:由于应用已经处理了鼠标交互事件,那我们完全可以将应用的触控事件转发给鼠标交互事件的Handler去处理,这样就避免了我们做机械的重复操作。

具体处理步骤如下:

  1. 在应用窗口的顶级元素(可视化树的根节点)上添加触控事件处理程序,捕获应用内部触控事件;

    this.AddHandler(TouchUpEvent, new RoutedEventHandler(GetTouchUp));
    this.AddHandler(TouchDownEvent, new RoutedEventHandler(GetTouchDown));
  2. 获取引发事件的源控件(原本想通过e.OriginalSource获取,但测试中发现获取的有错误,所以用UIElement类中的InputHitTest方法传入触控点坐标,获取到引发事件的源控件);

    TouchEventArgs te = (TouchEventArgs)e;
    Point p = te.GetTouchPoint(this).Position;//这里是获取触控点相对某个界面元素的坐标
    UIElement uiControl = (UIElement)this.InputHitTest(p);
  3. 触发源控件的鼠标事件(在TouchUp中还同时触发了Button类的Click事件,用于处理按钮的点击事件);

    MouseButtonEventArgs args = new MouseButtonEventArgs(Mouse.PrimaryDevice,te.Timestamp,MouseButton.Left);
    args.RoutedEvent = MouseDownEvent;
    uiControl.RaiseEvent(args);

完整的事件处理代码如下:

	    this.AddHandler(TouchUpEvent, new RoutedEventHandler(GetTouchUp));
this.AddHandler(TouchDownEvent, new RoutedEventHandler(GetTouchDown));
private void GetTouchDown(object sender, RoutedEventArgs e)
{
TouchEventArgs te = (TouchEventArgs)e;
Point p = te.GetTouchPoint(this).Position;
UIElement uiControl = (UIElement)this.InputHitTest(p);
MouseButtonEventArgs args = new MouseButtonEventArgs(Mouse.PrimaryDevice, te.Timestamp, MouseButton.Left);
args.RoutedEvent = MouseDownEvent;
uiControl.RaiseEvent(args);
}
private void GetTouchUp(object sender, RoutedEventArgs e)
{
TouchEventArgs te = (TouchEventArgs)e;
Point p = te.GetTouchPoint(this).Position;
UIElement uiControl = (UIElement)this.InputHitTest(p);
MouseButtonEventArgs args = new MouseButtonEventArgs(Mouse.PrimaryDevice, te.Timestamp, MouseButton.Left);
args.RoutedEvent = MouseUpEvent;
uiControl.RaiseEvent(args);
uiControl.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
}

要说明的一点是,我这里的处理是不完善的,仅仅处理了常见的点击操作。譬如鼠标右键(合理的触控事件应该是长按界面元素一段时间后触发),鼠标移动,滚轮操作都没有做处理,这些也是可以通过类似的方法转换为合适的触控事件触发的。

结尾

今天文章里所述的内容其实已经是很久以前的东西了,我现在的主要工作方向远离WPF开发很久了,突然翻相关的旧文件想起来,所以才有了这篇文章。好记性不如烂笔头,知识不用总有忘的一天,不如写出来贡献给需要的人,谢谢大家!

[WPF]为旧版本的应用添加触控支持的更多相关文章

  1. 【WPF学习】第十八章 多点触控输入

    多点触控(multi-touch)是通过触摸屏幕与应用程序进行交互的一种方式.多点触控输入和更传统的基于笔(pen-based)的输入的区别是多点触控识别手势(gesture)——用户可移动多根手指以 ...

  2. MSDN 杂志:UI 前沿技术 - WPF 中的多点触控操作事件

    原文  MSDN 杂志:UI 前沿技术 - WPF 中的多点触控操作事件 UI 前沿技术 WPF 中的多点触控操作事件 Charles Petzold 下载代码示例 就在过去几年,多点触控还只是科幻电 ...

  3. ccc 多点触控2

    经过不断的思考发现,如果是两个sprite都添加触控的时候,往往直接成单点触控, 但是如果是两个node的时候在node上面点击就会变成多点触控的形式 cc.Class({ extends: cc.C ...

  4. ! cocos2d 同一个sprite的触控问题

    如果对一个A sprite添加触控,然后在一个场景中创建四个A的实例,那么1234逐个添加的话,只有最后一个会被点击到.其他的将不会响应.

  5. 关于android多点触控

    最近项目需要一个多点触控缩放的功能.然后上网查了下资料 总结一下: 首先android sdk版本很重要,比如你在AndroidManifest.xml中指定android:minSdkVersion ...

  6. 安卓Tv开发(一)移动智能电视之焦点控制(触控事件)

    前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家居,以及可穿戴设备的大量使用,但是这些设备上的开发并不是和传统手机开发一样,特别是焦点控制和用户操作体验风格上有很大的区别,本系列博 ...

  7. 添加RichEdit控件后导致MFC对话框程序无法运行的解决方法

    新建一个基于对话框的MFC程序,对话框上添加了RichEdit控件,编译成功后无法运行起来,Debug版本与Release版本均不行! Windbg分析结果: WARNING: Stack unwin ...

  8. Android 单指触控拖拽,两指触控缩放

    import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view. ...

  9. android触控,先了解MotionEvent(一)

    http://my.oschina.net/banxi/blog/56421 这是我个人的看法,要学好android触控,了解MotionEvent是必要,对所用的MotionEvent常用的API要 ...

随机推荐

  1. Java SE EE ME用处

    Java SE: 又称J2SE,开发部署桌面应用程序: Java EE:又称J2EE,开发网站 Java ME:是做手机APP开发 EE在SE基础上构建,提供web服务.组件模型.管理和通信API

  2. Oracle12c的卸载

    之前电脑装了Oracle12c 现在希望删除重新安装: 参照教程: http://jingyan.baidu.com/article/642c9d34e1cbdd644a46f7de.html E:\ ...

  3. JS Async Callback

    AsyncCallback 意义: 异步操作完成时调用的方法 语法1: 构造异步回调对象 AsyncCallback 异步回调对象名asyncCallback = new AsyncCallback( ...

  4. Java的GUI设计

    这里笔者写一些自己做GUI时候的心得,希望可以帮助需要学习的同学,函数的实现和界面设计不在同一个文件中,且涉及参数的传递 一.继承于JFrame   当遇到继承于JFrame的类的时候,可以省去建立对 ...

  5. 219.01.19 bzoj3252: 攻略(长链剖分+贪心)

    传送门 长链剖分好题. 题意:给一棵带点权的树,可以从根节点到任一叶节点走kkk次,走过的点只能计算一次,问kkk次走过的点点权值和最大值. 思路: 考虑将整棵树带权长链剖分,这样链与链之间是不会重复 ...

  6. poj-2406(字符串hash)

    题目链接:传送门 思路:字符串hash,终止条件竟然判断错了,真是太大意了. #include<iostream> #include<cstdio> #include<c ...

  7. Linux 的虚拟文件系统(强烈推荐)

    1 引言 Linux 中允许众多不同的文件系统共存,如 ext2, ext3, vfat 等.通过使用同一套文件 I/O 系统 调用即可对 Linux 中的任意文件进行操作而无需考虑其所在的具体文件系 ...

  8. 相似性度量 Aprioir算法

    第三章 标称:转换成0,1来算,或者用非对称二元属性 二元:x1,x2的分布取00,01,10,11的二元属性个数,列表,算比例.不对称的二元属性就忽略00的属性个数 序数:转换成排位rif,度量:r ...

  9. ubuntu-server-12.04.2安装配置jdk

    原文链接:http://blog.csdn.net/amymengfan/article/details/9958461 我选择的是离线安装,这需要先下载好jdk安装包(下载地址:http://www ...

  10. Update Node.js Package.json

    Update the latest package while using node.js, follow the command as following. npm i -g npm-check-u ...