如何避免鼠标在Touch上移动

我有一个WPF应用程序,可以使用鼠标和使用Touch。 我禁用所有窗口“增强function”,只有触摸事件:

Stylus.IsPressAndHoldEnabled="False" Stylus.IsTapFeedbackEnabled="False" Stylus.IsTouchFeedbackEnabled="False" Stylus.IsFlicksEnabled="False" 

结果是点击的行为就像我想要的那样,除了两点:

  • 拖动时点击的小“触摸”光标(小白星)出现。
    完全无用,因为用户手指已经在这个位置,不需要反馈(除非我的元素在可行的情况下可能改变颜色)。
  • 移动/点击结束后,元素保持“hover”状态。

两者都是这样一个事实的结果:当Windows正确传输触摸事件时,他仍然将鼠标移动到最后一个主触摸事件。

当我在应用程序中使用触摸时,我不希望窗口完全移动鼠标。 有没有办法完全避免这种情况?

笔记:

  • 处理触摸事件对此没有任何改变。
  • 使用SetCursorPos移动鼠标使光标闪烁,并不是真正用户友好。
  • 禁用触摸面板充当输入设备会完全禁用所有事件(我也更喜欢应用程序本地解决方案,而不是系统范围的解决方案)。
  • 我不在乎解决方案是否涉及COM / PInvoke,或者在C / C ++中提供我将翻译。
  • 如果有必要修补/挂钩一些Windows dll,那么软件无论如何都将在专用设备上运行。
  • 我正在调查表面SDK,但我怀疑它会显示任何解决方案。 由于表面是纯触摸设备,因此不存在与鼠标交互不良的风险。

这是我从现在开始找到的最佳解决方案。 不要犹豫发布自己的,特别是如果它更好。

使用SetWindowsHookEx低级鼠标事件捕获( WH_MOUSE_LL )以及从Touch转换为鼠标的所有事件都标记为这样( MOUSEEVENTF_FROMTOUCH标志在事件的ExtraInfo中设置,请参阅Microsoft的FAQ )我能够全局删除所有鼠标事件从触摸屏。

它不是一个理想的解决方案,但它现在可以在我的应用程序中运行全屏(99%的时间,因为它是一个专用的硬件设备)。

第二步也只有全屏(我不会提供代码,因为它非常简单)只是将鼠标移动到“安全”位置,如屏幕右下角的SetCursorPos

如果您需要代码,那么它就是Github上的Gist ,我将在本文末尾发布当前版本。 要使用它:

 // As long as the instance is alive the conversion won't occur var disableTouchMouse = new DisableTouchConversionToMouse(); // To let the conversion happen again, Dispose the class. disableTouchMouse.Dispose(); 

该类的完整源代码:

 namespace BlackFox { using System; using System.ComponentModel; using System.Runtime.InteropServices; using System.Security; ///  /// As long as this object exists all mouse events created from a touch event for legacy support will be disabled. ///  class DisableTouchConversionToMouse : IDisposable { static readonly LowLevelMouseProc hookCallback = HookCallback; static IntPtr hookId = IntPtr.Zero; public DisableTouchConversionToMouse() { hookId = SetHook(hookCallback); } static IntPtr SetHook(LowLevelMouseProc proc) { var moduleHandle = UnsafeNativeMethods.GetModuleHandle(null); var setHookResult = UnsafeNativeMethods.SetWindowsHookEx(WH_MOUSE_LL, proc, moduleHandle, 0); if (setHookResult == IntPtr.Zero) { throw new Win32Exception(); } return setHookResult; } delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam); static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { var info = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)); var extraInfo = (uint)info.dwExtraInfo.ToInt32(); if ((extraInfo & MOUSEEVENTF_MASK) == MOUSEEVENTF_FROMTOUCH) { if((extraInfo & 0x80) != 0) { //Touch Input return new IntPtr(1); } else { //Pen Input return new IntPtr(1); } } } return UnsafeNativeMethods.CallNextHookEx(hookId, nCode, wParam, lParam); } bool disposed; public void Dispose() { if (disposed) return; UnsafeNativeMethods.UnhookWindowsHookEx(hookId); disposed = true; GC.SuppressFinalize(this); } ~DisableTouchConversionToMouse() { Dispose(); } #region Interop // ReSharper disable InconsistentNaming // ReSharper disable MemberCanBePrivate.Local // ReSharper disable FieldCanBeMadeReadOnly.Local const uint MOUSEEVENTF_MASK = 0xFFFFFF00; const uint MOUSEEVENTF_FROMTOUCH = 0xFF515700; const int WH_MOUSE_LL = 14; [StructLayout(LayoutKind.Sequential)] struct POINT { public int x; public int y; } [StructLayout(LayoutKind.Sequential)] struct MSLLHOOKSTRUCT { public POINT pt; public uint mouseData; public uint flags; public uint time; public IntPtr dwExtraInfo; } [SuppressUnmanagedCodeSecurity] static class UnsafeNativeMethods { [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr GetModuleHandle(string lpModuleName); } // ReSharper restore InconsistentNaming // ReSharper restore FieldCanBeMadeReadOnly.Local // ReSharper restore MemberCanBePrivate.Local #endregion } } 

编辑:从故障排除应用程序和系统事件和鼠标消息的注释部分,以消除触摸笔的歧义。

我曾经用过一次应用程序的东西,就是简单地设置一个自定义光标,其图像只是一个空白.CUR文件。

至于第二个问题,我确实建议将光标移动到另一个位置,但后来我看到你做了同样的事情。 如果您的应用程序无法全屏运行,您只需将该安全位置定义为应用程序窗口的右下角即可。

刚遇到同样的问题,我在这里发现可以使用PreviewXXX事件确定事件是通过触摸还是通过鼠标输入启动的。 看看下面的代码:

 private void UIElement_OnPreviewMouseMove(object sender, MouseEventArgs e) { if (e.StylusDevice != null) { AddInfoItem("Stylus or Touch recognized"); e.Handled = true; return; } AddInfoItem("No Stylus or Touch recognized"); } 

您可以检查MouseEventArgsStylusDevice属性,以确定之前是否涉及触摸事件。 如果它不为null,则可以将e.Handled设置为true以防止引发与PreviewXXX事件对应的事件。

希望有所帮助。 可以从此处下载此演示项目(Dropbox链接)。