Подтвердить что ты не робот

Как избежать движения мыши на Touch

У меня есть приложение WPF, которое можно использовать как с помощью мыши, так и с помощью Touch. Я отключу все "усовершенствования" окон, чтобы просто иметь события касания:

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

В результате клик ведет себя так, как я хочу, за исключением двух точек:

  • Маленький "сенсорный" курсор (маленькая белая звезда) появляется, когда щелкнул при перетаскивании.
    Совершенно бесполезно, поскольку пользовательский палец уже находится в этом месте, никакая обратная связь не требуется (кроме моего элемента, который может изменить цвет, если это возможно).
  • Элементы остаются в состоянии "Hover" после окончания движения/клика.

Оба являются следствием того факта, что, хотя окна правильно передают события касания, он по-прежнему перемещает мышь к последнему событию с основным касанием.

Я не хочу, чтобы окна перемещали мышь вообще, когда я использую touch внутри своего приложения. Есть ли способ полностью избежать этого?

Примечания:

  • Обработка событий касания ничего не меняет.
  • Используя SetCursorPos, чтобы переместить мышь, сделайте курсор мигающим и не очень удобным для пользователя.
  • Отключение сенсорной панели в качестве устройства ввода полностью отключает все события (И я также предпочитаю локальное решение приложения, а не системное).
  • Мне все равно, если решение включает COM/PInvoke или предоставляется в C/С++, я переведу.
  • Если необходимо запланировать или перехватить некоторые DLL файлы Windows, пусть и это, программное обеспечение будет работать на выделенном устройстве.
  • Я изучаю поверхностный SDK, но я сомневаюсь, что он покажет любое решение. Поскольку поверхность является устройством с чистым касанием, нет риска плохого взаимодействия с мышью.
4b9b3361

Ответ 1

Вот лучшее решение, которое я нашел отныне. Не стесняйтесь публиковать свои сообщения, особенно если это лучше.

Используя SetWindowsHookEx выслеживание событий на уровне нижнего уровня (WH_MOUSE_LL) и тот факт, что все события, преобразованные из Touch to Mouse, отмечены как такой (флаг MOUSEEVENTF_FROMTOUCH установлен в событии ExtraInfo, см. Часто задаваемые вопросы по Майкрософт) Я смог глобально удалить все события мыши, поступающие с сенсорной панели.

Это не идеальное решение, но теперь оно будет работать в моем приложении, когда оно работает в полноэкранном режиме (в 99% случаев, поскольку это специализированное аппаратное устройство).

Второй шаг также хорош только в полноэкранном режиме (что я не буду предоставлять код, так как это довольно просто) просто перемещает мышь в "безопасное" положение, как в правом нижнем углу экрана, с SetCursorPos.

Если вам нужен код, он находится в 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
    }
}

edit: Из раздела комментариев Устранение неполадок приложений и Системные события и Mouse Messages дополнительную информацию, чтобы устранить ручку от касания.

Ответ 2

Что-то, что я использовал для приложения один раз, было просто настроить пользовательский курсор, чей образ был просто пустым .CUR файлом.

Что касается второй проблемы, я бы предложил переместить курсор в другое место, но потом увидел, что вы сделали то же самое. Если приложение не будет работать в полноэкранном режиме, вы можете просто определить, что безопасное положение должно быть нижним правом углу окна вашего приложения.

Ответ 3

У меня была такая же проблема, и я обнаружил здесь, что можно использовать события 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 MouseEventArgs, чтобы определить, было ли событие касания задействовано ранее. Если он не является нулевым, вы можете установить для параметра e.Handled значение true, чтобы исключить событие, соответствующее событию PreviewXXX.

Надеюсь, что это поможет. Демо-проект для этого можно загрузить из здесь (ссылка Dropbox).