8

I need to detect when the user moves the mouse over the Form and all its child controls and also when it leaves the Form. I tried the MouseEnter and MouseLeave events of the Form, I tried the WM_MOUSEMOVE & WM_MOUSELEAVE and WM_NCMOUSEMOVE & WM_NCMOUSELEAVE pairs of windows messages but none seem to work as I want...

Most of my Form is occupied by child controls of many sorts, there's not much client area visible. This means that if I move the mouse very quickly the mouse movement will not be detected, although the mouse is inside the Form.

For instance, I have a TextBox that is docked at the bottom and between the desktop and the TextBox, there's only a very small border. If I quickly move the mouse from the bottom into the TextBox, the mouse movement won't be detected, but the mouse is inside the TextBox, therefore, inside the Form.

How can I achieve what I need?

Ghasem
  • 14,455
  • 21
  • 138
  • 171
rfgamaral
  • 16,546
  • 57
  • 163
  • 275

4 Answers4

13

You can hook the main message loop and preprocess/postprocess any (WM_MOUSEMOVE) message what you want.

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;
        }
    }
}
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
TcKs
  • 25,849
  • 11
  • 66
  • 104
  • That only makes it work the other way... I mean, now it detects when the mouse is over the Form's child controls, but no the form itself. I want to detect the WHOLE thing. I also need to detect when the mouse entered the Form and when it left, not just that it's moving inside. – rfgamaral Jun 12 '09 at 13:27
  • Well, I found exactly what I was looking for in this example: http://netcode.ru/dotnet/?lang=&katID=30&skatID=283&artID=7862. It uses the same principle as your answer, the IMessageFilter. And it allows me to detect when the mouse enters and leaves the form. I just have to adapt and strip the code to what I want. Anyway, if you could elaborate your answer on the IMessageFilter, what it is, how it works and all that, I shall mark this answer as the accepted one. And please add a note to check the comments for other people looking for a solution to the exact same problem. – rfgamaral Jun 12 '09 at 14:19
  • Did you even saw my request TcKS? Maybe you do this time... Could you please edit your post and elaborate a bit on the whole IMessageFilter thing and how it works? – rfgamaral Jun 13 '09 at 19:16
  • 2
    I'm not sure, what exactly you want. The IMessageFilter is very common interface and can be used for almost anything related with windows messaging system. On the internet are tons of samples and tutorials. I don't have no additional knowledge about it, than can be found in the tutorials. – TcKs Jun 18 '09 at 18:37
  • This is detecting absolute mouse position across ANY form in the main window even though I added it to a class contained in one of the lower embedded controls. I have just one small form I need this to fire for and give coordinates based on where the mouse is relative to it on X,Y not relative to the main form upon which is is contained. – fIwJlxSzApHEZIl Feb 22 '13 at 23:23
2

How about this: In your form's OnLoad, recursively go through all of the child controls (and their children) and hook up the MouseEnter event.

Then whenever the mouse enters any descendant, the event handler will be called. Similarly, you could hook up MouseMove and/or MouseLeave events.

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);
   }
}
NascarEd
  • 1,380
  • 12
  • 14
1

Quick and dirty solution:

private bool MouseInControl(Control ctrl)
{
    return ctrl.Bounds.Contains(ctrl.PointToClient(MousePosition));
}
Defkon1
  • 492
  • 4
  • 15
0

On your user control create a mousehover Event for your control like this, (or other event type) like this

private void picBoxThumb_MouseHover(object sender, EventArgs e)
{
    // Call Parent OnMouseHover Event
    OnMouseHover(EventArgs.Empty);
}

On your WinFrom which hosts the UserControl have this for the UserControl to Handle the MouseOver in your Designer.cs

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

Which calls this method on your WinForm

private void ThumbnailMouseHover(object sender, EventArgs e)
{

    ThumbImage thumb = (ThumbImage) sender;

}

Where ThumbImage is the type of usercontrol

fkerrigan
  • 270
  • 2
  • 9