2011-01-10 5 views
6

Ich verwende ein Steuerelement ToolTip auf meinem Formular, aber habe festgestellt, dass der Tooltip woanders angezeigt wird, obwohl sich mein Cursor auf einem Steuerelement befindet. Ich möchte dies innerhalb der Kontrolle zeigen, auf der sich mein Cursor befindet.Warum werden keine Ballonspitzen auf die richtige Kontrolle gezeigt?

alt text

Wie oben im Bild gezeigt, wenn mein Cursor über Textbox3 ist, zeigt die Tooltip Textbox4 auf. Ich möchte, dass es bei Textbox3 angezeigt wird.

Ich bin derzeit mit dem folgenden Code den Tooltip in 3 verschiedenen Ereignissen anzuzeigen:

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

bearbeitet

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

Dies funktioniert gut, aber wenn die Maus zunächst auf das Gerät eingeordnet Anzeige der normalen wenn ich zum zweiten Mal hatte es korrekt angezeigt

Schauen Sie sich die folgenden Bilder

alt text

alt text

Antwort

22

Das Problem ist, die Sie sehen, weil Ihr ToolTip Kontrolle IsBalloon property auf "True" gesetzt ist. Wenn diese Eigenschaft festgelegt ist, ändert die ToolTip ihre relative Position nicht, was dazu führt, dass der Pfeil der Sprechblase auf das falsche Steuerelement zeigt.

Hier ist ein Side-by-Side-Vergleich dieses Phänomen zeigt:

               

Die einfache Lösung ist natürlich, die IsBalloon Eigenschaft zu deaktivieren, indem Sie sie auf "Falsch". Das Steuerelement kehrt zu einem standardmäßigen, rechteckigen Tooltip-Fenster zurück, das korrekt ausgerichtet aussieht.

Wenn das für Sie nicht akzeptabel ist, müssen Sie den genauen Ort angeben, an dem die Quickinfo-Sprechblase angezeigt werden soll. Unglücklicherweise scheint es einen Fehler im Steuerelement ToolTip zu geben, der bewirkt, dass es beim ersten Verbinden mit einem Steuerelement nicht richtig angezeigt wird. Dies kann in der Regel durch einmaliges Aufrufen der Methode Show mit einem leeren String behoben werden. Zum Beispiel mit dem folgenden Code:

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

produziert dieses Ergebnis:

   

Natürlich Ihr Glück variieren kann diesen Weg gehen, wie gut. Im Allgemeinen verwende ich nicht das integrierte Steuerelement ToolTip für Bearbeitungssteuerelemente (z. B. Textfelder und Comboboxen). Ich finde es ist viel zuverlässiger P/Invoke SendMessage, EM_SHOWBALLOONTIP und eine EDITBALLOONTIP structure mit Informationen über die Tooltip, die ich zeigen möchte angeben.Ich werde die passenden Definitionen nachschlagen und den Wrapper-Code als Übung für den Leser schreiben, da diese Antwort schon viel zu lang ist.

+0

Dies ist eine schwarze Farbe Tooltip anstelle der Meldung angezeigt wird und auch der Ballonspitze wird auch nicht angezeigt bekommen – Dotnet

+0

@Dorababu: „Dieses“ gefällt mir nicht viel erzählen. Es gab 3 verschiedene mögliche Umgehungslösungen in meiner Antwort: Welche hast du versucht? Außerdem weiß ich, dass sie alle arbeiten, weil ich sie selbst getestet habe, um die Screenshots zu machen. Können Sie Ihre Frage mit dem Code aktualisieren, den Sie für Vergleichszwecke verwenden? –

+0

@ Cody Gray Ich habe nur den Code von Ihnen verwendet und auch isBallon auf false gesetzt – Dotnet

0

Hey ich habe zuletzt durch diesen Code

Wenn Leave

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

Wenn Mouseover

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

Haben Sie nur die SetToolTip Methode zu verwenden versucht (mit heraus aufrufen die zeigen Methode) in der MouseOver Ereignis

ttpTemp.SetToolTip (txtTemp, "Beispiel: 01 (sollte numerisch sein)");

Das funktioniert gut für mich (ich verwende Managed C++, aber ich denke, es ist das gleiche).

5

Nach einer Menge von Fehlersuche fand ich den Code unten, um besser zu sein als die eingebaute QuickInfo. Stellen Sie sicher, dass Visual Styles aktiviert sind, indem Sie die Abhängigkeit in der Manifestdatei auskommentieren.

erstellen Balloontip über einen TextBox wie folgt aus:

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

und implementieren BalloonTip wie folgt aus:

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

Dies ist, wie es aussieht:

enter image description here

+0

@miroxlav 'x' und' y' werden am Anfang der Show() - Methode basierend auf den Koordinaten des Steuerelements berechnet. Sie sind Bildschirmkoordinaten (nicht relativ zur oberen linken Ecke des Fensters) und werden durch Senden einer Windows-Nachricht "TTM_TRACKPOSITION" gesetzt. Brechen Sie 'x' und' y' auf und stellen Sie sicher, dass es sich um gültige Bildschirmkoordinaten in Pixeln handelt. Ich habe keine Ahnung, warum die Koordinaten versagen würden. Ich gehe davon aus, dass es etwas mit der Konvertierung von Client zu Bildschirmkoordinaten zu tun hat. Sie könnten 'x' und' y' als Funktionsparameter für die Show() -Methode hinzufügen. Machen Sie sie 'short' anstelle von' ushort', damit es mit Multimonitors funktioniert. – Chris

+0

Wo bekommen Sie diese SemaphoreSlim-Klasse? – Nyerguds

+0

Ah. 4.5. Verwaltet, um nur Semaphore zu verwenden. Ich bin aber neugierig ... wie kannst du es machen, wenn du auf den Tooltip klickst? – Nyerguds

1

Mit Credits zu Chris' answer, verbinde ich VB.N ET-Port hier:

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 
+0

Und wir verwenden es so: 'Dim btt wie neu [Lib] .Windows.BalloonTip (" Titel "," Nachricht ", Absender, [Lib] .Windows.BalloonTip.ICON.INFO, 5000)' – Fusseldieb

Verwandte Themen