之前做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. Subarray Product Less Than K LT713

    Your are given an array of positive integers nums. Count and print the number of (contiguous) subarr ...

  2. ajax 跨域请求没有带上cookie 解决办法

    公司项目前后端分离.. 前端全部html 静态页面.. 后端java 接口服务 由于前后端分离,出现跨域问题. 为了解决,我们使用jsonp 方式请求接口服务,暂时解决了跨域问题(使用jquery a ...

  3. Homebrew -- mac 缺失包补充工具

    https://brew.sh/index_zh-cn.htmlhttps://brew.sh/ 非root权限下运行 # github 源代码 https://github.com/Homebrew ...

  4. 取消IDEA中代码重复的检测

  5. rails 辅助方法

    rails辅助方法全解: https://ruby-china.github.io/rails-guides/routing.html

  6. 2019.01.21 洛谷P3919 【模板】可持久化数组(主席树)

    传送门 题意简述:支持在某个历史版本上修改某一个位置上的值,访问某个历史版本上的某一位置的值. 思路: 用主席树直接维护历史版本即可. 代码: #include<bits/stdc++.h> ...

  7. 清理docker大日志文件

    1.进入容器文件的存放目录 ,并查看某一个容器的文件大小 [root@auto ~]# [root@auto ~]# cd /var/lib/docker/containers [root@auto ...

  8. s5-1 网络层引言

    网络层要做什么? 源和目的之间的网络有哪些类? 数据报网络  提供无连接的服务 虚电路网络  提供面向连接的服务  网络层的目标:把数据分组一路送到接收机.  网络层利用下层--数据链路层提供的服 ...

  9. int -2147483648 ----- 2147483647

    int最大值+1为什么是-2147483648最小值-1为什么是2147483647   今天一个新手学编程就问到这个问题,很多人第一次学编程肯定会遇到这个问题,大部分都知道是溢出之类的,用源码和补码 ...

  10. php,ajax上传文件,多文件上传

    HTML <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF- ...