当包装控件设置捕获时,如何接收鼠标事件?

时间:2010-05-25 18:17:56

标签: winforms winapi windows-messages

当我按下修改键(移位或控制)时,我的WndProc没有看到鼠标上通知。我看到它们没有修改键,我看到带有修饰键的鼠标按下通知。

我正在尝试跟踪我未编写的组件中的用户操作,因此我使用Windows Forms NativeWindow包装器(包装组件)从WndProc()方法获取Windows消息。

我已经尝试跟踪我收到的通知,而我看到的唯一线索是WM_CAPTURECHANGED。我收到WM_LBUTTONDOWN消息时尝试调用SetCapture,但它没有帮助。

不使用修饰符(跳过绘画,计时器和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 个答案:

答案 0 :(得分:2)

通常,当在(本机)窗口控件上单击鼠标时,会进入某种模态跟踪循环来管理“拖动”操作。在模态循环的持续时间内,消息直接从消息队列中提取并处理 - 鼠标向上通知将是模态循环的终止条件之一,因此通常在不调度的情况下消耗。

您可以点击桌面上的其他地方,将鼠标移到窗口上并释放并看到点击吗?这表明在鼠标按下消息时会触发某种模态代码。


我可以想出四种可以解决这个问题的方法。

  • 找出控件支持哪种拖动操作 - 并禁用它。希望如果内置的WindowProc知道不允许模态拖动,它将不会进入模态循环。
  • 防止WindowProc发现模态拖动:即拦截AND DO NOT PASS ON任何WM_LBUTTONDOWN消息到链中的下一个Windowproc。
  • 使用SetWindowsHookEx安装消息挂钩。

所有这些解决方案都非常适合Windows API。不知道他们如何在托管环境中翻译。

答案 1 :(得分:0)

在处理程序中,您需要在收到WM_LBUTTONUP消息时检查WPARAM密钥。

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

答案 2 :(得分:0)

如果组件captures mouse messages,WM_LBUTTONUP消息可能绕过您的包装器并直接转到组件。

答案 3 :(得分:0)

默认情况下,修改器和导航键是为操作系统内部使用而保留的。默认键盘处理程序解释它们并根据需要根据它们生成适当的消息。如果控件想要直接对其执行操作,则需要处理WM_GETDLGCODE消息,可选地包含适当的DLGC_WANT...标记,例如DLGC_WANTALLKEYSDLGC_WANTARROWS ,因此密钥作为普通消息传递到消息队列(例如,DLGC_WANTALLKEYS将生成WM_KEYDOWN/UP消息)。

答案 4 :(得分:0)

使用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;
    }
}

该示例并非特定于您的特定目标,但您可以对其进行修改以适应。这恰好是我最近写的。