2011-01-10 22 views
6

Estoy usando un control ToolTip en mi formulario, pero he descubierto que, aunque mi cursor está en un control, la información sobre herramientas se muestra en otro lugar. Me gustaría mostrar esto dentro del control que tiene mi cursor.¿Por qué no se muestran las puntas de los globos apuntando hacia el control correcto?

alt text

Como se muestra en la imagen de arriba, cuando mi cursor está sobre Textbox3, la información sobre herramientas que se muestre en Textbox4. Me gustaría que se muestre apuntando al Textbox3.

Actualmente estoy usando el siguiente código para mostrar la información sobre herramientas en 3 eventos diferentes:

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

Editar

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

Esto funciona bien, pero cuando el ratón inicialmente en que el control es mostrando la normalidad si tuviera por segunda vez, se muestra correctamente

Mire las siguientes imágenes

alt text

alt text

Respuesta

22

El problema que se está viendo es porque se establece de IsBalloon property su mando a ToolTip en "True". Con este conjunto de propiedades, el ToolTip no cambia su ubicación relativa, lo que hace que la flecha del globo apunte al control incorrecto.

He aquí una comparación lado a lado de la demostración de este fenómeno:

               

La solución simple, obviamente, es deshabilitar la propiedad IsBalloon estableciéndolo en "Falso". El control volverá a mostrar una ventana de información sobre herramientas estándar y rectangular, que se verá correctamente alineada.

Si eso no es aceptable para usted, tendrá que especificar la ubicación exacta donde desea que aparezca el globo de información sobre herramientas. Desafortunadamente, parece que hay un error en el control ToolTip que hace que no aparezca correctamente la primera vez que se conecta a un control. Esto generalmente se puede solucionar llamando al método Show con una cadena vacía una vez. Por ejemplo, usando el siguiente código:

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 este resultado:

   

Por supuesto, su suerte puede variar por ese camino, también. Por lo general, no utilizo el control incorporado ToolTip para controles de edición (como cuadros de texto y cuadros combinados). Encuentro que es mucho más confiable para P/Invoke SendMessage, especificando EM_SHOWBALLOONTIP y un EDITBALLOONTIP structure que contiene información sobre la información sobre herramientas que quiero mostrar.Dejaré de buscar las definiciones apropiadas y escribiré el código del contenedor como un ejercicio para el lector, ya que esta respuesta ya es demasiado larga.

+0

Sirve para mostrar la información sobre herramientas de color negro en lugar del mensaje y también está consiguiendo tampoco aparece la punta del balón – Dotnet

+0

@Dorababu: "Este" no me dice mucho. Hubo 3 soluciones posibles diferentes en mi respuesta: ¿cuál probaste? Además, sé que todos funcionan porque los probé yo mismo para tomar capturas de pantalla. ¿Puede actualizar su pregunta con el código que está utilizando para fines de comparación? –

+0

@ Cody Gray Acabo de usar el código tuyo y también configuré isBallon en falso – Dotnet

0

Hey me dieron en el pasado por este código

Cuando MouseLeave

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

al pasar el 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

¿Ha tratado de usar sólo el método SetToolTip (con a llamar el muestra el método) en el MouseOver evento

ttpTemp.SetToolTip (txtTemp, "Ej: 01 (Should be Numeric)");

Esto funciona bien para mí (uso Managed C++ pero creo que es el mismo).

5

Después de una gran cantidad de solución de problemas, encontré que el código de abajo es superior a la información sobre herramientas incorporada en el globo. Asegúrese de que los Estilos visuales estén habilitados al descomentar la dependencia en el archivo de manifiesto.

Crear un BalloonTip sobre un cuadro de texto así:

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

e implementar BalloonTip así:

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

Esto es como se ve:

enter image description here

+0

@miroxlav 'x' e' y' se calculan al principio del método Show() basado en las coordenadas del control. Son coordenadas de pantalla (no relativas a la esquina superior izquierda de la ventana) y se configuran mediante el envío de un mensaje de Windows 'TTM_TRACKPOSITION'. Rompe 'x' y' y' y asegúrese de que las coordenadas de la pantalla sean válidas en píxeles. No tengo idea de por qué fallarían las coordenadas. Supongo que tiene que hacer algo con la conversión del cliente a las coordenadas de la pantalla. Puede agregar 'x' y' y' como parámetros de función para el método Show(). Hazlos 'cortos' en lugar de 'ushort' para que funcionen con multimonitores. – Chris

+0

¿De dónde sacas esa clase 'SemaphoreSlim'? – Nyerguds

+0

Ah. 4.5. Administrado para usar solo semáforo. Aunque tengo curiosidad ... ¿cómo puedes cerrarlo haciendo clic en la información sobre herramientas actual? – Nyerguds

1

Con créditos a Chris' answer, publico VB.N puerto ET aquí:

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

Y lo usamos así: 'Dim btt Como nuevo [Lib] .Windows.BalloonTip (" Título "," Mensaje ", remitente, [Lib] .Windows.BalloonTip.ICON.INFO, 5000)' – Fusseldieb

Cuestiones relacionadas