views:

760

answers:

7

I have a form with a lot of controls on it. How can I detect when the mouse leaves the form? I've tried wiring up a MouseLeave event for every single control and the form, but that does not work because those events fire all the time as the mouse passes over controls. Is there a way that actually works.?

A: 

You need to listen to the mouseleave event for the form itself.

korona
A: 

Korona, your method does not work, because if the mouse leaves the form via a space that is covered with a control, form_mouseLeave will not fire.

AngryHacker
+2  A: 

You should listen:

  • MouseLeave events of all controls of the form
  • MouseLeave event of the form

Just link your listeners to a function that checks whether the cursor is in the forms client are or not.

Try this:

    protected override void OnControlAdded(ControlEventArgs e)
    {
        SubscribeEvents(e.Control);
        base.OnControlAdded(e);
    }

    protected override void OnControlRemoved(ControlEventArgs e)
    {
        UnsubscribeEvents(e.Control);
        base.OnControlRemoved(e);
    }

    private void SubscribeEvents(Control control)
    {
        control.MouseLeave += new EventHandler(control_MouseLeave);
        control.ControlAdded += new ControlEventHandler(control_ControlAdded);
        control.ControlRemoved += new ControlEventHandler(control_ControlRemoved);

        foreach (Control innerControl in control.Controls)
        {
            SubscribeEvents(innerControl);
        }
    }

    private void UnsubscribeEvents(Control control)
    {
        control.MouseLeave -= new EventHandler(control_MouseLeave);
        control.ControlAdded -= new ControlEventHandler(control_ControlAdded);
        control.ControlRemoved -= new ControlEventHandler(control_ControlRemoved);

        foreach (Control innerControl in control.Controls)
        {
            UnsubscribeEvents(innerControl);
        }
    }

    private void control_ControlAdded(object sender, ControlEventArgs e)
    {
        SubscribeEvents(e.Control);
    }

    private void control_ControlRemoved(object sender, ControlEventArgs e)
    {
        UnsubscribeEvents(e.Control);
    }

    protected override void OnMouseLeave(EventArgs e)
    {
        CheckMouseLeave();
        base.OnMouseLeave(e);
    }

    private void control_MouseLeave(object sender, EventArgs e)
    {
        CheckMouseLeave();
    }

    private void CheckMouseLeave()
    {
        Point pt = PointToClient(Cursor.Position);

        if (ClientRectangle.Contains(pt) == false)
        {
            OnMouseLeftFrom();
        }
    }

    private void OnMouseLeftFrom()
    {
        Console.WriteLine("Mouse left the form");
    }
Eren Aygunes
Nice, I have learned something new: ClientRectangle.Contains(pt)Thanks. +1
Stefan
A: 

Im putting this link here as a reference. The link shows how to add a hook to mouse and keyboard events in .Net using win32-API so you can receive events even when your app does not has the focus:

http://www.colinneller.com/blog/PermaLink,guid,2838f59a-f4af-4c95-a322-b9ee5918a39c.aspx

Even if it does not answer the question I think it is relevant information that can be handy when fighting issues like this and other similar problems with keyboard/mouse-events.

Stefan
A: 

From looking at aygunes.myopenid.com's answer I made this version in Vb.Net that recursive adding MouseLeaveHandlers to all controls on the form and then Use the nice Clientrectangle.Contains(pt) to check if mousecursor is on or out of form. Working like a charm. Cred goes to aygunes.myopenid.com.

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    AddMouseLeaveHandlers()
End Sub
Sub AddMouseLeaveHandlers()
    For Each c As Control In Me.Controls
        HookItUp(c)
    Next
    AddHandler Me.MouseLeave, AddressOf CheckMouseLeave
End Sub
Sub HookItUp(ByVal c As Control)        
    AddHandler c.MouseLeave, AddressOf CheckMouseLeave
    If c.HasChildren Then
        For Each f As Control In c.Controls
            HookItUp(f)
        Next
    End If
End Sub
Private Sub CheckMouseLeave(ByVal sender As Object, ByVal e As System.EventArgs)
    Dim pt As Point = PointToClient(Cursor.Position)
    If ClientRectangle.Contains(pt) = False Then
        MsgBox("Mouse left form")
    End If
End Sub
Stefan
+1  A: 

This is all well and good, but what happens when someone runs your application using a touch screen PC, and the mouse never enters or leaves anything? While I'd say it's fine to do this kind of thing for eyecandy, I would strongly advise against using it for any real application functionality

Orion Edwards
or can only use a keyboard (WCAG - http://www.w3.org/TR/2008/WD-UNDERSTANDING-WCAG20-20081103/keyboard-operation-keyboard-operable.html)
StingyJack
+1  A: 

The only reliable way I know of is a timer. Here's sample code that tweaks the opacity on a roll-over:

  public partial class Form1 : Form {
    Timer timer1 = new Timer();
    public Form1() {
      InitializeComponent();
      this.Opacity = 0.10;
      timer1.Tick += new EventHandler(timer1_Tick);
      timer1.Interval = 200;
      timer1.Enabled = true;
    }

    void timer1_Tick(object sender, EventArgs e) {
      Point pos = Control.MousePosition;
      bool inForm = pos.X >= Left && pos.Y >= Top && pos.X < Right && pos.Y < Bottom;
      this.Opacity = inForm ? 0.99 : 0.10;
    }
  }
Hans Passant
Although the "elegance" factor on this is pretty low, I agree that this is probably the only reliable way to do this.
Kevin