2011-01-10 3 views
6

Sto usando un controllo ToolTip sul mio modulo, ma ho scoperto che anche se il mio cursore si trova su un controllo, il suggerimento è mostrato altrove. Mi piacerebbe mostrarlo nel controllo su cui è puntato il mio cursore.Perché le punte dei palloncini non vengono mostrate puntando al controllo corretto?

alt text

Come mostrato nell'immagine sopra, quando il cursore si trova sopra Textbox3, il suggerimento è visualizzato sul Textbox4. Mi piacerebbe che fosse visualizzato puntando allo Textbox3.

Attualmente sto utilizzando il seguente codice per visualizzare il tooltip in 3 diversi eventi:

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

Modifica

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

Questo funziona bene, ma quando il mouse inizialmente al controllo è visualizzazione del normale se avessi per la seconda volta viene visualizzato correttamente

Guarda le immagini seguenti

alt text

alt text

risposta

22

Il problema che stai vedendo è perché del IsBalloon property vostro controllo ToolTip è impostato su "True". Con questa proprietà impostata, ToolTip non modifica la relativa posizione, causando la freccia del palloncino in modo che punti al controllo errato.

Ecco un confronto side-by-side dimostrando questo fenomeno:

               

La soluzione semplice, ovviamente, è quello di disattivare la proprietà IsBalloon impostandolo su "false". Il controllo tornerà a visualizzare una finestra tooltip standard, rettangolare, che apparirà correttamente allineata.

Se ciò non è accettabile per te, dovrai specificare la posizione esatta in cui desideri visualizzare il fumetto del suggerimento. Sfortunatamente, sembra che ci sia un bug nel controllo ToolTip che fa sì che non appaia correttamente la prima volta che viene collegato a un controllo. Questo può essere risolto generalmente chiamando una volta il metodo Show con una stringa vuota. Ad esempio, utilizzando il seguente codice:

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

produce questo risultato:

   

Naturalmente, la fortuna può variare andare questo itinerario, pure. Generalmente non utilizzo il controllo integrato ToolTip per i controlli di modifica (come caselle di testo e caselle combinate). Trovo molto più affidabile P/Invoke SendMessage, che specifica EM_SHOWBALLOONTIP e un EDITBALLOONTIP structure contenente informazioni sul tooltip che voglio mostrare.Lascerò la ricerca delle definizioni appropriate e scriverò il codice del wrapper come esercizio per il lettore, poiché questa risposta è già troppo lunga.

+0

Questa è la visualizzazione di un suggerimento di colore nero al posto del messaggio e anche non è anche sempre visualizzata la punta palloncino – Dotnet

+0

@Dorababu: "Questo" non mi dice molto. Nella mia risposta c'erano 3 diversi possibili soluzioni: quale hai provato? Inoltre, so che funzionano tutti perché li ho testati io stesso per prendere gli screenshot. Puoi aggiornare la tua domanda con il codice che stai utilizzando a scopo di confronto? –

+0

@ Cody Grey Ho appena usato il codice del tuo e anche impostato isBallon su false – Dotnet

0

Hey ho avuto finalmente da questo codice

Quando MouseLeave

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

Quando passaggio del mouse

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

Hai provato a utilizzare solo il metodo SetToolTip (con fuori chiamando il metodo mostra il metodo) nel MouseOver evento

ttpTemp.SetToolTip (txtTemp, "Ex: 01 (Dovrebbe essere numerico)");

Questo funziona per me (io uso Managed C++ ma penso che sia lo stesso).

5

Dopo un sacco di risoluzione dei problemi ho trovato il codice qui sotto per essere nettamente superiore alla ToolTip del fumetto incorporato. Assicurati che gli stili visivi siano abilitati rimuovendo il commento dalla dipendenza nel file manifest.

Creare un BalloonTip nel corso di un controllo TextBox come questo:

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

e implementare BalloonTip come questo:

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

Questo è come appare:

enter image description here

+0

@miroxlav '' x' e y' sono calcolati all'inizio del metodo show() in base alle coordinate di controllo. Sono coordinate dello schermo (non relative all'angolo in alto a sinistra della finestra) e sono fissati con l'invio di un messaggio di Windows 'TTM_TRACKPOSITION'. Rompere su 'x' e' y' e assicurarsi che siano le coordinate dello schermo valide in pixel. Non ho idea del perché le coordinate fallirebbero. Presumo che deve fare qualcosa con la conversione da coordinate client a schermo. È possibile aggiungere 'x' e' y' come parametri di funzione per il metodo Show(). Renderli 'short' invece di' ushort', quindi funziona con i multimonitor. – Chris

+0

Dove prendi quella classe 'SemaphoreSlim'? – Nyerguds

+0

Ah. 4.5. Riuscito a usare solo il semaforo. Sono curioso però ... come puoi chiuderlo cliccando sul tooltip attuale? – Nyerguds

1

con crediti a Chris' answer, inserisco VB.N ET porto qui:

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

E lo usiamo in questo modo: 'Dim BTT come nuovo [Lib] .Windows.BalloonTip ("Titolo", "Messaggio", mittente, [Lib] .Windows.BalloonTip.ICON.INFO, 5000)' – Fusseldieb