4

WPF 触摸失效 试试重启触摸

 3 years ago
source link: https://lindexi.gitee.io/post/WPF-%E8%A7%A6%E6%91%B8%E5%A4%B1%E6%95%88-%E8%AF%95%E8%AF%95%E9%87%8D%E5%90%AF%E8%A7%A6%E6%91%B8.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
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/post/WPF-%E8%A7%A6%E6%91%B8%E5%A4%B1%E6%95%88-%E8%AF%95%E8%AF%95%E9%87%8D%E5%90%AF%E8%A7%A6%E6%91%B8.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

如果你想持续阅读我的最新博客,请点击 RSS 订阅,推荐使用RSS Stalker订阅博客,或者前往 CSDN 关注我的主页

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

无盈利,不卖课,做纯粹的技术博客

以下是广告时间

推荐关注 Edi.Wang 的公众号
lindexi%2F201985113622445

欢迎进入 Eleven 老师组建的 .NET 社区
lindexi%2F20209121930471745.jpg

以上广告全是友情推广,无盈利


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK