WPF 触摸失效 试试重启触摸

在使用一些诡异的系统以及诡异的触摸框的时候,也许会出现 WPF 程序触摸失效,失效的本质原因是 Win32 层应用触摸失效。也许出现的问题是某个窗口设置 TopMost 然后插拔一些触摸设备等,这些行为,如果触摸设备太过诡异,也许就会让 Win32 窗口触摸失效。刚好 WPF 也是一个 Win32 窗口,此时的 WPF 也会触摸失效

这个方法因为过于强,我建议只有你在尝试过其他方法无法修复之后才能使用。本文的方法修复触摸是根据没有什么是重启解决不了的方法修复的,本文的方法将会使用反射调用 WPF 的代码,我仅仅有测试 .NET Framework 4.8 的框架里面的逻辑,这就意味着需要你在运行的设备上安装有 .NET Framework 4.8 框架,但是对于运行的 WPF 没有任何限制,可以使用 .NET Framework 4.5 甚至是 .NET Framework 4.0 的版本。当然,本文方法对于 .NET Core 3.1 和 .NET 5 同样生效

本文的核心逻辑就是调用 WispLogic 的 RegisterHwndForInput 和 UnRegisterHwndForInput 来实现重启触摸,但是没有真的结束触摸线程,因此不够彻底。而我自己基于开源的 WPF 框架也定制了可以从触摸线程都重启的强力版本,当然了,这个版本非开源的版本

在使用本文的方法之前,请确定你对触摸有足够的了解

如果你对触摸的了解很少,那么我推荐你先看以下博客

WPF 触摸屏应用需要了解的知识

浅谈 Windows 桌面端触摸架构演进

WPF 客户端开发需要知道的触摸失效问题

对于 Win32 应用来说,如果应用的触摸失效了,可以的解决方法是重新注册一次触摸,或者重启应用。而在 WPF 中,没有公开的方法可以让咱重启注册触摸,但是使用非公开的方法可以调用到。关于在 WPF 中的触摸调用细节请看 WPF 触摸到事件WPF 通过 InputManager 模拟调度触摸事件

重启注册触摸的步骤就是先反注册,然后再次注册。分别调用的是 WispLogic 的 UnRegisterHwndForInput 和 RegisterHwndForInput 方法,以下是步骤

在 WPF 中,可以使用下面代码获取 StylusLogic 对象

        private object GetStylusLogic()
        {
            TabletDeviceCollection devices = System.Windows.Input.Tablet.TabletDevices;

            if (devices.Count > 0)
            {
                // Get the Type of InputManager.
                Type inputManagerType = typeof(System.Windows.Input.InputManager);

                // Call the StylusLogic method on the InputManager.Current instance.
                object stylusLogic = inputManagerType.InvokeMember("StylusLogic",
                    BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
                    null, InputManager.Current, null);

                return stylusLogic;
            }

            return null;
        }

在没有开启 Pointer 的情况下,这里的 StylusLogic 就是 WispLogic 对象,因为 WispLogic 的定义如下

internal class WispLogic : StylusLogic
{

}

在 Win10 中,大多数的触摸失效问题,都可以通过开启 Pointer 消息解决。而在 .NET 5 中,修复了 WPF 使用 WM_Pointer 消息在高 DPI 下的兼容触摸。如何开启 Pointer 消息请看 WPF dotnet core 如何开启 Pointer 消息的支持

在获取到 WispLogic 就可以通过反射调用 RegisterHwndForInput 和 UnRegisterHwndForInput 方法来重启触摸

通过开源的 WPF 代码可以看到两个方法的定义如下

 internal void RegisterHwndForInput(InputManager inputManager, PresentationSource inputSource)
 {

 }

 internal void UnRegisterHwndForInput(HwndSource hwndSource)
 {

 }

这里的 InputManager 可以使用 InputManager.Current 获取,而 PresentationSource 可以使用 PresentationSource.FromVisual(this) 获取,上面的 this 需要一个在界面显示的元素

而 HwndSource 可以使用下面代码获取

            var windowInteropHelper = new WindowInteropHelper(this);
            var hwndSource = HwndSource.FromHwnd(windowInteropHelper.Handle);

先调用 UnRegisterHwndForInput 禁用触摸,然后调用 RegisterHwndForInput 打开触摸

            object stylusLogic = GetStylusLogic();

            if (stylusLogic == null)
            {
                return;
            }

            Type inputManagerType = typeof(System.Windows.Input.InputManager);
            var wispLogicType = inputManagerType.Assembly.GetType("System.Windows.Input.StylusWisp.WispLogic");

            var windowInteropHelper = new WindowInteropHelper(this);
            var hwndSource = HwndSource.FromHwnd(windowInteropHelper.Handle);

            var unRegisterHwndForInputMethodInfo = wispLogicType.GetMethod("UnRegisterHwndForInput",
                BindingFlags.Instance | BindingFlags.NonPublic);

            unRegisterHwndForInputMethodInfo.Invoke(stylusLogic, new object[] {hwndSource});


            var registerHwndForInputMethodInfo = wispLogicType.GetMethod("RegisterHwndForInput",
                BindingFlags.Instance | BindingFlags.NonPublic);

            registerHwndForInputMethodInfo.Invoke(stylusLogic, new object[]
            {
                InputManager.Current,
                PresentationSource.FromVisual(this)
            });

以上代码放在 githubgitee 欢迎小伙伴访问

本文的方法不能解决内部逻辑调用问题的触摸失效问题,也不能解决太过诡异的系统的触摸失效问题。本文的重启触摸的方法的执行速度是很慢的

以上方法也是有缺点的,使用了上面方法之后,就不能使用 高性能 DynamicRenderer 书写 的方式。解决 DynamicRenderer 丢失的方法就是重新注册一次 StylusPlugIn 元素

更多触摸请看 WPF 触摸相关 更多笔迹相关请看

我搭建了自己的博客 https://blog.lindexi.com/ 欢迎大家访问,里面有很多新的博客。只有在我看到博客写成熟之后才会放在csdn或博客园,但是一旦发布了就不再更新

如果在博客看到有任何不懂的,欢迎交流,我搭建了 dotnet 职业技术学院 欢迎大家加入

如有不方便在博客评论的问题,可以加我 QQ 2844808902 交流

知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

lindexi_gd CSDN认证博客专家 C# WPF UWP 微软最具价值专家
我是微软Windows应用开发方向的最具价值专家,欢迎访问我博客blog.lindexi.com里面有大量WPF和UWP博客
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 终极编程指南 设计师:CSDN官方博客 返回首页