为什么没有显示指向正确控件的气球提示?

时间:2011-01-10 10:12:51

标签: c# .net winforms textbox tooltip

我在表单上使用ToolTip控件,但发现即使我的光标位于一个控件上,工具提示也显示在其他位置。我想在我的光标所在的控件中显示它。

alt text

如上图所示,当我的光标结束Textbox3时,工具提示会显示在Textbox4上。我希望它显示为Textbox3

我目前正在使用以下代码在3个不同的事件中显示工具提示:

 private void txtImmediateddest_Enter(object sender, EventArgs e)
 {
     ttpDetail.Show("Ex:111000025", txtImmediateddest);
 }

 private void txtImmediateddest_MouseHover(object sender, EventArgs e)
 {
     ttpDetail.Show("Ex:111000025", txtImmediateddest);
 }

  private void txtImmediateddest_MouseUp(object sender, MouseEventArgs e)
  {
      ttpDetail.Show("Ex:111000025", txtImmediateddest, e.Location);
      //toolTipimmeddest.Show("Required & Must be 9 Digits", txtImmediateddest);
  }

编辑

 private void textBox1_MouseHover(object sender, EventArgs e)
    {
        ttpDetail.AutoPopDelay = 2000;
        ttpDetail.InitialDelay = 1000;
        ttpDetail.ReshowDelay = 500;
        ttpDetail.IsBalloon = true;
        //ttpDetail.SetToolTip(textBox1, "Ex:01(Should be Numeric)");
        ttpDetail.Show("Ex : 01(Should Be Numeric)", textBox1,textBox1.Width, textBox1.Height/10,5000);
    }

这样可以正常工作但是当鼠标最初接到控件时它会显示正常,如果我第二次显示它正确显示

查看以下图片

alt text

alt text

5 个答案:

答案 0 :(得分:23)

您遇到的问题是因为ToolTip控件的IsBalloon property设置为“True”。设置此属性后,ToolTip不会更改其相对位置,导致气球的箭头指向错误的控件。

这是一个并列比较,证明了这种现象:

显然,简单的修复方法是通过将IsBalloon属性设置为“False”来禁用它。控件将恢复显示标准的矩形工具提示窗口,该窗口将正确对齐。

如果您无法接受,则必须指定工具提示气球显示的确切位置。不幸的是,ToolTip控件中存在一个错误,导致它在第一次连接到控件时无法正确显示。通常可以通过使用空字符串调用Show方法一次来修复此问题。例如,使用以下代码:

private void txtImmediateddest_Enter(object sender, EventArgs e)
{
    ttpDetail.Show(string.Empty, textBox3, 0);
    ttpDetail.Show("Ex:111000025", textBox3, textBox3.Width / 2, textBox3.Height, 5000);
}

产生这个结果:

当然,你的运气也可能因此而异。我通常不使用内置的ToolTip控件来编辑控件(例如文本框和组合框)。我发现P / Invoke SendMessage更加可靠,指定EM_SHOWBALLOONTIPEDITBALLOONTIP structure包含有关我想要显示的工具提示的信息。我将留下查找适当的定义并将包装器代码编写为读者的练习,因为这个答案已经太久了。

答案 1 :(得分:5)

经过大量的故障排除后,我发现代码明显优于内置气球工具提示。通过取消注释清单文件中的依赖项来确保启用了视觉样式。





在TextBox上创建一个BalloonTip,如下所示:


&#xA ;
  new BalloonTip(“Title”,“Message”,textBox1,BalloonTip.ICON.INFO,5000);
  
&#xA;&#xA; < p>并实现 BalloonTip ,如下所示:

&#xA;&#xA;
  using System;&#xA; using System.Collections.Generic;&#xA ;使用System.Linq;&#xA;使用System.Text;&#xA;使用System.Windows.Forms;&#xA;使用System.Runtime.InteropServices;&#xA;&#xA;命名空间Lib.Windows&# XA; {&#XA; BalloonTip&#xA; {&#XA; private System.Timers.Timer timer = new System.Timers.Timer();&#xA;私人SemaphoreSlim信号量=新的SemaphoreSlim(1);&#xA;私人IntPtr hWnd;&#xA;&#xA; public BalloonTip(string text,Control control)&#xA; {&#XA;显示(“”,文本,控制);&#xA; }&#XA;&#XA; public BalloonTip(string title,string text,Control control,ICON icon = 0,double timeOut = 0,bool focus = false)&#xA; {&#XA;显示(标题,文字,控制,图标,时间,焦点);&#xA; }&#XA;&#XA; void显示(字符串标题,字符串文本,控件控件,ICON图标= 0,double timeOut = 0,bool focus = false,短x = 0,短y = 0)&#xA; {&#XA; if(x == 0&amp;&amp; y == 0)&#xA; {&#XA; x =(短)(control.RectangleToScreen(control.ClientRectangle).Left + control.Width / 2);&#xA; y =(短)(control.RectangleToScreen(control.ClientRectangle).Top + control.Height / 2);&#xA; }&#XA; TOOLINFO toolInfo = new TOOLINFO();&#xA; toolInfo.cbSize =(uint)Marshal.SizeOf(toolInfo);&#xA; toolInfo.uFlags = 0x20; // TTF_TRACK&#xA; toolInfo.lpszText = text;&#xA; IntPtr pToolInfo = Marshal.AllocCoTaskMem(Marshal.SizeOf(toolInfo));&#xA; Marshal.StructureToPtr(toolInfo,pToolInfo,false);&#xA; byte [] buffer = Encoding.UTF8.GetBytes(title);&#xA; buffer = buffer.Concat(new byte [] {0})。ToArray();&#xA; IntPtr pszTitle = Marshal.AllocCoTaskMem(buffer.Length);&#xA; Marshal.Copy(缓冲区,0,pszTitle,buffer.Length);&#xA; hWnd = User32.CreateWindowEx(0x8,“tooltips_class32”,“”,0xC3,0,0,0,control.Parent.Handle,(IntPtr)0,(IntPtr)0,(IntPtr)0);&#xA ; User32.SendMessage(hWnd,1028,(IntPtr)0,pToolInfo); // TTM_ADDTOOL&#xA; User32.SendMessage(hWnd,1042,(IntPtr)0,(IntPtr)((ushort)x |((ushort)y&lt;&lt; 16))); // TTM_TRACKPOSITION&#xA; //User32.SendMessage(hWnd,1043,(IntPtr)0,(IntPtr)0); // TTM_SETTIPBKCOLOR&#xA; //User32.SendMessage(hWnd,1044,(IntPtr)0xffff,(IntPtr)0); // TTM_SETTIPTEXTCOLOR&#xA; User32.SendMessage(hWnd,1056,(IntPtr)icon,pszTitle); // TTM_SETTITLE 0:无,1:信息,2:警告,3:错误,&gt; 3:假设为hIcon。 ; 1057 for Unicode&#xA; User32.SendMessage(hWnd,1048,(IntPtr)0,(IntPtr)500); // TTM_SETMAXTIPWIDTH&#xA; User32.SendMessage(hWnd,0x40c,(IntPtr)0,pToolInfo); // TTM_UPDATETIPTEXT; 0x439用于Unicode&#xA; User32.SendMessage(hWnd,1041,(IntPtr)1,pToolInfo); // TTM_TRACKACTIVATE&#xA; Marshal.FreeCoTaskMem(pszTitle);&#XA; Marshal.DestroyStructure(pToolInfo,typeof(TOOLINFO));&#xA; Marshal.FreeCoTaskMem(pToolInfo);&#XA; if(focus)&#xA; control.Focus();&#XA; //取消注释以在用户更改焦点时关闭气球,&#xA; //开始输入,调整/移动父窗口,最小化父窗口等&#xA; //根据显示气球提示的控件&#xA;&#xA;调整要订阅的控制事件。 /*control.Click + = control_Event;&#xA; control.Leave + = control_Event;&#xA; control.TextChanged + = control_Event;&#xA; control.LocationChanged + = control_Event;&#xA; control.SizeChanged + = control_Event;&#xA; control.VisibleChanged + = control_Event;&#xA;控制父= control.Parent;&#xA; while(parent!= null)&#xA; {&#XA; parent.VisibleChanged + = control_Event;&#xA; parent = parent.Parent;&#xA; }&#XA; control.TopLevelControl.LocationChanged + = control_Event;&#xA; ((Form)control.TopLevelControl).Deactivate + = control_Event; * /&#xA;&#xA; timer.AutoReset = false;&#xA; timer.Elapsed + = timer_Elapsed;&#xA; if(timeOut&gt; 0)&#xA; {&#XA; timer.Interval = timeOut;&#xA; timer.Start();&#XA; }&#XA; }&#XA;&#XA; void timer_Elapsed(object sender,System.Timers.ElapsedEventArgs e)&#xA; {&#XA;关闭();&#XA; }&#XA;&#XA; void control_Event(object sender,EventArgs e)&#xA; {&#XA;关闭();&#XA; }&#XA;&#XA; void Close()&#xA; {&#XA; if(!semaphore.Wait(0))//确保只执行一次&#xA;返回;&#XA; timer.Elapsed  -  = timer_Elapsed;&#xA; timer.Close();&#XA; User32.SendMessage(hWnd,0x0010,(IntPtr)0,(IntPtr)0); // WM_CLOSE&#xA; //User32.SendMessage(hWnd,0x0002,(IntPtr)0,(IntPtr)0); // WM_DESTROY&#xA; //User32.SendMessage(hWnd,0x0082,(IntPtr)0,(IntPtr)0); // WM_NCDESTROY&#xA; }&#XA;&#XA; [StructLayout(LayoutKind.Sequential)]&#XA; struct TOOLINFO&#xA; {&#XA; public uint cbSize;&#xA; public uflags;&#xA; public IntPtr hwnd;&#xA; public IntPtr uId;&#xA;公共RECT rect;&#xA; public IntPtr hinst;&#xA; [的MarshalAs(UnmanagedType.LPStr)]&#XA; public string lpszText;&#xA; public IntPtr lParam;&#xA; }&#XA; [StructLayout(LayoutKind.Sequential)]&#XA; struct RECT&#xA; {&#XA; public int Left;&#xA; public int Top;&#xA; public int Right;&#xA; public int Bottom;&#xA; }&#XA;&#XA; public enum ICON&#xA; {&#XA; NONE,&#XA; INFO,&#XA;警告,&#XA;错误&#XA; }&#XA; }&#XA;&#XA;静态类User32&#xA; {&#XA; [DllImportAttribute( “USER32.DLL”)]&#XA; public static extern int SendMessage(IntPtr hWnd,UInt32 Msg,IntPtr wParam,IntPtr lParam);&#xA; [DllImportAttribute( “USER32.DLL”)]&#XA; public static extern IntPtr CreateWindowEx(uint dwExStyle,string lpClassName,string lpWindowName,uint dwStyle,int x,int y,int nWidth,int nHeight,IntPtr hWndParent,IntPtr hMenu,IntPtr hInstance,IntPtr LPVOIDlpParam);&#xA; }&#xA;}&#xA;  
&#xA;&#xA;

它的外观如下:

&#xA;&#xA;

&#xA;

答案 2 :(得分:3)

您是否尝试仅在 MouseOver 事件中使用 SetToolTip 方法(不使用显示方法)

ttpTemp.SetToolTip(txtTemp,“Ex:01(应为数字)”);

这对我来说很好(我使用Managed C ++,但我认为它是一样的。)

答案 3 :(得分:1)

使用Chris' answer的积分,我在这里发布了VB.NET端口:

Imports System.Collections.Generic
Imports System
Imports System.Linq
Imports System.Text
Imports System.Windows.Forms
Imports System.Runtime.InteropServices

Namespace [Lib].Windows
    Class BalloonTip
        Private timer As New System.Timers.Timer()
        Private semaphore As New System.Threading.SemaphoreSlim(1)
        Private hWnd As IntPtr

        Public Sub New(text As String, control As Control)
            Show("", text, control)
        End Sub

        Public Sub New(title As String, text As String, control As Control, Optional icon As ICON = 0, Optional timeOut As Double = 0, Optional focus As Boolean = False)
            Show(title, text, control, icon, timeOut, focus)
        End Sub

        Private Sub Show(title As String, text As String, control As Control, Optional icon As ICON = 0, Optional timeout As Double = 0, Optional focus As Boolean = False)
            Dim x As UShort = CType(control.RectangleToScreen(control.ClientRectangle).Left + control.Width / 2, UShort)
            Dim y As UShort = CType(control.RectangleToScreen(control.ClientRectangle).Top + control.Height / 2, UShort)
            Dim toolInfo As New TOOLINFO()
            toolInfo.cbSize = CType(Marshal.SizeOf(toolInfo), UInteger)
            toolInfo.uFlags = &H20
            ' TTF_TRACK
            toolInfo.lpszText = text
            Dim pToolInfo As IntPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(toolInfo))
            Marshal.StructureToPtr(toolInfo, pToolInfo, False)
            Dim buffer As Byte() = Encoding.UTF8.GetBytes(title)
            buffer = buffer.Concat(New Byte() {0}).ToArray()
            Dim pszTitle As IntPtr = Marshal.AllocCoTaskMem(buffer.Length)
            Marshal.Copy(buffer, 0, pszTitle, buffer.Length)
            hWnd = User32.CreateWindowEx(&H8, "tooltips_class32", "", &HC3, 0, 0, _
                0, 0, control.Parent.Handle, CType(0, IntPtr), CType(0, IntPtr), CType(0, IntPtr))
            User32.SendMessage(hWnd, 1028, CType(0, IntPtr), pToolInfo)
            ' TTM_ADDTOOL
            'User32.SendMessage(hWnd, 1043, CType(0, IntPtr), CType(0, IntPtr); ' TTM_SETTIPBKCOLOR
            'User32.SendMessage(hWnd, 1044, CType(&HFFFF, IntPtr), CType(0, IntPtr); ' TTM_SETTIPTEXTCOLOR
            User32.SendMessage(hWnd, 1056, CType(icon, IntPtr), pszTitle)
            ' TTM_SETTITLE 0:None, 1:Info, 2:Warning, 3:Error, >3:assumed to be an hIcon. ; 1057 for Unicode
            User32.SendMessage(hWnd, 1048, CType(0, IntPtr), CType(500, IntPtr))
            ' TTM_SETMAXTIPWIDTH
            User32.SendMessage(hWnd, &H40C, CType(0, IntPtr), pToolInfo)
            ' TTM_UPDATETIPTEXT; 0x439 for Unicode
            User32.SendMessage(hWnd, 1042, CType(0, IntPtr), CType(x Or (CUInt(y) << 16), IntPtr))
            ' TTM_TRACKPOSITION
            User32.SendMessage(hWnd, 1041, CType(1, IntPtr), pToolInfo)
            ' TTM_TRACKACTIVATE
            Marshal.FreeCoTaskMem(pszTitle)
            Marshal.DestroyStructure(pToolInfo, GetType(TOOLINFO))
            Marshal.FreeCoTaskMem(pToolInfo)
            If focus Then
                control.Focus()
            End If

            ' uncomment below to make balloon close when user changes focus,
            ' starts typing, resizes/moves parent window, minimizes parent window, etc
            ' adjust which control events to subscribe to depending on the control over which the balloon tip is shown
            'AddHandler control.Click, AddressOf control_Event
            'AddHandler control.Leave, AddressOf control_Event
            'AddHandler control.TextChanged, AddressOf control_Event
            'AddHandler control.LocationChanged, AddressOf control_Event
            'AddHandler control.SizeChanged, AddressOf control_Event
            'AddHandler control.VisibleChanged, AddressOf control_Event
            'Dim parent As Control = control.Parent
            'While Not (parent Is Nothing)
            '    AddHandler parent.VisibleChanged, AddressOf control_Event
            '    parent = parent.Parent
            'End While
            'AddHandler control.TopLevelControl.LocationChanged, AddressOf control_Event
            'AddHandler DirectCast(control.TopLevelControl, Form).Deactivate, AddressOf control_Event
            timer.AutoReset = False
            RemoveHandler timer.Elapsed, AddressOf timer_Elapsed
            If timeout > 0 Then
                timer.Interval = timeout
                timer.Start()
            End If
        End Sub

        Private Sub timer_Elapsed(sender As Object, e As System.Timers.ElapsedEventArgs)
            Close()
        End Sub

        Private Sub control_Event(sender As Object, e As EventArgs)
            Close()
        End Sub

        Sub Close()
            If Not semaphore.Wait(0) Then
                ' ensures one time only execution
                Return
            End If
            RemoveHandler timer.Elapsed, AddressOf timer_Elapsed
            timer.Close()
            User32.SendMessage(hWnd, &H10, CType(0, IntPtr), CType(0, IntPtr))
            ' WM_CLOSE
            'User32.SendMessage(hWnd, &H0002, CType(0, IntPtr), CType(0, IntPtr)); ' WM_DESTROY
            'User32.SendMessage(hWnd, &H0082, CType(0, IntPtr), CType(0, IntPtr)); ' WM_NCDESTROY
        End Sub

        <StructLayout(LayoutKind.Sequential)> _
        Private Structure TOOLINFO
            Public cbSize As UInteger
            Public uFlags As UInteger
            Public hwnd As IntPtr
            Public uId As IntPtr
            Public rect As RECT
            Public hinst As IntPtr
            <MarshalAs(UnmanagedType.LPStr)> _
            Public lpszText As String
            Public lParam As IntPtr
        End Structure
        <StructLayout(LayoutKind.Sequential)> _
        Private Structure RECT
            Public Left As Integer
            Public Top As Integer
            Public Right As Integer
            Public Bottom As Integer
        End Structure

        Public Enum ICON
            NONE
            INFO
            WARNING
            [ERROR]
        End Enum
    End Class

    NotInheritable Class User32
        Private Sub New()
        End Sub
        <DllImportAttribute("user32.dll")> _
        Public Shared Function SendMessage(hWnd As IntPtr, Msg As UInt32, wParam As IntPtr, lParam As IntPtr) As Integer
        End Function
        <DllImportAttribute("user32.dll")> _
        Public Shared Function CreateWindowEx(dwExStyle As UInteger, lpClassName As String, lpWindowName As String, dwStyle As UInteger, x As Integer, y As Integer, _
            nWidth As Integer, nHeight As Integer, hWndParent As IntPtr, hMenu As IntPtr, hInstance As IntPtr, LPVOIDlpParam As IntPtr) As IntPtr
        End Function
    End Class
End Namespace

答案 4 :(得分:0)

嘿,我最后得到了这段代码

当MouseLeave

       public class MouseLeave
    {
        public void mouseLeave(TextBox txtTemp, ToolTip ttpTemp)
        {
            ttpTemp.Hide(txtTemp);
        }
    }

鼠标悬停时

    public class MouseOver
    {
        public void mouseOver(TextBox txtTemp, ToolTip ttpTemp)
        {
            switch (txtTemp.Name)
            {
                case "textBox1":
                    {

                        ttpTemp.AutoPopDelay = 2000;
                        ttpTemp.InitialDelay = 1000;
                        ttpTemp.ReshowDelay = 500;
                        ttpTemp.IsBalloon = true;
                        ttpTemp.SetToolTip(txtTemp, "Ex:01(Should be Numeric)");
                        ttpTemp.Show("Ex : 01(Should Be Numeric)", txtTemp, txtTemp.Width, txtTemp.Height / 10, 5000);
                    }
                    break;

                case "txtDetail":
                    {

                        ttpTemp.AutoPopDelay = 2000;
                        ttpTemp.InitialDelay = 1000;
                        ttpTemp.ReshowDelay = 500;
                        ttpTemp.IsBalloon = true;
                        ttpTemp.SetToolTip(txtTemp, "Ex:01(Should be Numeric)");
                        ttpTemp.Show("Ex : 01(Should Be Numeric)", txtTemp, txtTemp.Width, txtTemp.Height / 10, 5000);
                    }
                    break;
            }
        }
    }