如何避免鼠标在Touch上移动

时间:2011-11-04 13:44:31

标签: c# wpf windows-7

我有一个能够与鼠标一起使用和使用Touch的WPF应用程序。我禁用所有窗口“增强功能”以便只触摸事件:

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

结果是点击表现得像我想要的,除了两点:

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

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

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

注意:

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

4 个答案:

答案 0 :(得分:10)

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

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

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

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

如果您需要代码,请访问a Gist on Github,我将在本文末尾发布当前版本。使用它:

// 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;

    /// <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
    }
}

修改:从Troubleshooting Applications的评论部分和System Events and Mouse Messages其他信息,以消除笔触摸的歧义。

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

对我来说,是否真正要避免将触摸屏提升为鼠标还不是很清楚,因为您首先要询问如何避免将鼠标光标从触摸屏上移开,但是随后在评论中您明确表示确实要鼠标促销。

如果要避免鼠标升级,我发布了一个适用于Windows 8和更高版本here的答案。

答案 3 :(得分:-1)

遇到了同样的问题,我发现here可以使用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");
}

您可以查看StylusDevice的{​​{1}}属性,以确定之前是否涉及过触摸事件。如果它不为null,则可以将MouseEventArgs设置为true,以防止引发与PreviewXXX事件对应的事件。

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

相关问题