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

Soyez le premier à donner votre avis sur cette source.

Vue 18 504 fois - Téléchargée 649 fois

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

A voir également

Ajouter un commentaire Commentaires
Messages postés
8
Date d'inscription
mercredi 12 avril 2006
Statut
Membre
Dernière intervention
25 mars 2009

OK, je teste ça de suite, merci coq,
Bonne journée !
Messages postés
6351
Date d'inscription
samedi 1 juin 2002
Statut
Modérateur
Dernière intervention
2 août 2014
93
Salut,

En fait c'est plutôt des couples :
- Bouton gauche : WM_LBUTTONDOWN et WM_LBUTTONUP
- Bouton droit : WM_RBUTTONDOWN et WM_RBUTTONUP
- ...

Voir aussi WM_LBUTTONDBLCLK, WM_RBUTTONDBLCLK, ...
Messages postés
8
Date d'inscription
mercredi 12 avril 2006
Statut
Membre
Dernière intervention
25 mars 2009

Heu, je cherche également comment faire pour catcher un event sur MouseClick, et là, je ne trouve pas le message windows équivalent, si quelqu'un le connaissait, ça m'arrangerait :)
Merci !
Messages postés
8
Date d'inscription
mercredi 12 avril 2006
Statut
Membre
Dernière intervention
25 mars 2009

Je suis en train de le tester, et ça a l'air d'être exactement ce que je cherchais, merci beaucoup coq (et merci également à Fredibulle dont l'astuce est également utile) !
Messages postés
2
Date d'inscription
vendredi 27 février 2009
Statut
Membre
Dernière intervention
27 février 2009

Oups, petite erreur et désolé pour les BBCode en trop sur ce premier post
if ( ctrl !null && (ctrl this._container || this.Contain(_container, ctrl)) )

correction

if ( ctrl != null && this.Contain(_container, ctrl) )

Mais vous l'auriez vu ;)
Afficher les 13 commentaires

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.