C#:设置工具提示气球中箭头的位置?

发布于 2024-08-17 08:51:34 字数 125 浏览 5 评论 0原文

是否可以更改气球工具提示中箭头/茎的位置? 更改的原因是因为位于屏幕顶部的按钮应该在其下方有一个工具提示。

已删除损坏的图像链接

以上是现在的情况,我只需要箭头位于气球的左上角即可。

Is it possible to change the location of the Arrow / Stem in a Balloon Tooltip?
Reason to change is because of a Button, located in the top of the screen, should have a tooltip below it.

broken image link removed

Above is the situation right now, I only need the arrow to be on the top-left side of the balloon.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

2024-08-24 08:51:34

我使用 InteropServices 调用几个 User32.dll 方法(CreateWindowEx、DestroyWindow、SetWindowPos 和 SendMessage),以使用本机 Win32 工具提示而不是 WinForms 提供的包装器。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;
using System.ComponentModel;

/// <remarks>This classes implements the balloon tooltip.
/// http://stackoverflow.com/questions/2028466
/// I hated Microsoft WinForms QA department after I had to develop my own version of the tooltip class,
/// just to workaround a bug, that would only add ~5 lines of code into the system.windows.forms.dll when fixed properly.</remarks>
class BalloonToolTip : IDisposable
{
    #region Unmanaged shit

    [DllImport( "user32.dll" )]
    static extern IntPtr CreateWindowEx( int exstyle, string classname, string windowtitle,
        int style, int x, int y, int width, int height, IntPtr parent,
        int menu, int nullvalue, int nullptr );

    [DllImport( "user32.dll" )]
    static extern int DestroyWindow( IntPtr hwnd );

    [DllImport( "user32.dll" )]
    static extern bool SetWindowPos( IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags );

    [DllImport( "user32.dll" )]
    static extern int SendMessage( IntPtr hWnd, int Msg, int wParam, IntPtr lParam );

    const int WS_POPUP = unchecked( (int)0x80000000 );
    const int TTS_BALLOON = 0x40;
    const int TTS_NOPREFIX = 0x02;
    const int TTS_ALWAYSTIP = 0x01;

    const int CW_USEDEFAULT = unchecked( (int)0x80000000 );

    const int WM_USER = 0x0400;

    IntPtr HWND_TOPMOST = new IntPtr( -1 );

    const int SWP_NOSIZE = 0x0001;
    const int SWP_NOMOVE = 0x0002;
    const int SWP_NOACTIVATE = 0x0010;

    const int WS_EX_TOPMOST = 0x00000008;

    [StructLayout( LayoutKind.Sequential )]
    public struct TOOLINFO
    {
        public int cbSize;
        public int uFlags;
        public IntPtr hwnd;
        public IntPtr id;
        private RECT m_rect;
        public IntPtr nullvalue;
        [MarshalAs( UnmanagedType.LPTStr )]
        public string text;
        public uint param;

        public Rectangle rect
        {
            get
            {
                return new Rectangle( m_rect.left, m_rect.top, m_rect.right - m_rect.left, m_rect.bottom - m_rect.top );
            }
            set
            {
                m_rect.left = value.Left;
                m_rect.top = value.Top;
                m_rect.right = value.Right;
                m_rect.bottom = value.Bottom;
            }
        }
    }

    [StructLayout( LayoutKind.Sequential )]
    struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
    }

    const int TTF_TRANSPARENT = 0x0100;
    const int TTF_TRACK = 0x0020;
    const int TTF_ABSOLUTE = 0x0080;

    #endregion

    #region Managed wrapper over some tooltip messages.
    // The most useful part of the wrappers in this section is their documentation.

    /// <summary>Send TTM_SETTITLE message to the tooltip.
    /// TODO [very low]: implement the custom icon, if needed. </summary>
    /// <param name="_wndTooltip">HWND of the tooltip</param>
    /// <param name="_icon">Standard icon</param>
    /// <param name="_title">The title string</param>
    internal static int SetTitle( IntPtr _wndTooltip, ToolTipIcon _icon, string _title )
    {
        const int TTM_SETTITLE = WM_USER + 33;

        var tempptr = IntPtr.Zero;
        try
        {
            tempptr = Marshal.StringToHGlobalUni( _title );
            return SendMessage( _wndTooltip, TTM_SETTITLE, (int)_icon, tempptr );
        }
        finally
        {
            if( IntPtr.Zero != tempptr )
            {
                Marshal.FreeHGlobal( tempptr );
                tempptr = IntPtr.Zero;
            }
        }
    }

    /// <summary>Send a message that wants LPTOOLINFO as the lParam</summary>
    /// <param name="_wndTooltip">HWND of the tooltip.</param>
    /// <param name="_msg">window message to send</param>
    /// <param name="_wParam">wParam value</param>
    /// <param name="_ti">TOOLINFO structure that goes to the lParam field. The cbSize field must be set.</param>
    internal static int SendToolInfoMessage( IntPtr _wndTooltip, int _msg, int _wParam, TOOLINFO _ti )
    {
        var tempptr = IntPtr.Zero;
        try
        {
            tempptr = Marshal.AllocHGlobal( _ti.cbSize );
            Marshal.StructureToPtr( _ti, tempptr, false );

            return SendMessage( _wndTooltip, _msg, _wParam, tempptr );
        }
        finally
        {
            if( IntPtr.Zero != tempptr )
            {
                Marshal.FreeHGlobal( tempptr );
                tempptr = IntPtr.Zero;
            }
        }
    }

    /// <summary>Registers a tool with a ToolTip control</summary>
    /// <param name="_wndTooltip">HWND of the tooltip</param>
    /// <param name="_ti">TOOLINFO structure containing information that the ToolTip control needs to display text for the tool.</param>
    /// <returns>Returns true if successful.</returns>
    internal static bool AddTool( IntPtr _wndTooltip, TOOLINFO _ti )
    {
        const int TTM_ADDTOOL = WM_USER + 50;
        int res = SendToolInfoMessage( _wndTooltip, TTM_ADDTOOL, 0, _ti );
        return Convert.ToBoolean( res );
    }

    /// <summary>Registers a tool with a ToolTip control</summary>
    /// <param name="_wndTooltip">HWND of the tooltip</param>
    /// <param name="_ti">TOOLINFO structure containing information that the ToolTip control needs to display text for the tool.</param>
    internal static void DelTool( IntPtr _wndTooltip, TOOLINFO _ti )
    {
        const int TTM_DELTOOL = WM_USER + 51;
        SendToolInfoMessage( _wndTooltip, TTM_DELTOOL, 0, _ti );
    }

    internal static void UpdateTipText( IntPtr _wndTooltip, TOOLINFO _ti )
    {
        const int TTM_UPDATETIPTEXT = WM_USER + 57;
        SendToolInfoMessage( _wndTooltip, TTM_UPDATETIPTEXT, 0, _ti );
    }

    /// <summary>Activates or deactivates a tracking ToolTip</summary>
    /// <param name="_wndTooltip">HWND of the tooltip</param>
    /// <param name="bActivate">Value specifying whether tracking is being activated or deactivated</param>
    /// <param name="_ti">Pointer to a TOOLINFO structure that identifies the tool to which this message applies</param>
    internal static void TrackActivate( IntPtr _wndTooltip, bool bActivate, TOOLINFO _ti )
    {
        const int TTM_TRACKACTIVATE = WM_USER + 17;
        SendToolInfoMessage( _wndTooltip, TTM_TRACKACTIVATE, Convert.ToInt32( bActivate ), _ti );
    }

    /// <summary>returns (LPARAM) MAKELONG( pt.X, pt.Y )</summary>
    internal static IntPtr makeLParam( Point pt )
    {
        int res = ( pt.X & 0xFFFF );
        res |= ( ( pt.Y & 0xFFFF ) << 16 );
        return new IntPtr( res );
    }

    /// <summary>Sets the position of a tracking ToolTip</summary>
    /// <param name="_wndTooltip">HWND of the tooltip.</param>
    /// <param name="pt">The point at which the tracking ToolTip will be displayed, in screen coordinates.</param>
    internal static void TrackPosition( IntPtr _wndTooltip, Point pt )
    {
        const int TTM_TRACKPOSITION = WM_USER + 18;
        SendMessage( _wndTooltip, TTM_TRACKPOSITION, 0, makeLParam( pt ) );
    }

    /// <summary>Sets the maximum width for a ToolTip window</summary>
    /// <param name="_wndTooltip">HWND of the tooltip.</param>
    /// <param name="pxWidth">Maximum ToolTip window width, or -1 to allow any width</param>
    /// <returns>the previous maximum ToolTip width</returns>
    internal static int SetMaxTipWidth( IntPtr _wndTooltip, int pxWidth )
    {
        const int TTM_SETMAXTIPWIDTH = WM_USER + 24;
        return SendMessage( _wndTooltip, TTM_SETMAXTIPWIDTH, 0, new IntPtr( pxWidth ) );
    }

    #endregion

    /// <summary>Sets the information that a ToolTip control maintains for a tool (not currently used).</summary>
    /// <param name="act"></param>
    internal void AlterToolInfo( Action<TOOLINFO> act )
    {
        const int TTM_GETTOOLINFO = WM_USER + 53;
        const int TTM_SETTOOLINFO = WM_USER + 54;

        var tempptr = IntPtr.Zero;
        try
        {
            tempptr = Marshal.AllocHGlobal( m_toolinfo.cbSize );
            Marshal.StructureToPtr( m_toolinfo, tempptr, false );

            SendMessage( m_wndToolTip, TTM_GETTOOLINFO, 0, tempptr );

            m_toolinfo = (TOOLINFO)Marshal.PtrToStructure( tempptr, typeof( TOOLINFO ) );

            act( m_toolinfo );

            Marshal.StructureToPtr( m_toolinfo, tempptr, false );

            SendMessage( m_wndToolTip, TTM_SETTOOLINFO, 0, tempptr );
        }
        finally
        {
            Marshal.FreeHGlobal( tempptr );
        }
    }

    readonly Control m_ownerControl;

    // The ToolTip's HWND
    IntPtr m_wndToolTip = IntPtr.Zero;

    /// <summary>The maximum width for a ToolTip window.
    /// If a ToolTip string exceeds the maximum width, the control breaks the text into multiple lines.</summary>
    int m_pxMaxWidth = 200;

    TOOLINFO m_toolinfo = new TOOLINFO();

    public BalloonToolTip( Control owner )
    {
        m_ownerControl = owner;

        // See http://msdn.microsoft.com/en-us/library/bb760252(VS.85).aspx#tooltip_sample_rect
        m_toolinfo.cbSize = Marshal.SizeOf( typeof( TOOLINFO ) );
        m_toolinfo.uFlags = TTF_TRANSPARENT | TTF_TRACK;
        m_toolinfo.hwnd = m_ownerControl.Handle;
        m_toolinfo.rect = m_ownerControl.Bounds;
    }

    /// <summary>Throws an exception if there's no alive WIN32 window.</summary>
    void VerifyControlIsAlive()
    {
        if( IntPtr.Zero == m_wndToolTip )
            throw new ApplicationException( "The control is not created, or is already destroyed." );
    }

    /// <summary>Create the balloon window.</summary>
    public void Create()
    {
        if( IntPtr.Zero != m_wndToolTip )
            Destroy();

        // Create the tooltip control.
        m_wndToolTip = CreateWindowEx( WS_EX_TOPMOST, "tooltips_class32",
            string.Empty,
            WS_POPUP | TTS_BALLOON | TTS_NOPREFIX | TTS_ALWAYSTIP,
            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
            m_ownerControl.Handle, 0, 0, 0 );

        if( IntPtr.Zero == m_wndToolTip )
            throw new Win32Exception();

        if( !SetWindowPos( m_wndToolTip, HWND_TOPMOST,
                    0, 0, 0, 0,
                    SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE ) )
            throw new Win32Exception();

        SetMaxTipWidth( m_wndToolTip, m_pxMaxWidth );
    }

    bool m_bVisible = false;

    /// <summary>return true if the balloon is currently visible.</summary>
    public bool bVisible
    {
        get
        {
            if( IntPtr.Zero == m_wndToolTip )
                return false;
            return m_bVisible;
        }
    }

    /// <summary>Balloon title.
    /// The balloon will only use the new value on the next Show() operation.</summary>
    public string strTitle;

    /// <summary>Balloon title icon.
    /// The balloon will only use the new value on the next Show() operation.</summary>
    public ToolTipIcon icon;

    /// <summary>Balloon title icon.
    /// The new value is updated immediately.</summary>
    public string strText
    {
        get { return m_toolinfo.text; }
        set
        {
            m_toolinfo.text = value;
            if( bVisible )
                UpdateTipText( m_wndToolTip, m_toolinfo );
        }
    }

    /// <summary>Show the balloon.</summary>
    /// <param name="pt">The balloon stem position, in the owner's client coordinates.</param>
    public void Show( Point pt )
    {
        VerifyControlIsAlive();

        if( m_bVisible ) Hide();

        // http://www.deez.info/sengelha/2008/06/12/balloon-tooltips/
        if( !AddTool( m_wndToolTip, m_toolinfo ) )
            throw new ApplicationException( "Unable to register the tooltip" );

        SetTitle( m_wndToolTip, icon, strTitle );

        TrackPosition( m_wndToolTip, m_ownerControl.PointToScreen( pt ) );

        TrackActivate( m_wndToolTip, true, m_toolinfo );

        m_bVisible = true;
    }

    /// <summary>Hide the balloon if it's visible.
    /// If the balloon was previously hidden, this method does nothing.</summary>
    public void Hide()
    {
        VerifyControlIsAlive();

        if( !m_bVisible ) return;

        TrackActivate( m_wndToolTip, false, m_toolinfo );

        DelTool( m_wndToolTip, m_toolinfo );

        m_bVisible = false;
    }

    /// <summary>Destroy the balloon.</summary>
    public void Destroy()
    {
        if( IntPtr.Zero == m_wndToolTip )
            return;
        if( m_bVisible ) Hide();

        DestroyWindow( m_wndToolTip );
        m_wndToolTip = IntPtr.Zero;
    }

    void IDisposable.Dispose()
    {
        Destroy();
    }
}

这是我的文章,包含完整的源代码

I used InteropServices to call several User32.dll methods (CreateWindowEx, DestroyWindow, SetWindowPos and SendMessage) to use native Win32 tooltips instead of the WinForms-provided wrapper.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;
using System.ComponentModel;

/// <remarks>This classes implements the balloon tooltip.
/// http://stackoverflow.com/questions/2028466
/// I hated Microsoft WinForms QA department after I had to develop my own version of the tooltip class,
/// just to workaround a bug, that would only add ~5 lines of code into the system.windows.forms.dll when fixed properly.</remarks>
class BalloonToolTip : IDisposable
{
    #region Unmanaged shit

    [DllImport( "user32.dll" )]
    static extern IntPtr CreateWindowEx( int exstyle, string classname, string windowtitle,
        int style, int x, int y, int width, int height, IntPtr parent,
        int menu, int nullvalue, int nullptr );

    [DllImport( "user32.dll" )]
    static extern int DestroyWindow( IntPtr hwnd );

    [DllImport( "user32.dll" )]
    static extern bool SetWindowPos( IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags );

    [DllImport( "user32.dll" )]
    static extern int SendMessage( IntPtr hWnd, int Msg, int wParam, IntPtr lParam );

    const int WS_POPUP = unchecked( (int)0x80000000 );
    const int TTS_BALLOON = 0x40;
    const int TTS_NOPREFIX = 0x02;
    const int TTS_ALWAYSTIP = 0x01;

    const int CW_USEDEFAULT = unchecked( (int)0x80000000 );

    const int WM_USER = 0x0400;

    IntPtr HWND_TOPMOST = new IntPtr( -1 );

    const int SWP_NOSIZE = 0x0001;
    const int SWP_NOMOVE = 0x0002;
    const int SWP_NOACTIVATE = 0x0010;

    const int WS_EX_TOPMOST = 0x00000008;

    [StructLayout( LayoutKind.Sequential )]
    public struct TOOLINFO
    {
        public int cbSize;
        public int uFlags;
        public IntPtr hwnd;
        public IntPtr id;
        private RECT m_rect;
        public IntPtr nullvalue;
        [MarshalAs( UnmanagedType.LPTStr )]
        public string text;
        public uint param;

        public Rectangle rect
        {
            get
            {
                return new Rectangle( m_rect.left, m_rect.top, m_rect.right - m_rect.left, m_rect.bottom - m_rect.top );
            }
            set
            {
                m_rect.left = value.Left;
                m_rect.top = value.Top;
                m_rect.right = value.Right;
                m_rect.bottom = value.Bottom;
            }
        }
    }

    [StructLayout( LayoutKind.Sequential )]
    struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
    }

    const int TTF_TRANSPARENT = 0x0100;
    const int TTF_TRACK = 0x0020;
    const int TTF_ABSOLUTE = 0x0080;

    #endregion

    #region Managed wrapper over some tooltip messages.
    // The most useful part of the wrappers in this section is their documentation.

    /// <summary>Send TTM_SETTITLE message to the tooltip.
    /// TODO [very low]: implement the custom icon, if needed. </summary>
    /// <param name="_wndTooltip">HWND of the tooltip</param>
    /// <param name="_icon">Standard icon</param>
    /// <param name="_title">The title string</param>
    internal static int SetTitle( IntPtr _wndTooltip, ToolTipIcon _icon, string _title )
    {
        const int TTM_SETTITLE = WM_USER + 33;

        var tempptr = IntPtr.Zero;
        try
        {
            tempptr = Marshal.StringToHGlobalUni( _title );
            return SendMessage( _wndTooltip, TTM_SETTITLE, (int)_icon, tempptr );
        }
        finally
        {
            if( IntPtr.Zero != tempptr )
            {
                Marshal.FreeHGlobal( tempptr );
                tempptr = IntPtr.Zero;
            }
        }
    }

    /// <summary>Send a message that wants LPTOOLINFO as the lParam</summary>
    /// <param name="_wndTooltip">HWND of the tooltip.</param>
    /// <param name="_msg">window message to send</param>
    /// <param name="_wParam">wParam value</param>
    /// <param name="_ti">TOOLINFO structure that goes to the lParam field. The cbSize field must be set.</param>
    internal static int SendToolInfoMessage( IntPtr _wndTooltip, int _msg, int _wParam, TOOLINFO _ti )
    {
        var tempptr = IntPtr.Zero;
        try
        {
            tempptr = Marshal.AllocHGlobal( _ti.cbSize );
            Marshal.StructureToPtr( _ti, tempptr, false );

            return SendMessage( _wndTooltip, _msg, _wParam, tempptr );
        }
        finally
        {
            if( IntPtr.Zero != tempptr )
            {
                Marshal.FreeHGlobal( tempptr );
                tempptr = IntPtr.Zero;
            }
        }
    }

    /// <summary>Registers a tool with a ToolTip control</summary>
    /// <param name="_wndTooltip">HWND of the tooltip</param>
    /// <param name="_ti">TOOLINFO structure containing information that the ToolTip control needs to display text for the tool.</param>
    /// <returns>Returns true if successful.</returns>
    internal static bool AddTool( IntPtr _wndTooltip, TOOLINFO _ti )
    {
        const int TTM_ADDTOOL = WM_USER + 50;
        int res = SendToolInfoMessage( _wndTooltip, TTM_ADDTOOL, 0, _ti );
        return Convert.ToBoolean( res );
    }

    /// <summary>Registers a tool with a ToolTip control</summary>
    /// <param name="_wndTooltip">HWND of the tooltip</param>
    /// <param name="_ti">TOOLINFO structure containing information that the ToolTip control needs to display text for the tool.</param>
    internal static void DelTool( IntPtr _wndTooltip, TOOLINFO _ti )
    {
        const int TTM_DELTOOL = WM_USER + 51;
        SendToolInfoMessage( _wndTooltip, TTM_DELTOOL, 0, _ti );
    }

    internal static void UpdateTipText( IntPtr _wndTooltip, TOOLINFO _ti )
    {
        const int TTM_UPDATETIPTEXT = WM_USER + 57;
        SendToolInfoMessage( _wndTooltip, TTM_UPDATETIPTEXT, 0, _ti );
    }

    /// <summary>Activates or deactivates a tracking ToolTip</summary>
    /// <param name="_wndTooltip">HWND of the tooltip</param>
    /// <param name="bActivate">Value specifying whether tracking is being activated or deactivated</param>
    /// <param name="_ti">Pointer to a TOOLINFO structure that identifies the tool to which this message applies</param>
    internal static void TrackActivate( IntPtr _wndTooltip, bool bActivate, TOOLINFO _ti )
    {
        const int TTM_TRACKACTIVATE = WM_USER + 17;
        SendToolInfoMessage( _wndTooltip, TTM_TRACKACTIVATE, Convert.ToInt32( bActivate ), _ti );
    }

    /// <summary>returns (LPARAM) MAKELONG( pt.X, pt.Y )</summary>
    internal static IntPtr makeLParam( Point pt )
    {
        int res = ( pt.X & 0xFFFF );
        res |= ( ( pt.Y & 0xFFFF ) << 16 );
        return new IntPtr( res );
    }

    /// <summary>Sets the position of a tracking ToolTip</summary>
    /// <param name="_wndTooltip">HWND of the tooltip.</param>
    /// <param name="pt">The point at which the tracking ToolTip will be displayed, in screen coordinates.</param>
    internal static void TrackPosition( IntPtr _wndTooltip, Point pt )
    {
        const int TTM_TRACKPOSITION = WM_USER + 18;
        SendMessage( _wndTooltip, TTM_TRACKPOSITION, 0, makeLParam( pt ) );
    }

    /// <summary>Sets the maximum width for a ToolTip window</summary>
    /// <param name="_wndTooltip">HWND of the tooltip.</param>
    /// <param name="pxWidth">Maximum ToolTip window width, or -1 to allow any width</param>
    /// <returns>the previous maximum ToolTip width</returns>
    internal static int SetMaxTipWidth( IntPtr _wndTooltip, int pxWidth )
    {
        const int TTM_SETMAXTIPWIDTH = WM_USER + 24;
        return SendMessage( _wndTooltip, TTM_SETMAXTIPWIDTH, 0, new IntPtr( pxWidth ) );
    }

    #endregion

    /// <summary>Sets the information that a ToolTip control maintains for a tool (not currently used).</summary>
    /// <param name="act"></param>
    internal void AlterToolInfo( Action<TOOLINFO> act )
    {
        const int TTM_GETTOOLINFO = WM_USER + 53;
        const int TTM_SETTOOLINFO = WM_USER + 54;

        var tempptr = IntPtr.Zero;
        try
        {
            tempptr = Marshal.AllocHGlobal( m_toolinfo.cbSize );
            Marshal.StructureToPtr( m_toolinfo, tempptr, false );

            SendMessage( m_wndToolTip, TTM_GETTOOLINFO, 0, tempptr );

            m_toolinfo = (TOOLINFO)Marshal.PtrToStructure( tempptr, typeof( TOOLINFO ) );

            act( m_toolinfo );

            Marshal.StructureToPtr( m_toolinfo, tempptr, false );

            SendMessage( m_wndToolTip, TTM_SETTOOLINFO, 0, tempptr );
        }
        finally
        {
            Marshal.FreeHGlobal( tempptr );
        }
    }

    readonly Control m_ownerControl;

    // The ToolTip's HWND
    IntPtr m_wndToolTip = IntPtr.Zero;

    /// <summary>The maximum width for a ToolTip window.
    /// If a ToolTip string exceeds the maximum width, the control breaks the text into multiple lines.</summary>
    int m_pxMaxWidth = 200;

    TOOLINFO m_toolinfo = new TOOLINFO();

    public BalloonToolTip( Control owner )
    {
        m_ownerControl = owner;

        // See http://msdn.microsoft.com/en-us/library/bb760252(VS.85).aspx#tooltip_sample_rect
        m_toolinfo.cbSize = Marshal.SizeOf( typeof( TOOLINFO ) );
        m_toolinfo.uFlags = TTF_TRANSPARENT | TTF_TRACK;
        m_toolinfo.hwnd = m_ownerControl.Handle;
        m_toolinfo.rect = m_ownerControl.Bounds;
    }

    /// <summary>Throws an exception if there's no alive WIN32 window.</summary>
    void VerifyControlIsAlive()
    {
        if( IntPtr.Zero == m_wndToolTip )
            throw new ApplicationException( "The control is not created, or is already destroyed." );
    }

    /// <summary>Create the balloon window.</summary>
    public void Create()
    {
        if( IntPtr.Zero != m_wndToolTip )
            Destroy();

        // Create the tooltip control.
        m_wndToolTip = CreateWindowEx( WS_EX_TOPMOST, "tooltips_class32",
            string.Empty,
            WS_POPUP | TTS_BALLOON | TTS_NOPREFIX | TTS_ALWAYSTIP,
            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
            m_ownerControl.Handle, 0, 0, 0 );

        if( IntPtr.Zero == m_wndToolTip )
            throw new Win32Exception();

        if( !SetWindowPos( m_wndToolTip, HWND_TOPMOST,
                    0, 0, 0, 0,
                    SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE ) )
            throw new Win32Exception();

        SetMaxTipWidth( m_wndToolTip, m_pxMaxWidth );
    }

    bool m_bVisible = false;

    /// <summary>return true if the balloon is currently visible.</summary>
    public bool bVisible
    {
        get
        {
            if( IntPtr.Zero == m_wndToolTip )
                return false;
            return m_bVisible;
        }
    }

    /// <summary>Balloon title.
    /// The balloon will only use the new value on the next Show() operation.</summary>
    public string strTitle;

    /// <summary>Balloon title icon.
    /// The balloon will only use the new value on the next Show() operation.</summary>
    public ToolTipIcon icon;

    /// <summary>Balloon title icon.
    /// The new value is updated immediately.</summary>
    public string strText
    {
        get { return m_toolinfo.text; }
        set
        {
            m_toolinfo.text = value;
            if( bVisible )
                UpdateTipText( m_wndToolTip, m_toolinfo );
        }
    }

    /// <summary>Show the balloon.</summary>
    /// <param name="pt">The balloon stem position, in the owner's client coordinates.</param>
    public void Show( Point pt )
    {
        VerifyControlIsAlive();

        if( m_bVisible ) Hide();

        // http://www.deez.info/sengelha/2008/06/12/balloon-tooltips/
        if( !AddTool( m_wndToolTip, m_toolinfo ) )
            throw new ApplicationException( "Unable to register the tooltip" );

        SetTitle( m_wndToolTip, icon, strTitle );

        TrackPosition( m_wndToolTip, m_ownerControl.PointToScreen( pt ) );

        TrackActivate( m_wndToolTip, true, m_toolinfo );

        m_bVisible = true;
    }

    /// <summary>Hide the balloon if it's visible.
    /// If the balloon was previously hidden, this method does nothing.</summary>
    public void Hide()
    {
        VerifyControlIsAlive();

        if( !m_bVisible ) return;

        TrackActivate( m_wndToolTip, false, m_toolinfo );

        DelTool( m_wndToolTip, m_toolinfo );

        m_bVisible = false;
    }

    /// <summary>Destroy the balloon.</summary>
    public void Destroy()
    {
        if( IntPtr.Zero == m_wndToolTip )
            return;
        if( m_bVisible ) Hide();

        DestroyWindow( m_wndToolTip );
        m_wndToolTip = IntPtr.Zero;
    }

    void IDisposable.Dispose()
    {
        Destroy();
    }
}

Here's my article with the complete source code.

栀梦 2024-08-24 08:51:34

您不需要执行任何特殊操作,因为 放置根据 MSDN,是自动的。您对气球工具提示有疑问吗?

You do not need to do anything special as the placement is automatic according to MSDN. Do have you an issue with ballon tooltip ?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文