Imessagefilter : événements mousemove / mouseenter / mouseleave au niveau d'un contrôle, sans tenir compte des contrôles enf

Description

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

Codes Sources

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.