How can I receive mouse events when a wrapped control has set capture?

感情迁移 提交于 2019-12-24 00:54:59

问题


My WndProc isn't seeing mouse-up notifications when I click with a modifier key (shift or control) pressed. I see them without the modifier key, and I see mouse-down notifications with the modifier keys.

I'm trying to track user actions in a component I didn't write, so I'm using the Windows Forms NativeWindow wrapper (wrapping the component) to get Windows messages from the WndProc() method.

I've tried tracking the notifications I do get, and I the only clue I see is WM_CAPTURECHANGED. I've tried calling SetCapture when I receive the WM_LBUTTONDOWN message, but it doesn't help.

Without modifier (skipping paint, timer and NCHITTEST messages):

WM_PARENTNOTIFY
WM_MOUSEACTIVATE
WM_MOUSEACTIVATE
WM_SETCURSOR
WM_LBUTTONDOWN
WM_SETCURSOR
WM_MOUSEMOVE
WM_SETCURSOR
WM_LBUTTONUP

With modifier (skipping paint, timer and NCHITTEST messages):

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

If I hold the mouse button down for a long time, I can usually get a WM_LBUTTONUP notification, but it should be possible to make it more responsive..

Edit: I've tried control-clicking outside of the component of interest and moving the cursor into it before releasing the mouse button, and then I do get a WM_LBUTTONUP notification, so it looks like the component is capturing the mouse on mouse-down. Is there any way to receive that notification when another window has captured the mouse?

Thanks.


回答1:


Frequently, when the mouse is clicked down on a (native) windows control, some kind of modal tracking loop is entered to manage the "drag" operation. During the duration of the modal loop messages are directly extracted from the message queue and processed - the mouse up notification would be one of the terminating conditions for the modal loop and thus typically consumed without being dispatched.

Can you click elsewhere on the desktop, move the mouse over the window and release and see the click? That would indicate some kind of modal code is being triggered on mouse-down messages.


I can think of four ways you can possibly get around this problem.

  • Find out what sort of drag operation the control supports - and disable it. Hopefully if the built in WindowProc knows that no modal drags are allowed it won't enter a modal loop.
  • Prevent the WindowProc finding out about the modal drag: i.e. intercept AND DON'T PASS ON any WM_LBUTTONDOWN messages to the next Windowproc in the chain.
  • Install a message hook using SetWindowsHookEx.

All these solutions are very windows API. No idea how they translate in the managed environment.




回答2:


Within your handler you need to check the WPARAM key when you receive a WM_LBUTTONUP message.

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




回答3:


If the component captures mouse messages, the WM_LBUTTONUP message might bypass your wrapper and go directly to the component.




回答4:


Modifier and navigation keys are reserved for the OS's internal use by default. The default keyboard handler interprets them and generates appropriate messages based on them as needed. If a control wants to act on them directly, it needs to handle the WM_GETDLGCODE message, optionally with a result that includes the appropriate DLGC_WANT... flags, such as DLGC_WANTALLKEYS and DLGC_WANTARROWS, so the keys are delivered to the message queue as normal messages (for instance, DLGC_WANTALLKEYS will generate WM_KEYDOWN/UP messages).




回答5:


Use Application.AddMessageFilter to add a handler to the native message pump. Something like this:

[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;
    }
}

That sample isn't specific to your particular goal, but you can modify it to suit. That just happened to be one I've written recently.



来源:https://stackoverflow.com/questions/2907316/how-can-i-receive-mouse-events-when-a-wrapped-control-has-set-capture

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!