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

发布于 2024-09-02 12:22:34 字数 876 浏览 0 评论 0原文

当我按下修饰键(Shift 或 Control)进行单击时,我的 WndProc 没有看到鼠标松开通知。我在没有修饰键的情况下看到它们,并且在使用修饰键时看到鼠标按下通知。

我试图跟踪我没有编写的组件中的用户操作,因此我使用 Windows 窗体 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 通知,但应该可以使其更具响应性..

编辑:我尝试在感兴趣的组件外部单击control并在释放鼠标按钮之前将光标移动到其中,然后我确实收到了 WM_LBUTTONUP 通知,因此看起来该组件正在捕获鼠标按下时。当另一个窗口捕获鼠标时有什么方法可以接收该通知吗?

谢谢。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

泪是无色的血 2024-09-09 12:22:34

通常,当在(本机)Windows 控件上单击鼠标时,会进入某种模式跟踪循环来管理“拖动”操作。在模态循环期间,消息直接从消息队列中提取并进行处理 - 鼠标松开通知将是模态循环的终止条件之一,因此通常会在不分派的情况下使用。

您可以单击桌面上的其他位置,将鼠标移到窗口上并释放并看到单击吗?这表明某种模式代码正在被鼠标按下消息触发。


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

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

所有这些解决方案都是Windows API。不知道它们如何在托管环境中转化。

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.

夜灵血窟げ 2024-09-09 12:22:34

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

http://msdn.microsoft.com/en-我们/库/ms645608%28VS.85%29.aspx

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

木槿暧夏七纪年 2024-09-09 12:22:34

如果组件捕获鼠标消息,则 WM_LBUTTONUP消息可能会绕过您的包装器并直接进入组件。

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

忆沫 2024-09-09 12:22:34

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

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).

蓝礼 2024-09-09 12:22:34

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

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文