I had the exact same need. Paul Williams' answer provided me with the core idea, but I had difficulty understanding the code. I found another take here, and together, the two examples helped me develop my own version.
To initialize, you pass the container control of interest into the ContainerMessageFilter
constructor. The class collects the window handles of the container and all child controls within it.
Then, during operation, the class filters the WM_MOUSEMOVE
message, checking the messages's HWnd
to determine what control the mouse is moving within. In this way, it determines when the mouse has moved within or outside the set of controls within the container that it is watching.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
public class ContainerMessageFilter : IMessageFilter {
private const int WM_MOUSEMOVE = 0x0200;
public event EventHandler MouseEnter;
public event EventHandler MouseLeave;
private bool insideContainer;
private readonly IEnumerable<IntPtr> handles;
public ContainerMessageFilter( Control container ) {
handles = CollectContainerHandles( container );
}
private static IEnumerable<IntPtr> CollectContainerHandles( Control container ) {
var handles = new List<IntPtr> { container.Handle };
RecurseControls( container.Controls, handles );
return handles;
}
private static void RecurseControls( IEnumerable controls, List<IntPtr> handles ) {
foreach ( Control control in controls ) {
handles.Add( control.Handle );
RecurseControls( control.Controls, handles );
}
}
public bool PreFilterMessage( ref Message m ) {
if ( m.Msg == WM_MOUSEMOVE ) {
if ( handles.Contains( m.HWnd ) ) {
// Mouse is inside container
if ( !insideContainer ) {
// was out, now in
insideContainer = true;
OnMouseEnter( EventArgs.Empty );
}
}
else {
// Mouse is outside container
if ( insideContainer ) {
// was in, now out
insideContainer = false;
OnMouseLeave( EventArgs.Empty );
}
}
}
return false;
}
protected virtual void OnMouseEnter( EventArgs e ) {
var handler = MouseEnter;
handler?.Invoke( this, e );
}
protected virtual void OnMouseLeave( EventArgs e ) {
var handler = MouseLeave;
handler?.Invoke( this, e );
}
}
In the following usage example, we want to monitor mouse entry and exit for a Panel
and the child controls that it contains:
public partial class Form1 : Form {
private readonly ContainerMessageFilter containerMessageFilter;
public Form1() {
InitializeComponent();
containerMessageFilter = new ContainerMessageFilter( panel1 );
containerMessageFilter.MouseEnter += ContainerMessageFilter_MouseEnter;
containerMessageFilter.MouseLeave += ContainerMessageFilter_MouseLeave;
Application.AddMessageFilter( containerMessageFilter );
}
private static void ContainerMessageFilter_MouseLeave( object sender, EventArgs e ) {
Console.WriteLine( "Leave" );
}
private static void ContainerMessageFilter_MouseEnter( object sender, EventArgs e ) {
Console.WriteLine( "Enter" );
}
private void Form1_FormClosed( object sender, FormClosedEventArgs e ) {
Application.RemoveMessageFilter( containerMessageFilter );
}
}