如何检测鼠标是否在整个窗体及其子控件内?

8
我需要检测用户在表单及其所有子控件上移动鼠标以及离开表单的情况。我尝试了表单的MouseEnterMouseLeave事件,还尝试了窗口消息WM_MOUSEMOVE&WM_MOUSELEAVEWM_NCMOUSEMOVE&WM_NCMOUSELEAVE,但似乎都无法满足我的需求...
由于大部分表单被各种子控件占据,因此客户区域可见性较小。这意味着如果我移动鼠标得太快,鼠标移动将无法被检测到,即使鼠标在表单内部。
例如,我有一个停靠在底部的文本框,在桌面和文本框之间只有很小的边框。如果我迅速将鼠标从底部移动到文本框中,鼠标移动不会被检测到,但鼠标实际上是在文本框内部,也就是在表单内部。
我该如何实现我的需求?
4个回答

13

你可以钩取主消息循环,并预处理/后处理任何你想要的(WM_MOUSEMOVE)消息。

public class Form1 : Form {
    private MouseMoveMessageFilter mouseMessageFilter;
    protected override void OnLoad(EventArgs e) {
        base.OnLoad( e );

        this.mouseMessageFilter = new MouseMoveMessageFilter();
        this.mouseMessageFilter.TargetForm = this;
        Application.AddMessageFilter(this.mouseMessageFilter);
    }

    protected override void OnClosed(EventArgs e) {
        base.OnClosed(e);
        Application.RemoveMessageFilter(this.mouseMessageFilter);
    }

    private class MouseMoveMessageFilter : IMessageFilter {
        public Form TargetForm { get; set; }

        public bool PreFilterMessage( ref Message m ) {
            int numMsg = m.Msg;
            if ( numMsg == 0x0200 /*WM_MOUSEMOVE*/)
                this.TargetForm.Text = string.Format($"X:{Control.MousePosition.X}, Y:{Control.MousePosition.Y}");

            return false;
        }
    }
}

那只是让它反过来工作...我的意思是,现在它可以检测到鼠标是否悬停在表单的子控件上,但不能检测整个表单。我想要检测整个表单。我还需要检测鼠标何时进入表单和离开表单,而不仅仅是移动内部。 - rfgamaral
你看到我的请求了吗,TcKS?也许这次你能做到... 你能否编辑你的帖子并详细说明整个IMessageFilter的工作原理? - rfgamaral
2
我不确定你想要什么。IMessageFilter是非常常见的接口,可以用于几乎与Windows消息系统相关的任何事情。在互联网上有大量的示例和教程。我对此没有额外的知识,除了可以在教程中找到的内容。 - TcKs
这是在主窗口中检测任何表单的绝对鼠标位置,即使我将其添加到包含在其中一个较低嵌入控件中的类中。我只有一个小表单需要触发并根据鼠标相对于其在X、Y上的位置而不是相对于其所包含的主表单来给出坐标。 - fIwJlxSzApHEZIl

2
在你的表单的OnLoad事件中,递归遍历所有子控件(和它们的子控件),并连接MouseEnter事件。这样,每当鼠标进入任何子控件时,事件处理程序都会被调用。同样,您也可以连接MouseMove和/或MouseLeave事件。
protected override void OnLoad()
{
   HookupMouseEnterEvents(this);
}

private void HookupMouseEnterEvents(Control control)
{
   foreach (Control childControl in control.Controls)
   {
      childControl.MouseEnter += new MouseEventHandler(mouseEnter);

      // Recurse on this child to get all of its descendents.
      HookupMouseEnterEvents(childControl);
   }
}

当控件在窗体的生命周期中发生变化时,它将无法工作。 - TcKs

1
快速而粗略的解决方案:
private bool MouseInControl(Control ctrl)
{
    return ctrl.Bounds.Contains(ctrl.PointToClient(MousePosition));
}

0
在您的用户控件上创建一个鼠标悬停事件,例如此类(或其他事件类型)。
private void picBoxThumb_MouseHover(object sender, EventArgs e)
{
    // Call Parent OnMouseHover Event
    OnMouseHover(EventArgs.Empty);
}

在您的WinForm上,托管UserControl的Designer.cs中,请为UserControl增加处理鼠标悬停的代码。

this.thumbImage1.MouseHover += new System.EventHandler(this.ThumbnailMouseHover);

在你的WinForm上调用这个方法

private void ThumbnailMouseHover(object sender, EventArgs e)
{

    ThumbImage thumb = (ThumbImage) sender;

}

其中ThumbImage是用户控件的类型


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接