2
votes

I would like to show a tooltip, as soon as the mouse cursor enters the vertical scrollbar of a System.Windows.Forms.ListView. For a DataGridView it would be easy, as there is a reference to the scrollbars for which I could hanlde the MouseEnter/ MouseLeave events.

The MouseMove event of the ListView isn't even triggered, when the mouse is over the scrollbars. Is there any solution for this problem?

2

2 Answers

1
votes

The ControlWatcher class raises events when the mouse pointer enters or leaves a control which is being "watched". It allows you to catch mouse events of multiple controls, or stop catching these events.

public partial class Form1 : Form
{
    public ToolTip toolTip { get; set; }

    public Form1()
    {
        InitializeComponent();
        toolTip = new ToolTip();

        var watcher = new ControlWatcher();
        watcher.MouseEnter += watcher_MouseEnter;
        watcher.MouseLeave += watcher_MouseLeave;
        watcher.AddControl(listView1);
        watcher.StartListening();
    }

    void watcher_MouseEnter(Control control)
    {
        var pt = control.PointToClient(Cursor.Position);
        toolTip.Show("Super tooltip", control, pt);
    }

    void watcher_MouseLeave(Control control)
    {
        toolTip.Hide(control);

    }
}

public class ControlWatcher : IMessageFilter
{
    public event Action<Control> MouseEnter;
    public event Action<Control> MouseLeave;

    private Control lastControl;

    private List<Control> controls;

    public ControlWatcher()
    {
        controls = new List<Control>();
    }

    public void StartListening()
    {
        Application.AddMessageFilter(this);
    }

    public void StopListening()
    {
        Application.RemoveMessageFilter(this);
    }

    public void AddControl(Control c)
    {
        controls.Add(c);
    }

    private bool IsMouseOverControl(Control control)
    {
        var pt = control.PointToClient(Cursor.Position);
        var isOver = (pt.X >= 0 && pt.Y >= 0 && pt.X <= control.Width && pt.Y <= control.Height);
        return isOver;
    }

    private void OnMouseEnter(Control control)
    {
        if (MouseEnter != null)
            MouseEnter(control);
    }

    private void OnMouseLeave(Control control)
    {
        if (MouseLeave != null)
            MouseLeave(control);
    }

    public bool PreFilterMessage(ref Message m)
    {
        if (m.Msg != 675 && m.Msg!=512)
        {
            return false;
        }
        //the control under the cursor
        var actControl = controls.FirstOrDefault(IsMouseOverControl);

        if (lastControl != null && actControl == null)
        {
            OnMouseLeave(lastControl);
            lastControl = null;
            return false;
        }

        if (lastControl==null && actControl != null && actControl != lastControl)
        {
            lastControl = actControl;
            OnMouseEnter(actControl);
            return false;
        }
        return false;
    }
}
0
votes

Just as an addition to the accepted answer, here is the method I use to detect if the mouse is over the horizontal, the vertical or no scrollbar:

    private ScrollBars IsMouseOverScrollbar(ListView dlv)
    {
        var pt = dlv.PointToClient(Cursor.Position);
        var isOverScrollbar = (pt.X >= 0 && pt.Y >= 0 && pt.X <= dlv.Width && pt.Y <= dlv.Height) && !dlv.ClientRectangle.Contains(pt);

        if (isOverScrollbar)
        {
            // the mouse is over some scrollbar
            if (dlv.Bounds.Width - pt.X > dlv.Bounds.Height - pt.Y)
            {
                // mouse pointer is within the horizontal scrollbar
                return ScrollBars.Horizontal;
            }
            else
            {
                // mouse pointer is within the vertical scrollbar
                return ScrollBars.Vertical;
            }
        }

        return ScrollBars.None;
    }