Les messages Windows, WM_MOUSE* dans notre cas, sont envoyés directement sur le contrôle concerné.
Si vous surchargez la méthode WndProc d'un contrôle "conteneur", vous ne verrez par conséquent pas les messages WM_MOUSEMOVE pour autant que le pointeur soit situé au dessus d'un contrôle enfant.
En gros les zones situées sous ces contrôles enfants sont des "zones mortes".
Pire, ce fonctionnement peut aussi être génant dans certains cas :
l'évènement MouseLeave sera par exemple déclenché si le curseur passe de la surface visible du contrôle conteneur à la surface d'un contrôle enfant.
Cette source est donc un exemple d'implémentation de l'interface IMessageFilter permettant d'avoir accès à ces évènements comme si il n'y avait aucun contrôle enfant à la surface du contrôle cible.
Source / Exemple :
/// <summary>
/// Implémentation de IMessageFilter permettant d'avoir accès à des events
/// MouseMove, MouseEnter et MouseLeaver au niveau du contrôle définis.
/// C'est-à-dire de recevoir l'event MouseMove même si le curseur se situe au dessus
/// d'un contrôle enfant.
/// </summary>
public class ContainerLevelMouseEventsMessageFilter : IMessageFilter
{
/// <summary>
/// Initialise une nouvelle instance de <see cref="ContainerLevelMouseEventsMessageFilter"/>.
/// </summary>
/// <param name="container">Contrôle pour lequel on veut les events.</param>
public ContainerLevelMouseEventsMessageFilter(Control container)
{
if ( container == null )
throw new ArgumentNullException("container");
this._container = container;
this._container.Disposed += new EventHandler(_container_Disposed);
}
/// <summary>
/// Contrôle pour lequel on veut les events.
/// </summary>
private Control _container = null;
/// <summary>
/// Permet de déterminer l'état de présence du pointeur sur le contrôle.
/// </summary>
private bool _isEntered = false;
/// <summary>
/// Constante WM_MOUSEMOVE. (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/mouseinput/mouseinputreference/mouseinputmessages/wm_mousemove.asp)
/// Voir winuser.h du Platform SDK.
/// </summary>
private const int WM_MOUSEMOVE = 0x0200;
#region Events exposés
/// <summary>
/// Se produit lorsque le pointeur se déplace sur le contrôle.
/// </summary>
public event MouseEventHandler MouseMove;
/// <summary>
/// Se produit lorsque le pointeur se déplace pour la première fois sur le contrôle.
/// </summary>
public event EventHandler MouseEnter;
/// <summary>
/// Se produit lorsque le contrôle se déplace pour la première fois hors du contrôle.
/// </summary>
public event EventHandler MouseLeave;
/// <summary>
/// Déclenche l'évènement <see cref="MouseMove"/>.
/// </summary>
/// <param name="e"></param>
protected virtual void OnMouseMove(MouseEventArgs e)
{
if ( this.MouseMove != null )
this.MouseMove(this._container, e);
}
/// <summary>
/// Déclenche l'évènement <see cref="MouseLeave"/>.
/// </summary>
protected virtual void OnMouseLeave()
{
if ( this.MouseLeave != null )
this.MouseLeave(this._container, EventArgs.Empty);
}
/// <summary>
/// Déclenche l'évènement <see cref="MouseEnter"/>.
/// </summary>
protected virtual void OnMouseEnter()
{
if ( this.MouseEnter != null )
this.MouseEnter(this._container, EventArgs.Empty);
}
#endregion Events exposés
public bool PreFilterMessage(ref Message m)
{
// tout se joue sur le message WM_MOUSEMOVE
if ( m.Msg == WM_MOUSEMOVE )
{
// détermine si la cible est notre contrôle ou est contenue par notre contrôle
Control ctrl = Control.FromHandle(m.HWnd);
if ( ctrl != null && (ctrl == this._container || this._container.Contains(ctrl)) )
{
// si ce message WM_MOUSEMOVE est le premier que l'on obtient,
// nous devont déclencher l'évènement MouseEnter
if ( !this._isEntered )
{
// déclenchement de MouseEnter
this.OnMouseEnter();
this._isEntered = true;
}
// récupération du point (en coordonnées du contrôle cible)
Point pt = new Point(m.LParam.ToInt32());
// conversion en coordonnée du contrôle, si nécessaire
Point containerPoint;
if ( ctrl != this._container )
containerPoint = this._container.PointToClient(ctrl.PointToScreen(pt));
else
containerPoint = pt;
// déclenchement de MouseMove
this.OnMouseMove(new MouseEventArgs(MouseButtons.None, 0, containerPoint.X, containerPoint.Y, 0));
}
else
{
// si la cible n'est pas un enfant, et que la sortie n'a pas encore été notifiée,
// nous devons déclencher MouseLeave
if ( this._isEntered )
{
// déclenchement de MouseLeave
this.OnMouseLeave();
this._isEntered = false;
}
}
}
return false;
}
private void _container_Disposed(object sender, EventArgs e)
{
try
{
this._container = null;
Application.RemoveMessageFilter(this);
}
catch
{
}
}
}
// EXEMPLE D'UTILISATION
ContainerLevelMouseEventsMessageFilter panelLevelEvents = new ContainerLevelMouseEventsMessageFilter(this.panel);
Application.AddMessageFilter(panelLevelEvents);
panelLevelEvents.MouseEnter +=new EventHandler(panelLevelEvents_MouseEnter);
panelLevelEvents.MouseLeave +=new EventHandler(panelLevelEvents_MouseLeave);
Conclusion :
WM_MOUSEMOVE :
http://msdn.microsoft.com/en-us/library/ms645616.aspx
AddMessageFilter :
http://msdn.microsoft.com/en-us/library/system.windows.forms.application.addmessagefilter.aspx
IMessageFilter :
http://msdn.microsoft.com/en-us/library/system.windows.forms.imessagefilter.aspx
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.