Как я могу получать события мыши, когда для обернутого элемента управления установлен захват? - PullRequest
2 голосов
/ 25 мая 2010

Мой WndProc не видит уведомлений о наведении мыши, когда я нажимаю, нажимая клавишу-модификатор (Shift или Control). Я вижу их без клавиши-модификатора и вижу уведомления мыши с клавишами-модификаторами.

Я пытаюсь отследить действия пользователя в компоненте, который не написал, поэтому использую оболочку Windows Forms NativeWindow (оболочку для компонента) для получения сообщений Windows из метода WndProc ().

Я пытался отслеживать уведомления, которые я получаю, и я вижу только подсказку WM_CAPTURECHANGED. Я пытался вызвать SetCapture, когда получаю сообщение WM_LBUTTONDOWN, но это не помогает.

Без модификатора (пропуская рисование, таймер и сообщения NCHITTEST):

WM_PARENTNOTIFY
WM_MOUSEACTIVATE
WM_MOUSEACTIVATE
WM_SETCURSOR
WM_LBUTTONDOWN
WM_SETCURSOR
WM_MOUSEMOVE
WM_SETCURSOR
WM_LBUTTONUP

С модификатором (пропуск краски, таймера и сообщений NCHITTEST):

WM_KEYDOWN
WM_PARENTNOTIFY
WM_MOUSEACTIVATE
WM_MOUSEACTIVATE
WM_SETCURSOR
WM_LBUTTONDOWN
WM_SETCURSOR (repeats)
WM_KEYDOWN (repeats)
WM_KEYUP

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

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

Спасибо.

Ответы [ 5 ]

2 голосов
/ 26 мая 2010

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

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


Я могу представить четыре способа решения этой проблемы.

  • Узнайте, какую операцию перетаскивания поддерживает элемент управления, и отключите ее. Надеемся, что если встроенный WindowProc знает, что модальные перетаскивания не разрешены, он не войдет в модальный цикл.
  • Не позволяет WindowProc узнавать о модальном перетаскивании: перехватывать и НЕ передавать любые сообщения WM_LBUTTONDOWN следующему Windowproc в цепочке.
  • Установить перехват сообщений с помощью SetWindowsHookEx.

Все эти решения очень Windows API. Не знаю, как они переводятся в управляемой среде.

0 голосов
/ 27 мая 2010

Используйте Application.AddMessageFilter , чтобы добавить обработчик в собственный обработчик сообщений. Примерно так:

[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
private class ZoomGestureHandler : IMessageFilter
{
    private const UInt32 WM_MOUSEWHEEL = 0x20A;
    private const UInt32 MK_CONTROL = 0x08;

    private readonly ImageListView _target;

    public ZoomGestureHandler(ImageListView target)
    {
        _target = target;
    }

    public bool PreFilterMessage(ref Message m)
    {
        if (m.Msg != WM_MOUSEWHEEL)
        {
            // Not a mouse wheel message
            return false;
        }

        int wheelDelta = HiWord(m.WParam.ToInt32());
        int keyState = LoWord(m.WParam.ToInt32());

        // Mouse wheel scrolled while the Control key was down
        if ((wheelDelta != 0) && (MK_CONTROL == keyState))
        {
            // Hit test the mouse location
            int xPos = LoWord(m.LParam.ToInt32());
            int yPos = HiWord(m.LParam.ToInt32());

            Point controlLocation = _target.Parent.PointToScreen(_target.Location);
            if ((xPos >= controlLocation.X) && (xPos < (controlLocation.X + _target.Width))
                && (yPos >= controlLocation.Y) && (yPos < (controlLocation.Y + _target.Height)))
            {
                // Determine whether to zoom in or out
                if (wheelDelta > 0)
                {
                    _target.ViewModel.TryZoomIn();
                }
                if (wheelDelta < 0)
                {
                    _target.ViewModel.TryZoomOut();
                }
            }
        }
        return false;
    }

    private static int HiWord(int number)
    {
        if ((number & 0x80000000) == 0x80000000)
            return (number >> 16);
        return (number >> 16) & 0xffff;
    }

    private static int LoWord(int number)
    {
        return number & 0xffff;
    }
}

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

0 голосов
/ 27 мая 2010

Модификатор и навигационные клавиши зарезервированы для внутреннего использования ОС по умолчанию. Обработчик клавиатуры по умолчанию интерпретирует их и генерирует соответствующие сообщения на их основе по мере необходимости. Если элемент управления хочет воздействовать на них напрямую, ему необходимо обработать сообщение WM_GETDLGCODE, возможно, с результатом, который включает соответствующие флаги DLGC_WANT..., такие как DLGC_WANTALLKEYS и DLGC_WANTARROWS, чтобы ключи доставлялись в очередь сообщений как обычные сообщения (например, DLGC_WANTALLKEYS будет генерировать WM_KEYDOWN/UP сообщений).

0 голосов
/ 26 мая 2010

Если компонент перехватывает сообщения мыши , сообщение WM_LBUTTONUP может обойти вашу оболочку и перейти непосредственно к компоненту.

0 голосов
/ 25 мая 2010

В вашем обработчике вам необходимо проверить ключ WPARAM при получении сообщения WM_LBUTTONUP.

http://msdn.microsoft.com/en-us/library/ms645608%28VS.85%29.aspx

...