简体   繁体   中英

make WinForms Tooltip show multiple times on one control?

I'm trying to use the WinForms Tooltip class on a (WinForms) UserControl that is custom drawn (with GDI+). It's legacy code, but I need to maintain it a few more years. I want Tooltips to show up when the cursor is paused at various places. I don't want to do the calculation to know if I should show the tooltip until the cursor has been paused, which lends itself to determining that information in the Popup event. In the non-working sample code below, I expect that I can move the cursor to any corner on the form and see a tool tip. It seems that if I click to remove a tooltip, I don't see one ever after. And the first tool tip to show is not as immediate as I would expect. How do I make this work?

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 TestToolTip
{
    public partial class Form1 : Form
    {
        private readonly ToolTip _tooltip = new ToolTip();
        public Form1()
        {
            InitializeComponent();

            _tooltip.AutoPopDelay = 10000;
            _tooltip.InitialDelay = 1000;
            _tooltip.ReshowDelay = 200;

            _tooltip.Popup += OnTooltipPopup;
            _tooltip.SetToolTip(this, "you should never see this"); // we need something or it won't ever trigger Popup
        }

        private Point _lp;
        protected override void OnMouseMove(MouseEventArgs e)
        {
            _lp = e.Location;
            base.OnMouseMove(e);
        }

        void OnTooltipPopup(object sender, PopupEventArgs e)
        {
            string text = null;
            if (_lp.X < 100 && _lp.Y < 100)
                text = "Top Left";
            else if (_lp.X < 100 && _lp.Y > Height - 100)
                text = "Bottom Left";
            else if (_lp.X > Width - 100 && _lp.Y < 100)
                text = "Top Right";
            else if (_lp.X > Width - 100 && _lp.Y > Height - 100)
                text = "Bottom Right";

            var existing = _tooltip.GetToolTip(this);
            if (existing == text) 
                return;

            if (text != null)
                _tooltip.SetToolTip(this, text); // calls into this method

            e.Cancel = true;
        }
    }
}

Not very elegant, but you can use a Timer to track when the mouse is idle in your control, and update the tooltip in the Elapsed event.

Example:

public partial class Form1 : Form
{
    private readonly ToolTip _tooltip = new ToolTip();
    private readonly System.Timers.Timer _mouseIdleTimer = new System.Timers.Timer();

    public Form1()
    {
        InitializeComponent();

        MouseLeave += Form1_MouseLeave;
        MouseMove += Form1_MouseMove;

        _mouseIdleTimer.AutoReset = false;
        _mouseIdleTimer.Interval = 900;
        _mouseIdleTimer.Elapsed += _mouseIdleTimer_Elapsed;

        _tooltip.AutoPopDelay = 10000;
        _tooltip.InitialDelay = 1000;
        _tooltip.ReshowDelay = 200;
    }

    void Form1_MouseLeave(object sender, EventArgs e)
    {
        _mouseIdleTimer.Stop();
    }

    private Point _lp;
    void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        _lp = e.Location;

        // Mouse still moving, restart the countdown
        _mouseIdleTimer.Stop();
        _mouseIdleTimer.Start();
    }

    void _mouseIdleTimer_Elapsed(object sender, EventArgs e)
    {
        string text = null;
        if (_lp.X < 100 && _lp.Y < 100)
            text = "Top Left";
        else if (_lp.X < 100 && _lp.Y > Height - 100)
            text = "Bottom Left";
        else if (_lp.X > Width - 100 && _lp.Y < 100)
            text = "Top Right";
        else if (_lp.X > Width - 100 && _lp.Y > Height - 100)
            text = "Bottom Right";

        BeginInvoke(
            (Action)(
                () => 
                {
                    string currentText = _tooltip.GetToolTip(this);
                    if (currentText != text)
                    {
                        _tooltip.SetToolTip(this, text);
                    }
                }
            )
        );
    }
}

By canceling the PopUp event you're explicitly preventing the ToolTip from ever being displayed, since PopUp occurs prior to actually showing the ToolTip . e.Cancel = true is getting hit every time your form triggers a ToolTip to be displayed.

I'm not sure of your intentions with e.Cancel , but I imagine you'd only want to set it to true when the text is null or NOT in one of the valid display regions. Add another else to your condition chain and place e.Cancel as the catch-all, which should correspond to all the areas in which you don't want to display a ToolTip :

void OnTooltipPopup(object sender, PopupEventArgs e)
{
    string text = null;
    if (_lp.X < 100 && _lp.Y < 100)
        text = "Top Left";
    else if (_lp.X < 100 && _lp.Y > Height - 100)
        text = "Bottom Left";
    else if (_lp.X > Width - 100 && _lp.Y < 100)
        text = "Top Right";
    else if (_lp.X > Width - 100 && _lp.Y > Height - 100)
        text = "Bottom Right";
    else
        e.Cancel = true;

        var existing = _tooltip.GetToolTip(this);
        if (existing == text)
        {
            return;
        }

        if (text != null)
            _tooltip.SetToolTip(this, text); // calls into this method
}

This should be the simplest and fastest fix.

I think than in your method void OnTooltipPopup(object sender, PopupEventArgs e) your missing the following line

_tooltip.Show(text, _lp);

It should force tooltip to show anyhow.

Also to avoid counting position on this method try to test hitting the region from Paint from System.Drawning. Its describbed on MSDN at above link: msdn

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM