João Matos
João Matos

Reputation: 1545

Multiple MouseHover events in a Control

I'm trying to implement a custom control in C# and I need to get events when the mouse is hovered. I know there is the MouseHover event but it only fires once. To get it to fire again I need to take the mouse of the control and enter it again.

Is there any way I can accomplish this?

Upvotes: 10

Views: 5301

Answers (5)

jon_two
jon_two

Reputation: 1238

Here is a slightly simpler version that doesn't rely on a Timer. Just record the time of the last mouse move then to compare current time whenever you want to check if the mouse is hovering.

    private DateTime mouseMoveTime = DateTime.Now;
    private Point mouseMoveLoc = new Point();

    private bool IsHovering()
    {
        return (mouseMoveTime.AddMilliseconds(SystemInformation.MouseHoverTime)).CompareTo(DateTime.Now) < 0;
    }

    private void myControl_MouseMove(object sender, MouseEventArgs e)
    {
        // update mouse position and time of last move
        if (Math.Abs(e.X - mouseMoveLoc.X) > SystemInformation.MouseHoverSize.Width || 
            Math.Abs(e.Y - mouseMoveLoc.Y) > SystemInformation.MouseHoverSize.Height)
        {
            mouseMoveLoc = new Point(e.X, e.Y);
            mouseMoveTime = DateTime.Now;
        }
    }

Or a really short version that doesn't have a distance tolerance.

    private DateTime mouseMoveTime = DateTime.Now;

    private bool IsHovering()
    {
        return (mouseMoveTime.AddMilliseconds(SystemInformation.MouseHoverTime)).CompareTo(DateTime.Now) < 0;
    }

    private void myControl_MouseMove(object sender, MouseEventArgs e)
    {
        mouseMoveTime = DateTime.Now;
    }

Just use if (IsHovering())... when you need to check if the mouse is moving.

One thing I have noticed though is that MouseMove is not fired when you are dragging. You can however copy the mouseMoveTime/Loc update code to your drag event to get round this.

Upvotes: 0

Jace
Jace

Reputation: 1532

This was the cleanest implementation I could come up with. It works quite well:

    [DllImport("user32.dll")]
    static extern IntPtr SendMessage
    (
        IntPtr controlHandle,
        uint message,
        IntPtr param1,
        IntPtr param2
    );

    const uint WM_MOUSELEAVE = 0x02A3;

    Point lastMousePoint = Point.Empty;

    void richTextBox_MouseMove(object sender, MouseEventArgs e)
    {
        if (Math.Abs(e.Location.X - lastMousePoint.X) > 1 || Math.Abs(e.Location.Y - lastMousePoint.Y) > 1)
        {
            lastMousePoint = e.Location;

            SendMessage((sender as RichTextBox).Handle, WM_MOUSELEAVE, IntPtr.Zero, IntPtr.Zero);
        }
    }

    void richTextBox_MouseHover(object sender, EventArgs e)
    {
        foo();
    }

Upvotes: 2

Massless
Massless

Reputation: 161

I realize that this is an old topic but I wanted to share a way I found to do it: on the mouse move event call ResetMouseEventArgs() on the control that catches the event.

Upvotes: 8

lc.
lc.

Reputation: 116528

Let's define "stops moving" as "remains within an x pixel radius for n ms".

Subscribe to the MouseMove event and use a timer (set to n ms) to set your timeout. Each time the mouse moves, check against the tolerance. If it's outside your tolerance, reset the timer and record a new origin.

Pseudocode:

Point lastPoint;
const float tolerance = 5.0;

//you might want to replace this with event subscribe/unsubscribe instead
bool listening = false;

void OnMouseOver()
{
    lastpoint = Mouse.Location;
    timer.Start();
    listening = true; //listen to MouseMove events
}

void OnMouseLeave()
{
    timer.Stop();
    listening = false; //stop listening
}

void OnMouseMove()
{
    if(listening)
    {
        if(Math.abs(Mouse.Location - lastPoint) > tolerance)
        {
            //mouse moved beyond tolerance - reset timer
            timer.Reset();
            lastPoint = Mouse.Location;
        }
    }
}

void timer_Tick(object sender, EventArgs e)
{
    //mouse "stopped moving"
}

Upvotes: 9

benPearce
benPearce

Reputation: 38373

Why not subscribe to the MouseMove events in the MouseHover event, then unsubscribe in the MouseLeave event

Edit:

One way to determine if the mouse is stopped would be to start a timer every time you get a mouse move event, when the timer has elapsed then you could consider the mouse stopped. The reason for removing the MouseMove events on MouseLeave would allow you to only receive events while the mouse is over your control.

Upvotes: 2

Related Questions