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

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

替代文字

如上图所示,当我的光标位于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); } 

这工作正常,但当鼠标初始到控件时它显示正常,如果我有第二次它正确显示

请看下面的图片

替代文字

替代文字

您看到的问题是因为您的ToolTip控件的IsBalloon属性设置为“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控件来编辑控件(例如文本框和combobox)。 我发现P / Invoke SendMessage更可靠,指定EM_SHOWBALLOONTIP和包含我想要显示的工具提示信息的EDITBALLOONTIP结构 。 我将留下查找适当的定义并将包装器代码编写为读者的练习,因为这个答案已经太久了。

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

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

 new BalloonTip("Title", "Message", textBox1, BalloonTip.ICON.INFO, 5000); 

并实现BalloonTip如下所示:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; namespace Lib.Windows { class BalloonTip { private System.Timers.Timer timer = new System.Timers.Timer(); private SemaphoreSlim semaphore = new SemaphoreSlim(1); private IntPtr hWnd; public BalloonTip(string text, Control control) { Show("", text, control); } public BalloonTip(string title, string text, Control control, ICON icon = 0, double timeOut = 0, bool focus = false) { Show(title, text, control, icon, timeOut, focus); } void Show(string title, string text, Control control, ICON icon = 0, double timeOut = 0, bool focus = false, short x = 0, short y = 0) { if (x == 0 && y == 0) { x = (short)(control.RectangleToScreen(control.ClientRectangle).Left + control.Width / 2); y = (short)(control.RectangleToScreen(control.ClientRectangle).Top + control.Height / 2); } TOOLINFO toolInfo = new TOOLINFO(); toolInfo.cbSize = (uint)Marshal.SizeOf(toolInfo); toolInfo.uFlags = 0x20; // TTF_TRACK toolInfo.lpszText = text; IntPtr pToolInfo = Marshal.AllocCoTaskMem(Marshal.SizeOf(toolInfo)); Marshal.StructureToPtr(toolInfo, pToolInfo, false); byte[] buffer = Encoding.UTF8.GetBytes(title); buffer = buffer.Concat(new byte[] { 0 }).ToArray(); IntPtr pszTitle = Marshal.AllocCoTaskMem(buffer.Length); Marshal.Copy(buffer, 0, pszTitle, buffer.Length); hWnd = User32.CreateWindowEx(0x8, "tooltips_class32", "", 0xC3, 0, 0, 0, 0, control.Parent.Handle, (IntPtr)0, (IntPtr)0, (IntPtr)0); User32.SendMessage(hWnd, 1028, (IntPtr)0, pToolInfo); // TTM_ADDTOOL User32.SendMessage(hWnd, 1042, (IntPtr)0, (IntPtr)((ushort)x | ((ushort)y << 16))); // TTM_TRACKPOSITION //User32.SendMessage(hWnd, 1043, (IntPtr)0, (IntPtr)0); // TTM_SETTIPBKCOLOR //User32.SendMessage(hWnd, 1044, (IntPtr)0xffff, (IntPtr)0); // TTM_SETTIPTEXTCOLOR User32.SendMessage(hWnd, 1056, (IntPtr)icon, pszTitle); // TTM_SETTITLE 0:None, 1:Info, 2:Warning, 3:Error, >3:assumed to be an hIcon. ; 1057 for Unicode User32.SendMessage(hWnd, 1048, (IntPtr)0, (IntPtr)500); // TTM_SETMAXTIPWIDTH User32.SendMessage(hWnd, 0x40c, (IntPtr)0, pToolInfo); // TTM_UPDATETIPTEXT; 0x439 for Unicode User32.SendMessage(hWnd, 1041, (IntPtr)1, pToolInfo); // TTM_TRACKACTIVATE Marshal.FreeCoTaskMem(pszTitle); Marshal.DestroyStructure(pToolInfo, typeof(TOOLINFO)); Marshal.FreeCoTaskMem(pToolInfo); if (focus) control.Focus(); // uncomment bellow 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 /*control.Click += control_Event; control.Leave += control_Event; control.TextChanged += control_Event; control.LocationChanged += control_Event; control.SizeChanged += control_Event; control.VisibleChanged += control_Event; Control parent = control.Parent; while(parent != null) { parent.VisibleChanged += control_Event; parent = parent.Parent; } control.TopLevelControl.LocationChanged += control_Event; ((Form)control.TopLevelControl).Deactivate += control_Event;*/ timer.AutoReset = false; timer.Elapsed += timer_Elapsed; if (timeOut > 0) { timer.Interval = timeOut; timer.Start(); } } void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { Close(); } void control_Event(object sender, EventArgs e) { Close(); } void Close() { if (!semaphore.Wait(0)) // ensures one time only execution return; timer.Elapsed -= timer_Elapsed; timer.Close(); User32.SendMessage(hWnd, 0x0010, (IntPtr)0, (IntPtr)0); // WM_CLOSE //User32.SendMessage(hWnd, 0x0002, (IntPtr)0, (IntPtr)0); // WM_DESTROY //User32.SendMessage(hWnd, 0x0082, (IntPtr)0, (IntPtr)0); // WM_NCDESTROY } [StructLayout(LayoutKind.Sequential)] struct TOOLINFO { public uint cbSize; public uint uFlags; public IntPtr hwnd; public IntPtr uId; public RECT rect; public IntPtr hinst; [MarshalAs(UnmanagedType.LPStr)] public string lpszText; public IntPtr lParam; } [StructLayout(LayoutKind.Sequential)] struct RECT { public int Left; public int Top; public int Right; public int Bottom; } public enum ICON { NONE, INFO, WARNING, ERROR } } static class User32 { [DllImportAttribute("user32.dll")] public static extern int SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); [DllImportAttribute("user32.dll")] 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); } } 

这就是它的样子:

在此处输入图像描述

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

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

这对我来说很好(我使用托管C ++,但我认为它是相同的)。

有了回答Chris的答案 ,我在这里发布了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  _ 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  _ Public lpszText As String Public lParam As IntPtr End Structure  _ 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  _ Public Shared Function SendMessage(hWnd As IntPtr, Msg As UInt32, wParam As IntPtr, lParam As IntPtr) As Integer End Function  _ 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 

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

当MouseLeave

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

鼠标hover时

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