一个可以改变箭头方向的气泡提示

用VB实现一个气泡提示并不难,即使是实现在任意地方显示的气泡提示。难的是,如果是采用TTF_TRACK方式允许在任意地方显示时,气泡的箭头总是向上,而且还不能自动消失。为此,我写了一个增强的气泡提示类,希望对有此需要的朋友一些参考。

一、新建一个类,类名为clsTip,类代码如下:

Option Explicit
'* ******************************************** *
'*  模块名称:clsTip.cls
'*  功能:一个可以改变箭头方向的气泡提示类
'*  作者:lyserver
'*  联系方式:http://blog.csdn.net/lyserver
'* ******************************************** *
Private Type TOOLINFO
    cbSize As Long
    dwFlags As Long
    hwnd As Long
    dwID As Long
    rtRect(3) As Long
    hInst As Long
    lpszText As String
    lParam  As Long
End Type
Private Declare Sub InitCommonControls Lib "comctl32" ()
Private Declare Function CreateWindowEx Lib "user32" Alias "CreateWindowExA" _
    (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, _
    ByVal dwStyle As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, _
    ByVal nHeight As Long, ByVal hwndParent As Long, ByVal hMenu As Long, _
    ByVal hInstance As Long, lpParam As Any) As Long
Private Const TOOLTIPS_CLASS As String = "tooltips_class32"
Private Declare Function DestroyWindow Lib "user32" (ByVal hwnd As Long) As Long
' ToolTips Style
Public Enum StyleConstants
    TTS_COMMON = &H0
    TTS_BALLOON = &H40
End Enum
Private Const TTS_ALWAYSTIP As Long = &H1
Private Const TTS_NOANIMATE As Long = &H10
Private Const TTS_NOFADE As Long = &H20
Private Const TTS_NOPREFIX As Long = &H2
Private Const TTDT_AUTOPOP = 2
Private Const TTDT_INITIAL = 3
' ToolTips Flags
Private Const TTF_ABSOLUTE As Long = &H80
Private Const TTF_CENTERTIP As Long = &H2
Private Const TTF_DI_SETITEM As Long = &H8000
Private Const TTF_IDISHWND As Long = &H1
Private Const TTF_RTLREADING As Long = &H4
Private Const TTF_SUBCLASS As Long = &H10
Private Const TTF_TRACK As Long = &H20
Private Const TTF_TRANSPARENT As Long = &H100
' ToolTips Icon
Public Enum IconConstants
    TTI_NONE = 0
    TTI_INFO = 1
    TTI_WARNING = 2
    TTI_ERROR = 3
End Enum
'ToolTips Arrow Orientation
Public Enum OrientationConstants
    Down = 0
    Up = 1
End Enum
' ToolTips Message
Private Const WM_USER As Long = &H400
Private Const TTM_SETDELAYTIME As Long = (WM_USER + 3)
Private Const TTM_ADDTOOL As Long = (WM_USER + 4)
Private Const TTM_DELTOOL As Long = (WM_USER + 5)
Private Const TTM_SETTOOLINFO As Long = (WM_USER + 9)
Private Const TTM_UPDATETIPTEXTA = (WM_USER + 12)
Private Const TTM_TRACKACTIVATE As Long = (WM_USER + 17)
Private Const TTM_TRACKPOSITION As Long = (WM_USER + 18)
Private Const TTM_SETTIPBKCOLOR As Long = (WM_USER + 19)
Private Const TTM_SETTIPTEXTCOLOR As Long = (WM_USER + 20)
Private Const TTM_SETTITLE As Long = (WM_USER + 32)
Private Declare Function GetCursorPos Lib "user32" (ByVal lpPoint As Long) As Long
Private Declare Function SetCursorPos Lib "user32" (ByVal X As Long, ByVal Y As Long) As Long
Private Declare Function ShowCursor Lib "user32" (ByVal bShow As Long) As Long
Private Declare Function GetForegroundWindow Lib "user32" () As Long
Private Declare Function ClientToScreen Lib "user32" (ByVal hwnd As Long, ByVal lpPoint As Long) As Long
Private Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, ByVal lpRect As Long) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, _
    ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, _
    ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, _
    ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, _
    ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Const GWL_STYLE = (-16)
Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, _
    ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Const HWND_NOTOPMOST = -2
Private Const SWP_NOSIZE = &H1
Private Const SWP_NOZORDER = &H4
Private Const SWP_NOACTIVATE = &H10
Private Const SWP_SHOWWINDOW = &H40
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, _
    ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, _
    Source As Any, ByVal Length As Long)
Private Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long, _
    ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, ByVal lpRect As Long) As Long

Dim m_hwndTip As Long
Dim m_hwndParent As Long
Dim m_TipInfo As TOOLINFO
Dim m_Title As String
Dim m_Icon As IconConstants
Dim m_Style As StyleConstants
Dim m_Orientation As OrientationConstants
Dim m_Delay As Long
Dim m_ForeColor As Long, m_BackColor As Long
Dim m_idTimer As Long

Private Sub Class_Initialize()
    InitCommonControls
    m_Icon = TTI_INFO
    m_TipInfo.cbSize = Len(m_TipInfo)
    m_TipInfo.dwFlags = TTF_IDISHWND Or TTF_TRANSPARENT Or TTF_SUBCLASS 'Or TTF_TRACK
    m_TipInfo.hInst = App.hInstance
    m_Delay = 2000
End Sub

Private Sub Class_Terminate()
    If m_idTimer <> 0 Then KillTimer 0, m_idTimer
    If m_hwndTip <> 0 Then DestroyWindow m_hwndTip
    m_idTimer = 0
    m_hwndTip = 0
End Sub

Public Sub Show(ByVal hwndParent As Long, Optional ByVal szText As String = vbNullString, _
    Optional ByVal szTitle As String = vbNullString, Optional X As Long, Optional Y As Long)
    
    Dim hwnd As Long
    Dim objPos(1) As Long, rtWin(3) As Long, ptPos As Long
    
    Call Class_Terminate

    m_Title = szTitle
    m_TipInfo.lpszText = szText
    m_hwndParent = IIf(hwndParent, hwndParent, GetForegroundWindow)
    m_hwndTip = CreateWindowEx(0, TOOLTIPS_CLASS, "", TTS_NOPREFIX Or TTS_ALWAYSTIP Or m_Style, _
        0, 0, 0, 0, m_hwndParent, 0, App.hInstance, ByVal 0&)
    m_TipInfo.hwnd = m_hwndParent
    m_TipInfo.dwID = m_hwndParent
    
    If X > 0 And Y > 0 Then
        objPos(0) = X: objPos(1) = Y
        ClientToScreen m_hwndParent, VarPtr(objPos(0))
    Else
        GetCursorPos VarPtr(objPos(0))
    End If
    ptPos = objPos(1) * &H10000 + objPos(0)
    
    SendMessage m_hwndTip, TTM_SETDELAYTIME, TTDT_INITIAL, ByVal 0
    SendMessage m_hwndTip, TTM_SETTITLE, m_Icon, ByVal m_Title
    If m_ForeColor <> Empty Then SendMessage m_hwndTip, TTM_SETTIPTEXTCOLOR, m_ForeColor, 0&
    If m_BackColor <> Empty Then SendMessage m_hwndTip, TTM_SETTIPBKCOLOR, m_BackColor, 0&
    SendMessage m_hwndTip, TTM_ADDTOOL, 0&, m_TipInfo
    
    If m_Orientation = Up Then
        SendMessage m_hwndTip, TTM_TRACKPOSITION, 0, ByVal ptPos
        SendMessage m_hwndTip, TTM_TRACKACTIVATE, 1, m_TipInfo
    Else
        SendMessage m_hwndTip, TTM_TRACKACTIVATE, 1, m_TipInfo
        GetWindowRect m_hwndTip, VarPtr(rtWin(0))
        objPos(0) = objPos(0) - 16
        objPos(1) = objPos(1) - (rtWin(3) - rtWin(1)) + 1
        SetWindowPos m_hwndTip, HWND_NOTOPMOST, objPos(0), objPos(1), 0, 0, _
            SWP_NOSIZE Or SWP_NOZORDER Or SWP_NOACTIVATE Or SWP_SHOWWINDOW
    End If
    m_idTimer = SetTimer(0, 0, m_Delay, GetClassProcAddr(Me, 22))
End Sub

Public Sub Hide()
    Call Class_Terminate
End Sub

Public Property Get Title() As String
    Title = m_Title
End Property
Public Property Let Title(ByVal New_Value As String)
    m_Title = New_Value
    If m_hwndTip <> 0 Then SendMessage m_hwndTip, TTM_SETTITLE, m_Icon, ByVal m_Title
End Property

Public Property Get Text() As String
    Text = m_TipInfo.lpszText
End Property
Public Property Let Text(ByVal New_Value As String)
    m_TipInfo.lpszText = New_Value
    If m_hwndTip <> 0 Then SendMessage m_hwndTip, TTM_UPDATETIPTEXTA, 0&, m_TipInfo
End Property

Public Property Get Style() As StyleConstants
    Style = m_Style
End Property
Public Property Let Style(ByVal New_Value As StyleConstants)
    m_Style = New_Value
End Property

Public Property Get Icon() As IconConstants
    Icon = m_Icon
End Property
Public Property Let Icon(ByVal New_Value As IconConstants)
    m_Icon = New_Value
    If m_hwndTip <> 0 Then SendMessage m_hwndTip, TTM_SETTITLE, m_Icon, ByVal m_Title
End Property

Public Property Get Orientation() As OrientationConstants
    Orientation = m_Orientation
End Property
Public Property Let Orientation(ByVal New_Value As OrientationConstants)
    m_Orientation = New_Value
    If New_Value = Up Then
        m_TipInfo.dwFlags = TTF_IDISHWND Or TTF_TRANSPARENT Or TTF_TRACK
    Else
        m_TipInfo.dwFlags = TTF_IDISHWND Or TTF_TRANSPARENT Or TTF_SUBCLASS
    End If
End Property

Public Property Get BackColor() As OLE_COLOR
    BackColor = m_BackColor
End Property
Public Property Let BackColor(ByVal New_Value As OLE_COLOR)
    m_BackColor = New_Value
    If m_hwndTip <> 0 Then SendMessage m_hwndTip, TTM_SETTIPBKCOLOR, m_BackColor, 0&
End Property

Public Property Get ForeColor() As OLE_COLOR
    ForeColor = m_ForeColor
End Property
Public Property Let ForeColor(ByVal New_Value As OLE_COLOR)
    m_ForeColor = New_Value
    If m_hwndTip <> 0 Then SendMessage m_hwndTip, TTM_SETTIPTEXTCOLOR, m_ForeColor, 0&
End Property

Public Property Get Delay() As Long
    Delay = m_Delay
End Property
Public Property Let Delay(ByVal New_Value As Long)
    m_Delay = New_Value
End Property

Private Function GetClassProcAddr(obj As Object, ByVal Index As Long, _
    Optional ByVal ParamCount As Long = 4, Optional ByVal HasReturnValue As Boolean) As Long
    Static lReturn As Long, pReturn As Long
    Static AsmCode(50) As Byte
    Dim i As Long, pThis As Long, pVtbl As Long, pFunc As Long

    pThis = ObjPtr(obj)
    CopyMemory pVtbl, ByVal pThis, 4
    CopyMemory pFunc, ByVal pVtbl + (6 + Index) * 4, 4
    pReturn = VarPtr(lReturn)

    For i = 0 To UBound(AsmCode)                                '填充nop
        AsmCode(i) = &H90
    Next
    AsmCode(0) = &H55                                           'push   ebp
    AsmCode(1) = &H8B: AsmCode(2) = &HEC                        'mov    ebp,esp
    AsmCode(3) = &H53                                           'push   ebx
    AsmCode(4) = &H56                                           'push   esi
    AsmCode(5) = &H57                                           'push   edi
    If HasReturnValue Then
        AsmCode(6) = &HB8                                       'mov    offset lReturn
        CopyMemory AsmCode(7), pReturn, 4
        AsmCode(11) = &H50                                      'push   eax
    End If
    For i = 0 To ParamCount - 1                                 'push   dword ptr[ebp+xx]
        AsmCode(12 + i * 3) = &HFF
        AsmCode(13 + i * 3) = &H75
        AsmCode(14 + i * 3) = (ParamCount - i) * 4 + 4
    Next
    i = i * 3 + 12
    AsmCode(i) = &HB9                                           'mov    ecx,this
    CopyMemory AsmCode(i + 1), pThis, 4
    AsmCode(i + 5) = &H51                                       'push ecx
    AsmCode(i + 6) = &HE8                                       'call 相对地址
    CopyMemory AsmCode(i + 7), pFunc - VarPtr(AsmCode(i + 6)) - 5, 4
    If HasReturnValue Then
        AsmCode(i + 11) = &HB8                                  'mov    eax,offset lReturn
        CopyMemory AsmCode(i + 12), pReturn, 4
        AsmCode(i + 16) = &H8B                                  'mov    eax,dword ptr[eax]
        AsmCode(i + 17) = &H0
    End If
    AsmCode(i + 18) = &H5F                                      'pop    edi
    AsmCode(i + 19) = &H5E                                      'pop    esi
    AsmCode(i + 20) = &H5B                                      'pop    ebx
    AsmCode(i + 21) = &H8B: AsmCode(i + 22) = &HE5              'mov    esp,ebp
    AsmCode(i + 23) = &H5D                                      'pop    ebp
    AsmCode(i + 24) = &HC3                                      'ret
    GetClassProcAddr = VarPtr(AsmCode(0))
End Function

Private Sub TimerProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal idEvent As Long, _
    ByVal dwTime As Long)
    Call Class_Terminate
End Sub

二、测试代码如下:

Option Explicit
Dim m_Tip As clsTip

Private Sub Command1_Click()
    Me.Circle (20, 50), 2, vbRed
    m_Tip.Orientation = Down
    m_Tip.Delay = 1500 '1500毫秒后气泡自动消失
    m_Tip.Show Me.hwnd, "这是一个可以指定位置和箭头方向气泡提示!" & vbCrLf & _
        "第二行信息", "信息", 20, 50
End Sub

Private Sub Command2_Click()
    m_Tip.Hide '也可以手动消失
End Sub

Private Sub Form_Load()
    Set m_Tip = New clsTip
    m_Tip.Style = TTS_BALLOON
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Set m_Tip = Nothing
End Sub
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值