Как избежать движения мыши на Touch - PullRequest
21 голосов
/ 04 ноября 2011

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

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

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

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

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

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

Примечания:

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

Ответы [ 3 ]

10 голосов
/ 04 ноября 2011

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

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

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

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

Если вам нужен код, он находится в Gist на 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: из раздела комментариев Устранение неполадок приложений и Системные событияи сообщения мыши дополнительная информация для устранения неоднозначности пера от прикосновения.

0 голосов
/ 19 февраля 2014

Просто возникла та же проблема, и я обнаружил здесь , что можно использовать события 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).

0 голосов
/ 15 ноября 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...