WPF 开启Pointer消息存在的坑

2022-08-12 19:33:26 浏览数 (1)

本文记录在 WPF 开启 Pointer 消息的坑

屏幕键盘

启用了Pointer之后,调用Textbox.Focus(),起不来屏幕键盘,必须点在它之上才行,触摸在它之上才行

使用屏幕绝对坐标而不是窗口坐标

默认 Pointer 消息是使用屏幕绝对坐标而不是窗口坐标

可能存在获取 Stylus 事件时触摸点不准,此时可以通过获取 Touch 代替,详细请看 WPF will have a touch offset after trun on the WM_Pointer message · Issue #3360 · dotnet/wpf 此问题应该在 Fix raw stylus data to support per-monitor DPI by rladuca · Pull Request #2891 · dotnet/wpf 修复

开启 Pointer 消息之后无法隐藏触摸反馈点

开启 Pointer 消息之后,调用 Stylus.IsPressAndHoldEnabled="False" 无效

在没有开启 Pointer 消息,将会在 System.Windows.Interop.HwndSource 的 Initialize 方法通过判断是否开启 Pointer 消息执行 HwndStylusInputProvider 逻辑

代码语言:javascript复制
            if (StylusLogic.IsStylusAndTouchSupportEnabled)
            {
                // Choose between Wisp and Pointer stacks
                if (StylusLogic.IsPointerStackEnabled)
                {
                	// 开启 Pointer 的逻辑
                    _stylus = new SecurityCriticalDataClass<IStylusInputProvider>(new HwndPointerInputProvider(this));
                }
                else
                {
                    _stylus = new SecurityCriticalDataClass<IStylusInputProvider>(new HwndStylusInputProvider(this));
                }
            }

在 HwndStylusInputProvider 将会读取 IsPressAndHoldEnabledProperty 属性,然后使用 WM_TABLET_QUERYSYSTEMGESTURESTATUS 返回 1 的方式告诉系统不显示触摸反馈点。也就是 WPF 隐藏触摸反馈点是通过 How do I disable the press-and-hold gesture for my window 的方法

如果不设置 Stylus.IsPressAndHoldEnabled="False" 也可以自己手动监听消息,在消息 WM_TABLET_QUERYSYSTEMGESTURESTATUS 里面返回 1 就可以告诉系统不显示触摸反馈点

代码语言:javascript复制
       private IntPtr Hook(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
        {
            const int WM_TABLET_DEFBASE =0x02C0;
            const int WM_TABLET_QUERYSYSTEMGESTURESTATUS = WM_TABLET_DEFBASE   12;
            const int WM_TABLET_FLICK = WM_TABLET_DEFBASE   11;

            if (msg == WM_TABLET_QUERYSYSTEMGESTURESTATUS)
            {
                uint flags = 0;

                flags |= TABLET_PRESSANDHOLD_DISABLED;
                flags |= TABLET_TAPFEEDBACK_DISABLED;
                flags |= TABLET_TOUCHUI_FORCEON;
                flags |= TABLET_TOUCHUI_FORCEOFF;
                flags |= TABLET_FLICKS_DISABLED;

                handled = true;
                return new IntPtr(flags);
            }
            else if (msg == WM_TABLET_FLICK)
            {
                handled = true;
                return new IntPtr(1);
            }

            return IntPtr.Zero;
        }

        private const uint TABLET_PRESSANDHOLD_DISABLED = 0x00000001;
        private const uint TABLET_TAPFEEDBACK_DISABLED = 0x00000008;
        private const uint TABLET_TOUCHUI_FORCEON = 0x00000100;
        private const uint TABLET_TOUCHUI_FORCEOFF = 0x00000200;
        private const uint TABLET_FLICKS_DISABLED = 0x00010000;

但如果开启了 Pointer 消息,那么这个机制将会无效,即使依然是手动监听消息,如 https://github.com/lindexi/lindexi_gd/tree/81b2a63a/KemjawyecawDurbahelal 的代码,也是无效的

问题报告给了 WPF 官方,请看 WPF can not work well with set IsPressAndHoldEnabled to false when enable pointer message · Issue #3379 · dotnet/wpf 但预计不会在 WPF 中修复,原因是这是 Windows 的 WM_Pointer 机制的坑,和 WPF 其实没有关系

另一个解决方法是在关闭系统全局触摸反馈点,关闭方法请看 3 Ways to Enable or Disable Touch Feedback in Windows 10

不存在互斥触摸交互

其实这个也算是一个特性,但是行为有变更。在 Win10 提出的一个新交互里面,允许未激活的窗口接收到鼠标滚轮消息。这一套是和 Pointer 一起提出的,我问了微软的大佬,收到了 MVP 内部邮件,可惜我没看明白,大概的意思是这个交互是 Win10 提供的,和 Pointer 走的是差不多的逻辑

这也就导致了原本支持互斥独占的触摸交互,在开启 Pointer 的应用下被无效。表现是如当前触摸被某个获取焦点的窗口捕获,此时触摸点到一个后台的窗口,未激活的窗口上,那此窗口依然可以收到触摸消息,无论这个窗口是在哪个进程上,只需要此窗口所在的进程开启 Pointer 消息即可

而原先的交互是如果触摸被某个前台窗口捕获,那么其他窗口将啥都收不到,包括 WM_Touch 消息或者实时触摸消息

滑动过程开启窗口触摸失效

在进行 Manipulation 过程中,打开或者激活了窗口,将导致此窗口不接受触摸消息而触摸失效。例如另一个进程的文本框获取焦点时,在滑动 ListView 列表时,打开了窗口或者激活现有的窗口到前台获取焦点,在此窗口内进行触摸,可能会收不到触摸事件

原因是在进行 Manipulation 将会设置一些特殊的内部字段参数,原本不走 Pointer 时,将会自然走到 MouseDevice.cs 的逻辑,触发了 Activate 逻辑,让 WPF 框架层处理窗口激活交互逻辑。但是在 Pointer 层时,走的是 PointerLogic.cs 的逻辑,没有激活交互的逻辑。修复方法是在 PointerLogic.cs 的逻辑也调用 MouseDevice.cs 的 PushActivateInputReport 方法激活交互

此问题已修复,参阅 Port touch activation fix from 4.8 by SamBent · Pull Request #5836 · dotnet/wpf

对应在 2022 的一月系统质量更新补丁,如 50088XX 系列补丁,参阅 https://support.microsoft.com/kb/5008890

.NET Framework January 2022 Security and Quality Rollup Updates - .NET Blog

0 人点赞