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?
views:
69answers:
3A 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;
}
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;
}
}
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;
}
}
}