Почему не отображаются подсказки, указывающие на правильное управление? - PullRequest
7 голосов
/ 10 января 2011

Я использую элемент управления 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 ]

23 голосов
/ 10 января 2011

Проблема, с которой вы сталкиваетесь, заключается в том, что свойство IsBalloon вашего ToolTip элемента управления установлено в значение "True".С этим установленным свойством ToolTip не меняет своего относительного местоположения, в результате чего стрелка воздушного шара указывает на неправильный элемент управления.

Вот параллельное сравнение, демонстрирующее это явление:

image image

Простое решение, очевидно, состоит в том, чтобы отключить свойство 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_SHOWBALLOONTIP и EDITBALLOONTIP структуру , содержащую информацию о нужной мне всплывающей подсказкепоказывать.Я оставлю поиск соответствующих определений и написание кода-обертки в качестве упражнения для читателя, так как этот ответ уже слишком длинный.

5 голосов
/ 05 мая 2015

После многих ошибок я обнаружил, что приведенный ниже код не превосходит встроенную всплывающую подсказку. Убедитесь, что визуальные стили включены, раскомментировав зависимость в файле манифеста.

Создайте всплывающую подсказку над текстовым полем, например:

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

Вот как это выглядит:

enter image description here

3 голосов
/ 12 июня 2014

Вы пытались использовать только метод SetToolTip (без вызова метода show ) в событии MouseOver

ttpTemp.SetToolTip (txtTemp, «Пример: 01 (должен быть числовым)»);

Это прекрасно работает для меня (я использую Managed C ++, но думаю, что это то же самое).

1 голос
/ 05 июня 2015

С кредитами на Ответ Криса , я выкладываю порт 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
0 голосов
/ 11 января 2011

Эй, наконец-то я получил этот код

Когда 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;
            }
        }
    }
...