2009-03-16 6 views
0

J'ai les gestionnaires d'événements OnMouseEnter et OnMouseLeave configurés pour mon formulaire. Lorsque la souris se déplace sur le formulaire, je veux définir l'opacité à 100% et quand il s'éloigne, je veux le mettre à 25%. Cela fonctionne bien, sauf lorsque la souris se déplace sur l'un des boutons du formulaire. L'événement OnMouseLeave déclenche et masque à nouveau le formulaire. Existe-t-il un bon moyen de gérer cela, sans avoir à câbler OnMouseEnter pour chaque contrôle sur le formulaire?OnMouseEnter pour tous les contrôles d'un formulaire

Répondre

2

EDIT: Je vais laisser cette réponse ici, même si elle ne peut pas fonctionner de manière fiable. La raison: empêcher quelqu'un d'autre d'essayer la même chose. Voir la fin du message pour la raison que cela ne fonctionnera pas.

Vous pouvez le faire assez facilement pour le rectangle client en obtenant la position du curseur et vérifier pour voir si elle est dans l'espace client de formulaire:

private void Form1_MouseLeave(object sender, EventArgs e) 
{ 
    Point clientPos = PointToClient(Cursor.Position); 
    if (!ClientRectangle.Contains(clientPos)) 
    { 
     this.Opacity = 0.25; 
    } 
} 

Cela suppose qu'aucun de vos contrôles enfants va changer la opacité. Cependant, vous trouverez que c'est une solution moins que parfaite, car lorsque la souris va à la barre de titre, le formulaire va à 0.25%. Vous pouvez corriger cela en vérifiant si la position de la souris est dans la fenêtre rect (en utilisant la propriété Bounds), mais alors votre fenêtre restera opaque si la souris quitte la barre de titre et sort de la fenêtre.

Vous rencontrez un problème similaire lorsque vous entrez dans la barre de titre depuis l'extérieur.

Je pense que vous devrez gérer les messages WM_NCMOUSEENTER et WM_NCMOUSELEAVE afin de rendre ce travail fiable. Pourquoi cela ne peut-il pas fonctionner? Même la gestion des notifications de zones non-clients peut échouer. Il est possible que la souris entre dans un contrôle enfant, ce qui empêcherait le formulaire d'être notifié.

2

Je pense que c'est impossible à faire, sans gérer les événements MouseEnter et MouseLeave de tous les enfants, mais vous n'avez pas à les câbler manuellement.

Voici un code que j'ai copié & collé à partir d'un de mes projets. Il fait presque ce que vous avez décrit ici. J'ai effectivement copié l'idée et le cadre de this site.

Dans le constructeur, j'appelle AttachMouseOnChildren() pour attacher les événements. Les OnContainerEnter et OnContainerLeave sont utilisés pour gérer la souris entrant/quittant le formulaire lui-même.

#region MouseEnter & Leave 

    private bool _childControlsAttached = false; 

    /// <summary> 
    /// Attach enter & leave events to child controls (recursive), this is needed for the ContainerEnter & 
    /// ContainerLeave methods. 
    /// </summary> 
    private void AttachMouseOnChildren() { 
     if (_childControlsAttached) { 
      return; 
     } 
     this.AttachMouseOnChildren(this.Controls); 
     _childControlsAttached = true; 
    } 

    /// <summary> 
    /// Attach the enter & leave events on a specific controls collection. The attachment 
    /// is recursive. 
    /// </summary> 
    /// <param name="controls">The collection of child controls</param> 
    private void AttachMouseOnChildren(System.Collections.IEnumerable controls) { 
     foreach (Control item in controls) { 
      item.MouseLeave += new EventHandler(item_MouseLeave); 
      item.MouseEnter += new EventHandler(item_MouseEnter); 
      this.AttachMouseOnChildren(item.Controls); 
     } 
    } 

    /// <summary> 
    /// Will be called by a MouseEnter event, with any of the controls within this 
    /// </summary> 
    void item_MouseEnter(object sender, EventArgs e) { 
     this.OnMouseEnter(e); 
    } 

    /// <summary> 
    /// Will be called by a MouseLeave event, with any of the controls within this 
    /// </summary> 
    void item_MouseLeave(object sender, EventArgs e) { 
     this.OnMouseLeave(e); 
    } 

    /// <summary> 
    /// Flag if the mouse is "entered" in this control, or any of its children 
    /// </summary> 
    private bool _containsMouse = false; 

    /// <summary> 
    /// Is called when the mouse entered the Form, or any of its children without entering 
    /// the form itself first. 
    /// </summary> 
    protected void OnContainerEnter(EventArgs e) { 
     // No longer transparent 
     this.Opacity = 1; 
    } 

    /// <summary> 
    /// Is called when the mouse leaves the form. When the mouse leaves the form via one of 
    /// its children, this will also call OnContainerLeave 
    /// </summary> 
    /// <param name="e"></param> 
    protected void OnContainerLeave(EventArgs e) { 
     this.Opacity = DEFAULT_OPACITY; 
    } 

    /// <summary> 
    /// <para>Is called when a MouseLeave occurs on this form, or any of its children</para> 
    /// <para>Calculates if OnContainerLeave should be called</para> 
    /// </summary> 
    protected override void OnMouseLeave(EventArgs e) { 
     Point clientMouse = PointToClient(Control.MousePosition); 
     if (!ClientRectangle.Contains(clientMouse)) { 
      this._containsMouse = false; 
      OnContainerLeave(e); 
     } 
    } 
    /// <summary> 
    /// <para>Is called when a MouseEnter occurs on this form, or any of its children</para> 
    /// <para>Calculates if OnContainerEnter should be called</para> 
    /// </summary> 
    protected override void OnMouseEnter(EventArgs e) { 
     if (!this._containsMouse) { 
      _containsMouse = true; 
      OnContainerEnter(e); 
     } 
    } 

    #endregion 
+0

Le seul problème est qu'il définit l'opacité à DEFAULT_OPACITY lorsque vous passez la souris sur la barre de titre. – Samuel

0

Je pense que d'une façon de gérer de manière fiable les événements de souris que vous êtes intéressé est de mettre en place un IMessageFilter sur votre objet Application à partir duquel vous pouvez intercepter tous les messages de souris (WM_MOUSEMOVE etc ..), même si elles sont envoyées à des contrôles de l'enfant de la forme.

est ici un code de démonstration:

using System; 
using System.Windows.Forms; 

namespace Test 
{ 
    static class Program 
    { 
     /// <summary> 
     /// The main entry point for the application. 
     /// </summary> 
     public static Form frm = null; 
     [STAThread] 
     static void Main() 
     { 
      Application.EnableVisualStyles(); 
      Application.SetCompatibleTextRenderingDefault(false); 
      frm = new Form1 {Opacity = 0.25}; 
      frm.Controls.Add(new Button{Dock = DockStyle.Fill, Text = "Ok"}); 

      Application.AddMessageFilter(new MouseMoveFilter()); 
      Application.Run(frm); 
     } 
    } 

    public class MouseMoveFilter : IMessageFilter 
    { 
     #region IMessageFilter Members 
     private const int WM_MOUSELEAVE  = 0x02A3; 
     private const int WM_NCMOUSEMOVE = 0x0A0; 
     private const int WM_MOUSEMOVE  = 0x0200; 
     private const int WM_NCMOUSELEAVE = 0x2A2; 

     public bool PreFilterMessage(ref Message m) 
     { 
      switch (m.Msg) 
      { 
       case WM_NCMOUSEMOVE: 
       case WM_MOUSEMOVE: 
        Program.frm.Opacity = 1; 
        break; 

       case WM_NCMOUSELEAVE: 
       case WM_MOUSELEAVE: 
        if (!Program.frm.Bounds.Contains(Control.MousePosition)) 
         Program.frm.Opacity = 0.25; 
        break; 

      } 
      return false; 
     } 

     #endregion 
    } 
} 

Vous pouvez aussi hériter de la classe de formulaire et passer outre PreProcessMessage() pour accomplir la même chose ...

Questions connexes