使 WinForms 工具提示在一个控件上多次显示

本文关键字:控件 一个 显示 工具提示 WinForms | 更新日期: 2023-09-27 18:34:08

我正在尝试在自定义绘制(使用 GDI+)的 (WinForms) 用户控件上使用 WinForms Tooltip 类。这是遗留代码,但我需要再维护几年。我希望当光标在各个位置暂停时显示工具提示。我不想进行计算以了解是否应该在光标暂停之前显示工具提示,这有助于确定 Popup 事件中的信息。在下面的非工作示例代码中,我希望我可以将光标移动到窗体上的任何角落并看到工具提示。似乎如果我单击以删除工具提示,则从此看不到工具提示。要显示的第一个工具提示并不像我期望的那样直接。我该如何完成这项工作?

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;
        }
    }
}

使 WinForms 工具提示在一个控件上多次显示

不是很优雅,但您可以使用Timer来跟踪鼠标在控件中的空闲时间,并在 Elapsed 事件中更新工具提示。

例:

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);
                    }
                }
            )
        );
    }
}

通过取消PopUp事件,您可以显式阻止显示ToolTip,因为PopUp发生在实际显示ToolTip之前。 每次窗体触发显示工具提示e.Cancel = true都会受到点击。

我不确定您对e.Cancel的意图,但我想您只想在文本为 null 或不在其中一个有效显示区域中时将其设置为 true。将另一个其他添加到您的条件链中,并将e.Cancel作为 catch-all,它应该对应于您不想在其中显示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
}

这应该是最简单和最快的修复。

我认为

比你的方法void OnTooltipPopup(object sender, PopupEventArgs e)您缺少以下行

_tooltip.Show(text, _lp);

它应该强制工具提示无论如何显示。

此外,为了避免在此方法上计数位置,请尝试从System.Drawning的"画图"中测试击中该区域。它在上面的链接中描述了MSDN:msdn