views:

69

answers:

3

I figured out how to capture mouse clicks over the entire form, but this method doesn't translate well for MouseEnter and MouseLeave. My form layout is made up from many Panels and TableLayoutPanels so there's no all-encompassing control I can monitor events for, and obviously a MouseLeave event for a button doesn't mean the cursor left the entire form. Has anyone figured out a good way to get around this?

A: 

A place to start is to check if the ClientRectangle contains the current mouse position. So, for example, on your MouseMove handler, you could have:

if (ClientRectangle.Contains(e.Location))
{
    bool mouseIsOverThisControl = true;
}
bentsai
Would this work for the entire form, tho? I can see how this would catch `MouseEnter` events, but when the mouse leaves the form, it stops calling `MouseMove` events, so it wouldn't check to see if it's left the form or not, no?
Daniel Rasmussen
When do you need to know that the mouse is over your form?
bentsai
The entire form acts like a giant button. (It's a notification of an incoming call.) So when the user mouse's over the form, I want it to light up, indicating that pressing it will take the call.
Daniel Rasmussen
A: 

Add a timer to the form with a reasonable Interval (maybe 50ms). Use this code in the Tick event handler to see if the mouse is over the form:

// Check if mouse is currently over the form
    bool temp_mof = ClientRectangle.Contains(
       Form.MousePosition.X - Location.X,
       Form.MousePosition.Y - Location.Y);

EDIT: Here is a more complete solution to detecting that the mouse is over the form and that the button has been clicked. timer1Tick() is the Tick event handler for a Timer on the form. There is no need to have additional event handlers for the other controls on the form. This will make your form "one giant button" :)

bool m_mouse_over_form = false;
// Assume the left button is down at onset
bool m_left_button_down = true;

void timer1Tick (object sender, EventArgs e)
{
   // Check if mouse is currently over the form
   bool temp_mof = ClientRectangle.Contains(
      Form.MousePosition.X - Location.X,
      Form.MousePosition.Y - Location.Y);

   // were we already over the form before this tick?
   if (temp_mof && m_mouse_over_form)
   {
       // we need to detect the mouse down and up to avoid
       // repeated calls if the mouse button is held down for more
       // than our Tick interval

       // was the mouse button up prior to now?
       if (!m_left_button_down)
       {
           // is the button down now?
           m_left_button_down = (MouseButtons == MouseButtons.Left);

           if (m_left_button_down)
           {
               // the button was down and has now been released
               LeftButtonClickHandler();
           }
           else
           {
               // do nothing, the button has not been release yet
           }
       }
       else
       {
           // update the button state
           m_left_button_down = (MouseButtons == MouseButtons.Left);
       }
   }
   else if (temp_mof)
   {
       // the mouse just entered the form

       m_mouse_over_form = true;

       // set the initial state of the left button
       m_left_button_down = MouseButtons == MouseButtons.Left);
   }
   else
   {
       // the mouse is not currently over the form
       m_mouse_over_form = false;
       m_left_button_down = true;
   }
}
Jerry Fernholz
-1: Please do not do this. SlavaGu gives a very good answer..explore Api calls if you must. Just don't do this. Ever.
Rusty
A: 

As someone pointed out here it's possible to use SetWindowsHookEx() or just hook up MouseMove event onto all controls in the form. The latter works for me fine. The only downside is if you add/remove controls at runtime you might need another solution.

using System;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsForms_MouseEvents
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            MouseMove += OnMouseMove;
            MouseLeave += OnMouseLeave;

            HookMouseMove(this.Controls);
        }

        private void HookMouseMove(Control.ControlCollection ctls)
        {
            foreach (Control ctl in ctls)
            {
                ctl.MouseMove += OnMouseMove;
                HookMouseMove(ctl.Controls);
            }
        }

        private void OnMouseMove(object sender, MouseEventArgs e)
        {
            BackColor = Color.Plum;

            Control ctl = sender as Control;
            if (ctl != null)
            {
                // Map mouse coordinate to form
                Point loc = this.PointToClient(ctl.PointToScreen(e.Location));
                Console.WriteLine("Mouse at {0},{1}", loc.X, loc.Y);
            }
        }

        private void OnMouseLeave(object sender, EventArgs e)
        {
            BackColor = Color.Gray;
        }

    }
}
SlavaGu