Control et Mouse

Résolu
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 - 4 janv. 2006 à 14:02
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 - 7 janv. 2006 à 10:32
Bonjour,
Je suis entrain de faire un Control, appelons-le "x" (qui dérive de Usercontrol) qui devra faire certaines actions lorsque la souris est au-dessus de lui.
Mon problème, c'est que ce Control ("x") possède d'autres Control, disons "y" (en loccurance, y'a pour le moment des Labels et une PictureBox) et que lorsque ma souris passe sur l'un des "y" qui se trouve dans "x" je reçois un MouseLeave puis un MouseEnter quand la souris entre repectivement quitte "y" (mais reste toujours dans "x" !).
Et ça me pose bien-entendu tout un tas de problème...

J'aurais aimé savoir comment régler ce problème le plus proprement possible (autrement dit, comment faire pour savoir si la souris est dans un Control, y compris un des sous-Control).

(J'espère que la question était claire )
Merci d'avance aux réponses !


<HR>

[Pub] http://www.csharpfr.com/auteurdetail.aspx?ID=13319 [\Pub]
C# forever

26 réponses

cs_coq Messages postés 6349 Date d'inscription samedi 1 juin 2002 Statut Membre Dernière intervention 2 août 2014 101
6 janv. 2006 à 13:45
Un peu trop vite d'ailleurs, j'ai écrit une connerie ^^ :

public class ContainerLevelMouseEventsMessageFilter : IMessageFilter
{
public ContainerLevelMouseEventsMessageFilter(Control container)
{
if ( container = = null )
throw new ArgumentNullException("container");


this._container = container;


this._container.Disposed += new EventHandler(_container_Disposed);
}


private Control _container = null;
private bool _isEntered = false;


private const int WM_MOUSEMOVE = 0x0200;


#region Membres de IMessageFilter


public bool PreFilterMessage(ref Message m)
{
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)) )
{
if ( !this._isEntered )
{
// levée 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
Point containerPoint;
if ( ctrl != this._container )
containerPoint = this._container.PointToClient(ctrl.PointToScreen(pt));
else
containerPoint = pt;


// levée 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
if ( this._isEntered )
{
// levée de MouseLeave
this.OnMouseLeave();


this._isEntered = false;
}
}
}


return false;
}


#endregion


private void _container_Disposed(object sender, EventArgs e)
{
try
{
this._container = null;
Application.RemoveMessageFilter(this);
}
catch
{
}
}


public event MouseEventHandler MouseMove;
public event EventHandler MouseEnter;
public event EventHandler MouseLeave;


protected virtual void OnMouseMove(MouseEventArgs e)
{
if ( this.MouseMove != null )
this.MouseMove(this._container, e);
}


protected virtual void OnMouseLeave()
{
if ( this.MouseLeave != null )
this.MouseLeave(this._container, EventArgs.Empty);
}


protected virtual void OnMouseEnter()
{
if ( this.MouseEnter != null )
this.MouseEnter(this._container, EventArgs.Empty);
}
}

/*
coq
MVP Visual C#
*/
3
sebmafate Messages postés 4936 Date d'inscription lundi 17 février 2003 Statut Membre Dernière intervention 14 février 2014 37
4 janv. 2006 à 14:26
passe par un override de WndProc

Sébastien FERRAND
[MVP C#]
0
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
4 janv. 2006 à 18:25
Salut et merci de la réponse.
A vrai dire j'ai toujours eu un peu de peine avec le WndProc, comme savoir par exemple quel message m'intéresse?
Tu pourrais un peu m'aiguiller stp? Merci


<HR>

[Pub] http://www.csharpfr.com/auteurdetail.aspx?ID=13319 [\Pub]
C# forever /auteurdetail.aspx?ID=13319
0
sebmafate Messages postés 4936 Date d'inscription lundi 17 février 2003 Statut Membre Dernière intervention 14 février 2014 37
4 janv. 2006 à 19:40
WM_MOUSEMOVE

Sébastien FERRAND
[MVP C#]
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
4 janv. 2006 à 21:06
Désolé mais je vois pas trop
Pas trop l'habitude de travailler avec ça je dois dire.

Je dois faire qqch comme ça ou bien ?



protected
override
void WndProc(
ref
Message m)
{

base.WndProc(
ref m);
if (m.Msg == WM_MOUSEMOVE) // Comment savoir que vaut WM_MOUSEMOVE ??
{
// Et ici on fait quoi ?
}

}


<HR>

[Pub] http://www.csharpfr.com/auteurdetail.aspx?ID=13319 [\Pub]
C# forever /auteurdetail.aspx?ID=13319
0
sebmafate Messages postés 4936 Date d'inscription lundi 17 février 2003 Statut Membre Dernière intervention 14 février 2014 37
4 janv. 2006 à 21:55
alors...

WM_MOUSEMOVE = 0x200;
WM_MOUSELEAVE = 0x2a3;

(merci reflector ;))

Sébastien FERRAND
[MVP C#]
0
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
4 janv. 2006 à 22:00
J'ai réussi à trouver les valeurs sur le net mais je sais toujours pas ce que je dois faire avec lol.
Hum...


<HR>

[Pub] http://www.csharpfr.com/auteurdetail.aspx?ID=13319 [\Pub]
C# forever /auteurdetail.aspx?ID=13319
0
sebmafate Messages postés 4936 Date d'inscription lundi 17 février 2003 Statut Membre Dernière intervention 14 février 2014 37
4 janv. 2006 à 22:16
ok... on va faire plus complet :)



private
const
int WM_MOUSEMOVE = 0x200;

private
const
int WM_MOUSELEAVE = 0x2a3;



protected
override
void WndProc(
ref
Message m)
{

switch (m.Msg) {

case WM_MOUSELEAVE :
isMouseHover =
false;
_MouseLeave(
EventArgs.Empty);

break;

case WM_MOUSEMOVE :

if (!isMouseHover)
_MouseEnter(
EventArgs.Empty);
_MouseMove(
EventArgs.Empty);

break;
}

base.WndProc(
ref m);
}



protected
virtual
void _MouseEnter(
EventArgs e) {

Console.WriteLine(
"La souris est entr‚e dans le controle");
}



protected
virtual
void _MouseLeave(
EventArgs e) {

Console.WriteLine(
"La souris a quitt‚ le controle");
}



protected
virtual
void _MouseMove(
EventArgs e) {

Console.WriteLine(
"La souris bouge au dessus du controle");
}

Sébastien FERRAND
[MVP C#]
0
sebmafate Messages postés 4936 Date d'inscription lundi 17 février 2003 Statut Membre Dernière intervention 14 février 2014 37
4 janv. 2006 à 22:28
ooops... j'm'a trompé

case WM_MOUSEMOVE :
if (!isMouseHover) {
isMouseHover = true;
_MouseEnter(EventArgs.Empty);
}
_MouseMove(EventArgs.Empty);
break;


Sébastien FERRAND
[MVP C#]
0
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
4 janv. 2006 à 22:43
Ben si je met ce code, alors j'observe dans ma Form de test, que quand j'entre dans le Control il m'affiche correctement ce qu'il faut dans la console (il dit qu'il est entré et que la souris bouge au dessus) mais le problème est toujours le même : la souris est à l'intérieur du Control et lorsqu'elle passe sur un label (compris dans le Control) ca vient écrit que la souris a quitter le Control (hors, ce n'est pas le cas, dumoins je ne veux justement pas que ça le soit !).
Je sais pas si tu vois ?


<HR>

[Pub] http://www.csharpfr.com/auteurdetail.aspx?ID=13319 [\Pub]
C# forever /auteurdetail.aspx?ID=13319
0
cs_coq Messages postés 6349 Date d'inscription samedi 1 juin 2002 Statut Membre Dernière intervention 2 août 2014 101
4 janv. 2006 à 22:51
Le problème c'est que le message WM_MOUSEMOVE est envoyé directement au Label et non pas relayé par le contrôle.
Je pense que le salut peut être dans l'utilisation d'un filtre au niveau de l'application (IMessageFilter) avec vérification pour savoir si le contrôle destinataire est enfant du contrôle "x".
Ou carrement en mode bourrin (mais probablement moins gourmand en ressources) regarder si les coordonnées du pointeur sont dans le rectangle du contrôle "x".

/*
coq
MVP Visual C#
*/
0
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
4 janv. 2006 à 23:08
En fait, l'idée est toute simple (ce que je veux faire finalement) : quand on entre sur un Control, je veux le colorier. Et quand on sort, je veux qu'il reprenne sa forme normal. Ca marche, sauf que quand on entre sur un label qui se trouve dans ce Control ben il reprend sa forme normal alors que ma souris est toujours dans le Control.
C'est vraiment si compliqué de faire ça ? lol


<HR>


[Pub] http://www.csharpfr.com/auteurdetail.aspx?ID=13319 [\Pub]
C# forever /auteurdetail.aspx?ID=13319
0
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
5 janv. 2006 à 01:23
Salut Bidou

Comme le dit Coq le plus simple c'est encore de tester les coordonnées du pointeur.

public class MyControl : UserControl
{
public MyControl( ) : base( )
{
// Une PictureBox pour l'exemple.
PictureBox pb = new PictureBox( );
pb.BackColor = Color.Lime;
pb.Location = new Point( 10, 10 );
pb.Size = new Size( 100, 100 );
this.Controls.Add( pb );
}


protected override void OnMouseEnter( EventArgs e )
{
base.OnMouseEnter( e );


this.BackColor = Color.Red;
}


protected override void OnMouseLeave( EventArgs e )
{
base.OnMouseLeave( e );


if ( !this.ClientRectangle.Contains( this.PointToClient( Control.MousePosition ) ) )
{
this.BackColor = SystemColors.Control;
}
}
}
0
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
5 janv. 2006 à 10:11
Salut Lutinore,
Oui en fait j'avais cette solution avant que je pose la question, mais elle me déplait très fortement pour plusieurs raison. Une des raisons principale c'est qu'après je ne peux plus utiliser le tooltip (qui fonctionne avec MouseHover) : Le tooltip est displayé seulement quand on est sur le Control à proprement dit, mais pas quand on est sur un sous-Control.
Une autre raison, c'est que si je quitte brusquement le Control avec le souris, on dirait qu'il n'a pas le temps d'effectuer le OnLeave et mon Control reste en couleur, alors que le curseur est en dehors.

J'aimerais bien une solution général, qui me permet de ne pas être notifié des events qui se produisent sur un de ces sous-controls (ou autres solutions propres)
Je ne sais pas si c'est très clair ??


<HR>

[Pub] http://www.csharpfr.com/auteurdetail.aspx?ID=13319 [\Pub]
C# forever
0
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
5 janv. 2006 à 18:36
Peut être si tu abonnes tous les MouseMove des contrôles enfants sur le même évènement du contrôle parent ça te permet de connaitre la position de la souris à tout moment et de déduire si elle entre ou quite le contrôle.. Sinon implementer IMessageFilter ou alors si tu veux faire une sorte de MousePreview sur le même principe que la propriète KeyPreview faut faire un hook ( un WH_MOUSE_LL ).
0
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
6 janv. 2006 à 08:31
Moui à vrai dire je ne sais pas encore vraiment comment je vais résoudre ce problème mais je viendrai poster un message si j'y arrive...
Merci de votre aide en tout cas.


<HR>

[Pub] http://www.csharpfr.com/auteurdetail.aspx?ID=13319 [\Pub]
C# forever
0
cs_coq Messages postés 6349 Date d'inscription samedi 1 juin 2002 Statut Membre Dernière intervention 2 août 2014 101
6 janv. 2006 à 12:50
Concernant le hook je ne suis pas trop pour : inutile ici de recevoir les notifications pour l'intégralité de l'écran.

/*
coq
MVP Visual C#
*/
0
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
6 janv. 2006 à 12:55
J'arriverai au bout de mes peines avec un IMessageFilter ou je me jette dans la gueule du loup?


<HR>

[Pub] http://www.csharpfr.com/auteurdetail.aspx?ID=13319 [\Pub]
C# forever
0
cs_coq Messages postés 6349 Date d'inscription samedi 1 juin 2002 Statut Membre Dernière intervention 2 août 2014 101
6 janv. 2006 à 13:34
Bon, je t'ai rapidement illustré le fond de ma pensée avec un petit exemple, ça sera plus clair :

public class ContainerLevelMouseEventsMessageFilter : IMessageFilter
{
public ContainerLevelMouseEventsMessageFilter(Control container)
{
if ( container == null )
throw new ArgumentNullException("container");


this._container = container;


this._container.Disposed += new EventHandler(_container_Disposed);
}


private Control _container = null;
private bool _isEntered = false;


private const int WM_MOUSEMOVE = 0x0200;


#region Membres de IMessageFilter


public bool PreFilterMessage(ref Message m)
{
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)) )
{
if ( !this._isEntered )
{
// levée 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
Point containerPoint = this._container.PointToClient(ctrl.PointToScreen(pt));


// levée de MouseMove
this.OnMouseMove(new MouseEventArgs(MouseButtons.None, 0, pt.X, pt.Y, 0));
}
else
{
// si la cible n'est pas un enfant, et que la sortie n'a pas encore été notifiée
if ( this._isEntered )
{
// levée de MouseLeave
this.OnMouseLeave();


this._isEntered = false;
}
}
}


return false;
}


#endregion


private void _container_Disposed(object sender, EventArgs e)
{
try
{
this._container = null;
Application.RemoveMessageFilter(this);
}
catch
{
}
}


public event MouseEventHandler MouseMove;
public event EventHandler MouseEnter;
public event EventHandler MouseLeave;


protected virtual void OnMouseMove(MouseEventArgs e)
{
if ( this.MouseMove != null )
this.MouseMove(this._container, e);
}


protected virtual void OnMouseLeave()
{
if ( this.MouseLeave != null )
this.MouseLeave(this._container, EventArgs.Empty);
}


protected virtual void OnMouseEnter()
{
if ( this.MouseEnter != null )
this.MouseEnter(this._container, EventArgs.Empty);
}
}

Utilisation :
ContainerLevelMouseEventsMessageFilter panelLevelEvents = new ContainerLevelMouseEventsMessageFilter(this.panel1);
Application.AddMessageFilter(panelLevelEvents);
panelLevelEvents.MouseEnter += new EventHandler(panelLevelEvents_MouseEnter);
panelLevelEvents.MouseLeave += new EventHandler(panelLevelEvents_MouseLeave);
panelLevelEvents.MouseMove += new MouseEventHandler(panelLevelEvents_MouseMove);

/*
coq
MVP Visual C#
*/
0
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
6 janv. 2006 à 16:48
Coq, comme d'hab ton code est complet et précis

Au passage je remarque que Control.Contains renvoie true pour les enfants des enfants, ça tombe bien..

J'imagine qu'il ne faut pas instancier ta classe dans le constructeur du contrôle ou alors en statique sinon on se retrouve avec plusieurs filtres ?

A propos des filtres sais tu à quoi sert les nouvelles fonctions Application.FilterMessage et Application.RegisterMessageLoop !?
0
Rejoignez-nous