We help IT Professionals succeed at work.

Get Mouse Enter event and mouse leave event to fire C# Winforms

gsdevEE
gsdevEE asked
on
I have a combobox dropdownlist (A 3rd party control that allows you to get the height of the dropdown area) as well as a 3rd Party ToolTip control (this is needed instead of windows as it allows for the tip to show combobox list items as the are hovered over).

The issue I am having involves that when you hover over an item and quickly move the mouse away, the tooltip for the last item pops up.

I need to be able to determine if the mouse cursor is still inside the dropdoiwn area, and if it isnt cancel the tooltip pop-up event.

I tried doing this a few ways, first by getting a panel, setting the location, width and hieght to that of the dropdown area, and then attaching mouse enter and leave events, setting a bool on enter and on leave to determine if the mouse was still in the area, yet they do not fire as expected - then I read that the mouse enter and leave isnt exactly what I am looking for - I found this comment on the web suggesting another idea...

"You could iterate through the control tree and add MouseEnter and
MouseLeave events to every single control. Then do the same for every
single ControlAdded and ControlRemoved, adding/removing all four event
bindings to every new control added anywhere in the tree. Just recurse
through the control.Controls collection."

I dont know if this is the way to go, but at this point I am willing to try anything that works - does anyone know how to accomplish this or another way to determine if the mouse cursor has entered/left a control area such as a panel ??
Comment
Watch Question

Kyle AbrahamsSenior .Net Developer

Commented:
create a shared instance of the tooltrip control

where you set the visibility to true (or cause the control to popup), set the shared instance to that control (eg: keep a reference to the last  control tip used)

Then in your page_mouse_move event, detect if the mouse is in the popup or the control.  If it is, do nothing, if not, set the last control to visible = false.

Author

Commented:
i am sorry, not quite sure I understand - also, you should know that the tooltip is not windows and it's Location property is not accessable - can you write out what you mean ?

Author

Commented:
have you used this technique before ?
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
You can Implement IMessageFilter and trap the WM_MOUSMOVE message at the application level (before it gets routed to the current form/control).  Track when the tooltip appears and only check for if the cursor position has left the target area when it is open.

See IMessageFilter:
http://msdn.microsoft.com/en-us/library/system.windows.forms.imessagefilter.aspx

Author

Commented:
ged - I do not understand your solution, can you write it out, or at least give me some idea in code ?
Idle Mind - I tried to use IMessage idea, a little to much - I came up with this, but it is not working - do you have any suggestions (can you please show code for your ideas, thanks!)

//Panel  created to capture mouse events after looking at your suggestion and examples

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;


namespace UIComponents.Supplementary
{
   
    public class GSMousePanel : Panel
    {

        protected override void OnControlAdded(ControlEventArgs e)
        {
            AttachToControl(e.Control);
            base.OnControlAdded(e);
        }
        protected override void OnControlRemoved(ControlEventArgs e)
        {
            DetachFromControl(e.Control);
            base.OnControlRemoved(e);
        }
        private void AttachToControl(Control c)
        {
            c.MouseMove += new MouseEventHandler(Child_MouseMove);
            c.ControlAdded += new ControlEventHandler(Child_ControlAdded);
            c.ControlRemoved += new ControlEventHandler(Child_ControlRemoved);
            AttachToChildren(c);
        }
        private void AttachToChildren(Control parent)
        {
            foreach (Control child in parent.Controls)
            {
                AttachToControl(child);
            }
        }
        private void DetachFromControl(Control c)
        {
            DetachFromChildren(c);
            c.MouseMove -= new MouseEventHandler(Child_MouseMove);
            c.ControlAdded -= new ControlEventHandler(Child_ControlAdded);
            c.ControlRemoved -= new ControlEventHandler(Child_ControlRemoved);
        }
        private void DetachFromChildren(Control parent)
        {
            foreach (Control child in parent.Controls)
            {
                DetachFromControl(child);
            }
        }
        private void Child_ControlAdded(object sender, ControlEventArgs e)
        {
            AttachToControl(e.Control);
        }
        private void Child_ControlRemoved(object sender, ControlEventArgs e)
        {
            DetachFromControl(e.Control);
        }
        private void Child_MouseMove(object sender, MouseEventArgs e)
        {
            Point pt = e.Location;
            Control child = sender as Control;
            do
            {
                pt.Offset(child.Left, child.Top);
                child = child.Parent;
            }
            while (child != this);
            MouseEventArgs newArgs = new MouseEventArgs(e.Button, e.Clicks, pt.X, pt.Y, e.Delta);
            OnMouseMove(newArgs);
        }
    }
}

Code useed on form (control is addedd to panel, panel's size is set to match dropdown area, and then set back)

        bool mouseCaptured{get;set;}
        private void gsMousePanel1_MouseLeave(object sender, EventArgs e)
        {
            mouseCaptured = false;
            this.gsMousePanel1.Capture = false;
            hidePanel();
        }
        private void gsMousePanel1_MouseEnter(object sender, EventArgs e)
        {
            mouseCaptured = true;
            this.gsMousePanel1.Capture = true;
        }
                private void expandPanel()
        {
            gsMousePanel1.Location = _sectionChoice.Location;
            gsMousePanel1.Width = _sectionChoice.DropDownList.Width+10;
            gsMousePanel1.Height = _sectionChoice.DropDownList.Height+10;
            gsMousePanel1.Controls.Add(_sectionChoice.DropDownList);
        }
        private void hidePanel()
        {
            gsMousePanel1.Controls.Remove(_sectionChoice.DropDownList);
            gsMousePanel1.Location = _sectionChoice.Location;
            gsMousePanel1.Width = 0;
            gsMousePanel1.Height = 0;
        }

and then put a check in the tooltip popup event to check the bool i am setting, no dice though
High School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009
Commented:
Here's a simple example (I hope):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{

    public partial class Form1 : Form
    {

        private PanelEnterExit pnlMonitor;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            pnlMonitor = new PanelEnterExit(this.panel1);
            pnlMonitor.PanelEnter += new PanelEnterExit.PanelEntered(pnlMonitor_PanelEnter);
            pnlMonitor.PanelExit += new PanelEnterExit.PanelExited(pnlMonitor_PanelExit);
            Application.AddMessageFilter(pnlMonitor);
        }

        void pnlMonitor_PanelEnter(Panel pnl)
        {
            label1.Text = "Panel Entered";
        }

        void pnlMonitor_PanelExit(Panel pnl)
        {
            label1.Text = "Panel Exited";

            // do something else?
            if (comboBox1.DroppedDown)
            {
                comboBox1.DroppedDown = false; // close the combobox dropdown when the mouse leaves the panel
            }
        }

    }

    public partial class PanelEnterExit : IMessageFilter
    {

        public delegate void PanelEntered(Panel pnl);
        public delegate void PanelExited(Panel pnl);

        public event PanelEntered PanelEnter;
        public event PanelExited PanelExit;

        private Panel pnl = null;
        private bool inPanel = false;
        
        public PanelEnterExit(Panel pnl)
        {
            this.pnl = pnl;
        }

        private const int WM_MOUSEMOVE = 0x200;

        bool IMessageFilter.PreFilterMessage(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_MOUSEMOVE:
                    if (pnl != null)
                    {
                        if (pnl.RectangleToScreen(pnl.ClientRectangle).Contains(Cursor.Position))
                        {
                            if (!inPanel)
                            {
                                inPanel = true;
                                PanelEnter(this.pnl);
                            }
                        }
                        else
                        {
                            if (inPanel)
                            {
                                inPanel = false;
                                PanelExit(this.pnl);
                            }
                        }
                    }
                    break;
            }
            return false;
        }

    }

}

Open in new window

Author

Commented:
Thank, I am going to try this - quick question - will the use of IMessageFilter disturb any existing events I have for Mouse Move on other controls ??
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
You can suppress messages (prevent them from reaching the app) by returning true from the PreFilterMessage function.  Since we are always returning false (and simply raise a custom event) all messages will be passed on to the app as normal.