如何在禁用的按钮上显示工具提示?

39

如果在 Windows 窗体中有一个禁用的按钮,如何在鼠标悬停时显示工具提示以告诉用户该按钮被禁用的原因?


2
如果功能改变为禁用时仍显示工具提示,那就太好了。这是帮助用户的最佳时机!我决定保持按钮启用状态,并显示一个消息框来解释为什么不会执行该操作。唉。 - greg
4个回答

15

将按钮(或任何适合此场景的控件)放置在容器中(面板、表格面板),并将工具提示关联到相应的基础面板单元格。它在许多场景下都非常出色、灵活。Tip:使单元格足够大以容纳按钮,这样鼠标悬停响应(工具提示显示)就不会似乎“渗”出按钮边界。


不错的想法,意味着你将控制计数翻了一倍。 - Sam Mackrill
2
嘿@SamMackrill(对不起回复晚了,但为了记录),确实如此,但应该仅适用于需要它的控件(通常只有几个),避免了许多不必要的代码/复杂性,使实现可视化,以便非开发人员(例如设计师)可以实现/维护。创建已经以这种方式配置的自定义控件作为包可能会很方便。 - galaxis
5
这是目前最优雅的答案。如果你需要在TableLayoutPanel单元格上方显示工具提示,你需要在该单元格中放置一个面板,然后把你的控件放在面板中,因为只有包含的控件可以被引用,而TableLayoutPanel单元格本身无法被引用。 - Aaroninus
我将工具提示添加到包含禁用按钮的GroupBox中。这意味着每当用户在禁用按钮附近悬停时,工具提示都会弹出,但我认为这不会成为问题。 - Skyfish
听起来你可能遇到了我回答中提到的“出血”问题;请确保包含面板不要比禁用按钮更大。 - galaxis

12

我已经根据BobbyShaftoe的答案进行了修改,使其更加普遍适用

注意事项:

  • MouseMove事件必须在父控件上设置(在我的情况下是一个面板)

private void TimeWorks_MouseMove(object sender, MouseEventArgs e)
{
    var parent = sender as Control;
    if (parent==null)
    {
        return;
    }
    var ctrl = parent.GetChildAtPoint(e.Location);
    if (ctrl != null && !ctrl.Enabled)
    {
        if (ctrl.Visible && toolTip1.Tag==null)
        {
            var tipstring = toolTip1.GetToolTip(ctrl);
            toolTip1.Show(tipstring, ctrl, ctrl.Width / 2, ctrl.Height / 2);
            toolTip1.Tag = ctrl;
        }
    }
    else
    {
        ctrl = toolTip1.Tag as Control;
        if (ctrl != null)
        {
            toolTip1.Hide(ctrl);
            toolTip1.Tag = null;
        }
    }

}

1
小的美容修正:在您的第二个if中,您可能希望添加条件“&&!ctrl.Enabled”,以便在快速切换到实际处于活动状态的另一个子控件时正确消失(需要特殊布局才能重现)。 - Damian Vogel

11

感谢Sam Mackrill的答案,利用标签知道你正在离开哪个控件是一个好主意。然而根据BobbyShaftoe的回答,你仍然需要 IsShown 标志。如果鼠标位置错误,在工具提示出现在其下方时,它可能会触发另一个 MouseMove 事件(即使您没有实际移动鼠标)。这可能会导致一些不必要的动画效果,因为工具提示不断消失和重新出现。

这是我的代码:

    private bool toolTipShown = false;
    private void TimeWorks_MouseMove(object sender, MouseEventArgs e)
    {
        var parent = sender as Control;
        if (parent == null)
        {
            return;
        }
        var ctrl = parent.GetChildAtPoint(e.Location);
        if (ctrl != null)
        {
            if (ctrl.Visible && toolTip1.Tag == null)
            {
                if (!toolTipShown)
                {
                    var tipstring = toolTip1.GetToolTip(ctrl);
                    toolTip1.Show(tipstring.Trim(), ctrl, ctrl.Width / 2, ctrl.Height / 2);
                    toolTip1.Tag = ctrl;
                    toolTipShown = true;
                }
            }
        }
        else
        {
            ctrl = toolTip1.Tag as Control;
            if (ctrl != null)
            {
                toolTip1.Hide(ctrl);
                toolTip1.Tag = null;
                toolTipShown = false;
            }
        }
    }

每次鼠标移动都触发这个操作,会有任何资源问题吗? - AidanO
当控件在TabControl中时无法工作。GetChildAtPoint()返回的是TabControl而不是子元素。 - doublehelix
@PeterSmartt:你所描述的情况只发生在 parent 控件本身具有 toolTip 时,否则来自 @SamMackrill 的代码对我而言就行得通。@flixfe:如果你正在使用 TabControl,则可以这样做:if (ctrl.GetType().IsInstanceOfType(typeof(TabControl))) ctrl = ((TabControl)ctrl).SelectedTab; 以获取当前选定的标签页。 - Damian Vogel
经过几个小时的尝试,我在我的情况下找不到令人满意的解决方案。最终我通过使用“MessageBox”解决了我的问题。然而,我认为在某些情况下,所提出的ToolTip是一种优雅的解决方案。 - Damian Vogel

8

假设你的控件名为button1,你可以像这样操作。
由于事件不会从控件触发,所以必须通过处理窗体的MouseMove事件来实现。

bool IsShown = false;      

void Form1_MouseMove(object sender, MouseEventArgs e)
{
   Control ctrl = this.GetChildAtPoint(e.Location);

   if (ctrl != null)
   {
       if (ctrl == this.button1 && !IsShown)
       {
           string tipstring = this.toolTip1.GetToolTip(this.button1);                 
           this.toolTip1.Show(tipstring, this.button1, this.button1.Width /2, 
                                                       this.button1.Height / 2);
           IsShown = true;
       }
   }
   else
   {
       this.toolTip1.Hide(this.button1);
       IsShown = false;
   }

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接