You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Framework中调用ToolTip.Show()时ToolTip.Popup事件未触发的问题排查与解决咨询

.NET Framework中调用ToolTip.Show()时ToolTip.Popup事件未触发的问题排查与解决咨询

我完全理解你的困扰——在.NET Framework中调用ToolTip.Show()时Popup事件不触发的问题确实是框架实现上的差异,和.NET 8的行为不一致,这和官方文档描述的内容有出入,并不是你哪里操作错了。

从你的测试和提供的示例代码来看,这个问题在.NET Framework 4.6.2到4.8版本中普遍存在:当你通过显式调用ToolTip.Show()方法来显示自定义绘制的工具提示时,Popup事件并不会像文档承诺的那样被触发;而同样的代码在.NET 8中却能正常触发该事件。这应该属于.NET Framework中ToolTip组件的一个已知行为差异(或者说未修复的小bug)。

针对这个问题,我给你提供几个在.NET Framework环境下可行的解决方案:

方案一:改用GetToolTip事件结合自动显示逻辑

这个方案遵循框架的设计规范,不需要手动调用Show()方法,而是让ToolTip组件自动处理显示时机,这样Popup事件就能正常触发。

  1. 先给TreeView控件绑定ToolTip:
public FormTreeNodeToolTip()
{
    InitializeComponent();
    treeView1.Nodes.Add(new TreeNode("First node"));
    treeView1.Nodes.Add(new TreeNode("Second node with longer text"));
    treeView1.Nodes.Add(new TreeNode("Third node"));
    // 给TreeView绑定ToolTip
    toolTip1.SetToolTip(treeView1, string.Empty);
}
  1. 处理ToolTip.GetToolTip事件,动态返回每个节点的提示文本:
private void toolTip1_GetToolTip(object sender, GetToolTipEventArgs e)
{
    if (e.Control == treeView1)
    {
        Point mousePos = treeView1.PointToClient(Cursor.Position);
        TreeNode currentNode = treeView1.GetNodeAt(mousePos);
        
        if (currentNode != null)
        {
            e.ToolTipText = currentNode.Text; // 替换为你的自定义提示文本
            _currentNode = currentNode;
        }
        else
        {
            e.ToolTipText = string.Empty;
        }
    }
}
  1. 此时Popup事件会在工具提示自动显示时正常触发,你可以继续在该事件中设置尺寸:
private void ToolTip1_Popup(object sender, PopupEventArgs e)
{
    if (_currentNode != null)
    {
        e.ToolTipSize = TextRenderer.MeasureText(_currentNode.Text, _font);
    }
}

方案二:手动计算尺寸并通过反射调整ToolTip内部参数(适合必须用显式Show()的场景)

如果你必须保留手动调用Show()的逻辑,可以通过反射提前设置ToolTip的内部尺寸,绕过Popup事件不触发的问题:

private void TreeView1_NodeMouseHover(object sender, TreeNodeMouseHoverEventArgs e)
{
    _currentNode = e.Node;
    string tipText = e.Node.Text;
    // 预先计算所需的提示框尺寸
    Size requiredSize = TextRenderer.MeasureText(tipText, _font);
    
    // 通过反射修改ToolTip的内部currentSize字段
    var toolTipField = toolTip1.GetType().GetField("currentSize", BindingFlags.NonPublic | BindingFlags.Instance);
    if (toolTipField != null)
    {
        toolTipField.SetValue(toolTip1, requiredSize);
    }
    
    Point cursorPos = treeView1.PointToClient(Cursor.Position);
    toolTip1.Show(tipText, treeView1, cursorPos.X + 20, cursorPos.Y + 20, 800);
}

注意:反射依赖于框架内部实现细节,虽然在.NET Framework 4.8中稳定,但如果未来框架有更新可能失效。

方案三:自定义无边框提示窗体(最灵活可靠的长期方案)

如果上述方案都无法满足你的需求,完全抛弃ToolTip组件,自己实现一个自定义提示窗体是最彻底的解决方式,你可以完全控制它的外观、大小和显示逻辑:

  1. 创建一个自定义Form:
public class CustomToolTipForm : Form
{
    private string _tipText;
    private Font _drawFont;

    public CustomToolTipForm(string text, Font font)
    {
        _tipText = text;
        _drawFont = font;
        FormBorderStyle = FormBorderStyle.None;
        BackColor = Color.LightYellow;
        ShowInTaskbar = false;
        StartPosition = FormStartPosition.Manual;
        // 计算窗体大小(加边距)
        Size textSize = TextRenderer.MeasureText(text, font);
        Size = new Size(textSize.Width + 8, textSize.Height + 4);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        e.Graphics.DrawString(_tipText, _drawFont, Brushes.Blue, new PointF(4, 2));
    }
}
  1. 在TreeView事件中使用这个窗体:
private CustomToolTipForm _currentTipForm;

private void TreeView1_MouseMove(object sender, MouseEventArgs e)
{
    TreeNode node = treeView1.GetNodeAt(e.Location);
    if (node != _currentNode)
    {
        _currentNode = node;
        // 隐藏并释放旧的提示窗体
        _currentTipForm?.Hide();
        _currentTipForm?.Dispose();
        
        if (node != null)
        {
            // 创建并显示新的提示窗体
            _currentTipForm = new CustomToolTipForm(node.Text, _font);
            Point screenPos = treeView1.PointToScreen(new Point(e.X + 20, e.Y + 20));
            _currentTipForm.Location = screenPos;
            _currentTipForm.Show(this);
        }
    }
}

private void TreeView1_MouseLeave(object sender, EventArgs e)
{
    _currentTipForm?.Hide();
    _currentTipForm?.Dispose();
    _currentTipForm = null;
    _currentNode = null;
}

这个方案完全脱离了原生ToolTip组件的限制,能实现任何你需要的自定义效果。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 06:53:01