¿Cuál es la mejor manera de saber si el mouse está sobre un formulario o no?

Descubrí cómo capturar los clics del mouse en todo el formulario, pero este método no se traduce bien para MouseEnter y MouseLeave . El diseño de mi formulario está formado por muchos Panels y TableLayoutPanels por lo que no puedo controlar los eventos para todos, y obviamente un evento MouseLeave para un botón no significa que el cursor haya salido del formulario completo. ¿Alguien ha descubierto una buena manera de evitar esto?

Como alguien señaló aquí , es posible usar SetWindowsHookEx () o simplemente conectar el evento MouseMove en todos los controles del formulario. Este último me funciona bien. El único inconveniente es que si agrega / elimina controles en tiempo de ejecución, es posible que necesite otra solución.

 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; } } } 

Un lugar para comenzar es verificar si el ClientRectangle contiene la posición actual del mouse. Entonces, por ejemplo, en su controlador MouseMove , podría tener:

 if (ClientRectangle.Contains(e.Location)) { bool mouseIsOverThisControl = true; } 

Agregue un temporizador al formulario con un intervalo razonable (tal vez 50 ms). Use este código en el controlador de eventos Tick para ver si el mouse está sobre el formulario:

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

EDITAR: Aquí hay una solución más completa para detectar que el mouse está sobre el formulario y que se ha hecho clic en el botón. timer1Tick() es el controlador de eventos Tick para un temporizador en el formulario. No es necesario tener controladores de eventos adicionales para los otros controles en el formulario. Esto hará que tu formulario sea “un botón gigante” 🙂

 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; } } 

Encontré algunas respuestas que estaban cerca de lo que quería, pero terminé haciendo algo diferente. Quería detectar si el mouse abandonó el área de formulario (incluida la barra de título) y esto funcionó para mí:

En el constructor de formularios, agrego un temporizador:

 time.Interval = 250; time.Tick += time_Tick; time.Start(); 

Luego en el método de la garrapata, hago lo siguiente:

 void time_Tick(object sender, EventArgs e) { switch (RectangleToScreen(Bounds).Contains(PointToScreen(Cursor.Position))) { case true: if (Opacity != .9999D) Opacity = .9999D; break; case false: if (Opacity != .5D) Opacity = .5D; break; } } 

Haga un evento MouseEnter y MouseLeave sobre los controles Form y Form; use un valor booleano para determinar si el mouse ingresó o se fue.

Un ejemplo sería

  private static bool mouseEnteredForm private void Form1_MouseMove(object sender, MouseEventArgs e) { mouseEnteredForm = true; Form.MouseLeave += Form1_MouseLeave; CheckMouseLocation(); } private void Form1_MouseLeave(object sender, MouseEventArgs e) { mouseEnteredForm = false CheckMouseLocation(); } private static void CheckMouseLocation() { if(!mouseOverForm) { MessageBox.Show("Mouse Not Over Form!); } else if(mouseOverForm) //else if is optional. You could also use else in this case. I used else if for the sake of the example. { MessageBox.Show("Mouse Is Over Form"); } } 

Esto puede ser tedioso si tiene muchos objetos sobre la forma