WPF 触屏事件后触发鼠标事件的问题及 DataGrid 误触问题

WPF 触屏事件后触发鼠标事件的问题及 DataGrid 误触问题

目录

一、触屏事件连带触发鼠标事件的问题

二、DataGrid 误触问题及解决方法

独立观察员 2021 年 10 月 10 日

一、触屏事件连带触发鼠标事件的问题

这个是 WPF 已知的问题,网络上也有一些讨论,但是没有一个完美的方法来解决。本文也就是讲解其中的一种方法,亲测可行。

先来说说具体现象:触屏操作时,如果程序里使用了触屏事件(如:PreviewTouchDown、TouchDown、PreviewTouchUp、TouchUp),那么相应地会接着触发鼠标事件(PreviewMouseDown、MouseDown、PreviewMouseUp、MouseUp),这个据说是微软为了在触屏设备上兼容老程序,让这些程序能够接收从触屏事件转换来的鼠标事件,从而能正常工作。

所以,有一个说法是,只使用鼠标事件就行了,比如就单单使用 PreviewMouseDown 事件,或者按钮的话直接使用 Click 事件,或者使用命令(Command),这种方法理论上是可以的,但是实际情况下,有的时候会发现,这样用的话,触屏操作很不灵敏,可能要点好几次才触发。

这个触屏事件提升为鼠标事件的一个表现就是,触屏拖动或者点击,会在屏幕上 “残留” 鼠标,当然,是不可见的,或者表现为一个小星号。所以,从这个角度出发,产生了这样一种方法:点击后将鼠标移开。

这个方法能满足部分场景,比如之前有这样一个问题,在 DataGrid 表格上方有一个 DatePicker 日期选择控件,日期展开后,下拉的悬浮框会遮在表格上,当在下拉的悬浮框中选择日期后下拉框收起,这时却在表格上产生了某个条目的选中效果。针对于这个情况,就可以使用移开鼠标的方案,相关帮助类见下方链接:

https://gitee.com/dlgcy/WPFTemplateLib/blob/master/WpfHelpers/ClickAndTouchHelper2.cs 

aefb10bdb5785f7acb778731f7182ded.png

但是这次我遇到了一个 DataGrid 的误触问题,用移开鼠标的方法无效(也有可能是使用方法和时机不对),所以只能另寻它法。

注意,本文将在上篇文章《WPF DataGrid 通过自定义表头模拟首行固定》的示例程序基础上进行演示,建议先看看那篇文章。下面开始改造。

首先在行样式中添加了两个事件,一个是 PreviewTouchDown,另一个是 PreviewMouseDown:

f2482be353ce7b6cf199bd0b94487ab1.png

触屏点击某一行,会先触发 PreviewTouchDown,然后触发 PreviewMouseDown,然后是行改变事件 SelectionChanged,最后依次是 PreviewTouchUp 和 PreviewMouseUp。带有 Preview 前缀的是隧道事件(可视为在事件前触发),没有的是冒泡事件(可视为在事件后触发,此处省略)。

那么如何去除触屏事件后连带引发鼠标事件的影响呢?通过在网络上苦苦搜索和尝试,在旧版的微软社区找到了一个可行的方法,帖子为《Prevent a WPF application to interpret touch events as mouse events?》(这个链接之后可能会访问不了)。

提问者就是为了解决触屏操作下触发鼠标事件的问题:

c9673c4946c5ad5a013dbfd7db0dbfca.png

然后里面两个人分别给出了他们的解决方法,先来看看第一个:

78569cc6860b0fdb0c63a5e49ed08ee3.png

这个就是本文采纳的方法,代码文字版如下:

public static class PreventTouchToMousePromotion
{public static void Register(FrameworkElement root){root.PreviewMouseDown  = Evaluate;root.PreviewMouseMove  = Evaluate;root.PreviewMouseUp  = Evaluate;}private static void Evaluate(object sender, MouseEventArgs e){//StylusDevice 属性,触屏操作连带触发时不为 null,鼠标触发时为 null;if (e.StylusDevice != null){e.Handled = true; // 如果判断为 由触屏引发,则将事件标记为已处理;}}
}

再顺便看看第二个人的方法(没有去尝试,感兴趣的朋友可以试试):

bc4ffa7918fd4092410738739ba14628.png

二、DataGrid 误触问题及解决方法

上一个部分介绍了去除触屏事件后连带引发鼠标事件影响的方法,也就是通过鼠标事件参数的 StylusDevice 属性来判断是否是由触屏操作引发的(不为 null 则是触屏操作引发),进而进行处理。

然而,本次我实际上是要解决一个 DataGrid 表格在触屏下的误触问题,相关业务逻辑是在行改变事件(转为命令了)中的,本来是没有写 PreviewTouchDown 和 PreviewMouseDown 事件的(就是为了解决误触问题而引入),所以将鼠标事件标记为已处理(e.Handled = true;)的方法不能直接使用,还需要修改。原因是,行改变事件 SelectionChanged 是在 PreviewMouseDown 事件之后触发的,如果在 PreviewMouseDown 中将事件标记为已处理,那么行改变事件也就不会触发了。

首先来看看误触现象吧(动图):

2086c96963b48555958ff2603c09f339.gif

也就是,我在行改变事件中加了个弹窗,询问用户是否要切换条目,如果选是的话,不作任何处理,如果选否的话,恢复之前的选中项。选是的时候不会有误触现象,选否的时候,鼠标操作的话也正常,而如果在弹窗时通过触屏点击了否,然后在界面空白处(这里是在右侧的信息区)触屏点击几下,就会在表格上,在之前点击要切换到的那一行上产生一个鼠标事件,而且没有触屏事件,这个不用怀疑,通过调试打断点很容易观察到。

关于点击几下会触发这个误触,我发现和屏幕支持几点触控有关。比如,公司的触摸屏支持 10 点触控,那么这里就是点击 10 下左右触发;我自己的一个小触摸屏,支持 5 点触控,这边则是在空白处点击 4 下触发。要查看屏幕支持几点触屏,可通过 GitHub 上的一个项目程序 ManipulationDemo 来查看(https://github.com/dotnet-campus/ManipulationDemo):

f7934f93beafa1ddb1b24a0c246956b6.png

言归正传,从误触现象的动图中可以看到,已经能够判断出是否是误触了:

beba88969f38cae1cca9fb4dc0ee7319.png

那么是怎么判断的呢?来看看代码:

private void EventSetter_PreviewTouchDown(object sender, TouchEventArgs e)
{// 真实触摸时会触发 PreviewTouchDown 事件,而误触时(点击弹窗取消后在空白处点击多次会误触表格)则不会(因为那个只触发鼠标事件);_vm.IsRealTouch = true;
}/* 注意:触摸事件之后还会触发鼠标事件 */private void EventSetter_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{//StylusDevice 属性,触屏操作连带触发时不为 null,鼠标触发时为 nullif (e.StylusDevice != null){// 触屏//e.Handled = true;}else{// 鼠标_vm.IsRealTouch = true; // 避免后续判断不正常;}
}

在 ViewModel 中新增了一个标记变量 IsRealTouch,用来记录是真实的触控或者鼠标点击意图,还是误触。真实触摸时会触发 PreviewTouchDown 事件,而误触时(点击弹窗取消后在空白处点击多次会误触表格)则不会(因为那个只触发鼠标事件),所以只要在鼠标事件 PreviewMouseDown 中能够判断出是否是触屏操作连带触发的就行了,而这个问题在前一部分已经解决了。所以,在触摸事件,以及鼠标事件的单纯鼠标触发的情况下,都对 IsRealTouch 赋值为 true 即可。

行改变事件(命令)中还需要给 IsRealTouch 复位,代码如下:

SelectionChangedCmd ??= new RelayCommand(o => IsCanSelectionChanged, o =>
{try{IsCanSelectionChanged = false;var args = o as SelectionChangedEventArgs;EditType = EditTypeEnum.Show;var isOk = MessageBox.Show($" 是否切换?(是否是误触?{!IsRealTouch})", "触屏误触问题演示", MessageBoxButton.YesNo);if (isOk == MessageBoxResult.No){if (SelectedUser != _originUser){SelectedUser = _originUser;}}}catch (Exception ex){Console.WriteLine(ex);}finally{IsRealTouch = false;_originUser = SelectedUser;IsCanSelectionChanged = true;}
});

可以看到,这样就能识别出是否是误触了。这里是演示,在实际使用时,识别到是误触,就可以直接返回而不用弹窗了。

问题解决了,那么原因呢?对于触屏操作产生鼠标事件,这个是微软为了兼容性而导致的,前面也说过了。至于为什么会有个触点残留在原来的位置,而且点击其它地方一定次数就会触发,这个问题我也没找到原因,请知道的朋友不吝赐教。有两个猜测,一是模态弹窗对事件有影响,一是命令对事件有影响,目前没想到怎么验证。

另外,之前说过弹窗点击是的情况下,后续没有误触现象,所以也有理由怀疑是从代码中改变了选中项(已绑定到 DataGrid 的选中项)所以会有这个问题。从代码中改变选中项又会触发行改变事件,所以加了个 IsCanSelectionChanged 来避免重入,当然,加不加这个避免重入的,都有误触现象。有点晕。 

最后奉上源码地址:https://gitee.com/dlgcy/DLGCY_WPFPractice/tree/Blog20211010 ,大家可以帮忙研究研究。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/297016.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Facebook应用Moments使用C++实现跨平台代码共享

据Facebook工程师Ashwin Bharambe、Zack Gomez及Will Ruben报道,作为Facebook最新应用之一的Moments正使用C实现跨iOS和Android平台共享业务逻辑。这里,我们将回顾一下Facebook工程师给出的选择C的理由和结果。\\对于Moments应用,为了“只关注…

ImportError: libpq.so.5: cannot open shared object file: No such file or directory

import psycopg2Traceback (most recent call last): File "<stdin>", line 1, in ? File "/usr/lib/python2.4/site-packages/psycopg2-2.4.1-py2.4-linux-x86_64.egg/psycopg2/__init__.py", line 71, in ? from psycopg2._psycopg import …

VS2008、VS2010中如何屏蔽讨厌的MSVCR*.dll的引用

VS系列工具作为目前微软主打的集成开发环境&#xff0c;在历经了近20多年的发展后&#xff0c;到如今已经可以说是Windows平台上各种IDE环境中的翘楚了。很多别的开发工具已经难望其项背了&#xff0c;如今VS2010也已经面市很长时间了&#xff0c;但是因为笔者囊中羞涩&#xf…

听说,99%的数学家都算不出这道题

全世界只有3.14 % 的人关注了爆炸吧知识著名的数学家毕达哥拉斯曾说&#xff1a;“朋友是你灵魂的倩影&#xff0c;要像220与284一样亲密。”就因为不经意的一句话&#xff0c;引发了一场数群和数学家长达几千年的“斗争”&#xff0c;甚至到现在依然没有结束&#xff01;无数个…

oracle账号区分大小写吗,实战Oracle 11g用户密码不区分大小写

连接到:Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - ProductionWith the Partitioning, OLAP, Data Mining and Real Application Testing optionsSQL> show  parameter sec_case_sensitive_logonNAME TYPE VAL…

sql 多表多行模糊查询_从零开始学习SQL(五)多表查询

经过之前的学习&#xff0c;现在我们已经对查询有了一定的了解&#xff0c;但是我们目前的所有查询都只能找到在一张表中的数据&#xff0c;但如果我们需要寻找分布在多张表格中的数据时&#xff0c;这种之前的查询就做不到了&#xff0c;这时就需要引入一种新的查询方法&#…

最新.NET MAUI有什么惊喜?

点击蓝字关注我们.NET 6 RC1 现已发布啦&#xff0c;我们为 .NET 多平台应用程序 UI (MAUI) 引入了所有的新布局。这是性能和可靠性的重大变化。我们很高兴我们还增加了一些关于accessibility方面的基于新的SemanticService、字体缩放选项和对Xamarin.Forms 效果的兼容性的功能…

onWindowFocusChanged

这个onWindowFocusChanged指的是这个Activity得到或者失去焦点的时候 就会call。 也就是说 如果你想要做一个Activity一加载完毕&#xff0c;就触发什么的话 完全可以用这个&#xff01;&#xff01;&#xff01; package com.app.android05; import android.app.Activity; imp…

亲一下就搞定的事,绝不花钱解决!

1 好了&#xff0c;你表演吧&#xff0c;球不看了2 “你这个车&#xff0c;至少得100条小鱼干啊” 3 羡慕了4 还…还有这种操作&#xff1f;5 被秀了一脸哈哈哈6 没有什么是一个么么哒解决不了的&#xff01;你点的每个赞&#xff0c;我都认真当成了喜欢

SetComputerName改网络中计算机名

通过 Wiz 发布转载于:https://www.cnblogs.com/xe2011/archive/2012/06/02/2531620.html

VS2010自定义新建文件模版

不知不觉VS2010已经成为.NET开发人员的必备工具&#xff0c;相比经典版VS2005&#xff0c;到过渡版VS2008&#xff0c;2010在性能稳定性和易用性上都得到很大的提高。 结合VS工具&#xff0c;其下的插件也层出不穷。今天重点给大家介绍如何使用VS2010VS2010自定义新建文件模版&…

微软发布了Visual Studio 2022 RC版,并将在11月8日发布正式版

微软今天发布了Visual Studio 2022 最接近正式发布的RC版本&#xff0c;同时宣布在11月8日发布正式版&#xff0c;届时将在线上发布虚拟的发布活动&#xff0c;具体参见&#xff1a;https://devblogs.microsoft.com/visualstudio/join-us-november-8th-for-the-launch-of-visua…

python装备_Python重型武器:Django

Django&#xff0c;发音为[dʒŋɡəʊ](詹戈) &#xff0c;是用python语言写的开源web开发框架&#xff0c;并遵循MVC设计。MVC框架的核心思想是&#xff1a;解耦&#xff0c;让不同的代码块之间降低耦合&#xff0c;增强代码的可扩展性和可移植性&#xff0c;实现向后兼容。M…

安卓 广告位

需求&#xff1a;类似网易新闻客户端&#xff0c;listview头部要显示广告位&#xff0c;广告位数量动态从后台获取&#xff0c;并且不一定一直有广告位 问题&#xff1a;listview下拉刷新与广告位touch事件的冲突解决&#xff0c;广告位的左滑右滑事件与整个fragment的左右切换…

arcgis oracle trace,ArcGIS应用Oracle Spatial特征分析

该文章并不是将Oracle Spatial与ST_Geometry做对比&#xff0c;关于两者的对比&#xff0c;可以参考&#xff1a;http://www.linuxidc.com/Linux/2011-10/45492.htm&#xff0c;这里从数据结构&#xff0c;到性能对比&#xff0c;都描述的很清楚。其实这篇文件就是说明一下在A…

SharePoint Timer Job

首先介绍一下什么是定时器作业&#xff0c;说的再多&#xff0c;也不如一张图说的清楚这两张图应该把我想说的已经表达清楚了&#xff0c;下一步介绍一下如何自定义Timer Job第一步&#xff1a;创建一个类&#xff08;CustomTimerJob.cs&#xff09;第二步&#xff1a;引用 usi…

python获取div标签的id_Python 获取div标签中的文字实例

预备知识点compile 函数compile 函数用于编译正则表达式&#xff0c;生成一个正则表达式( Pattern )对象&#xff0c;供 match() 和 search() 这两个函数使用。语法格式为&#xff1a;re.compile(pattern[, flags]).compile(pattern[, flags])参数&#xff1a;pattern : 一个字…

生活是长跑

人生不是百米冲刺&#xff0c;是长跑&#xff0c;需要日积月累&#xff0c;建立自己的人生观&#xff0c;生活态度&#xff0c;从各个方面去不断的积累自己&#xff0c;最后才能获取成功的人生。 生活是一个系统工程&#xff0c;每一方面都要经营维护&#xff0c;不要因为某种…

这次使用一个最舒服的姿势插入HttpClient拦截器技能点

码甲哥继续在同程艺龙写一点大前端&#xff0c;今天我们来了解一下如何拦截axios请求/响应&#xff1f;这次我们举一反三&#xff0c;用一个最舒适的姿势插入这个技能点。本文阅读耗时5 minute&#xff0c;行文耗时5 Days。axios是一个基于 promise 的网络请求库&#xff0c;可…

甜蜜助攻!情侣之间,到底能有多甜甜甜甜甜甜甜甜

▲ 点击查看没有一丝丝防备&#xff0c;还有 5 天&#xff0c;七夕就要到了。如何借「物」聊表爱意&#xff0c;大概是最近让很多拥有另一半的人头疼不已的问题。重点是&#xff0c;礼物挑不好&#xff0c;小心七夕就变送命题&#xff01;女孩子们到底喜欢什么礼物&#xff1f;…