如何避免鼠标在Touch上移动

Jul*_*lia 21 c# wpf windows-7

我有一个WPF应用程序,可以使用鼠标和使用Touch.我禁用所有窗口"增强功能"以便只触摸事件:

Stylus.IsPressAndHoldEnabled="False"
Stylus.IsTapFeedbackEnabled="False"
Stylus.IsTouchFeedbackEnabled="False"
Stylus.IsFlicksEnabled="False"
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

笔记:

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

Jul*_*lia 10

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

使用SetWindowsHookEx低级别鼠标事件catch(WH_MOUSE_LL)以及从Touch转换为Mouse的所有事件都标记为这样(MOUSEEVENTF_FROMTOUCH标志在事件的ExtraInfo中设置,请参阅Microsoft的常见问题解答)我能够全局删除所有触摸后的鼠标事件面板.

它不是一个理想的解决方案,但它现在可以在我的应用程序中运行全屏幕(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();
Run Code Online (Sandbox Code Playgroud)

该类的完整源代码:

namespace BlackFox
{
    using System;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    using System.Security;

    /// <summary>
    /// As long as this object exists all mouse events created from a touch event for legacy support will be disabled.
    /// </summary>
    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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 在 HookCallback 中声明 extraInfo 变量时,将 ToInt32 更改为 ToInt64,以防止在 64 位 Windows 中崩溃。 (2认同)