Adding Multiline Balloon ToolTips to ListView Column Headers

Environment: Win 9X, 2K, XP

The attached code demonstrates a technique you can use to create multiline balloon ToolTips for ListView column headers.

The code uses a subclassing technique to trap the WM_MOUSEMOVE event of the Header control that is a constituent part of the ListView control. The handler for this event determines the index of the column header under the mouse pointer and changes the tooltip text respectively; it uses the HDM_HITTEST message for this purpose.

To find the handle of the Header control inside ListView from MSCOMCTL.OCX, the code uses the FindWindowEx function, which searches for the child window with the "msvb_lib_header" class inside the ListView control. The code also can be used to subclass the "pure" ListView control (its class name is "SysListView32"); to do it, you must replace the "msvb_lib_header" string on "SysHeader32".

The sample creates MS Windows ToolTips with the CreateWindowEx function passing to this function the "tooltips_class32" class name. Balloon ToolTips work only in Windows 2000/XP; in the previous versions of Windows, you'll see old-style rectangular ToolTips. You can use this technique to create multiline balloon ToolTips for any control that contains a Windows Header control. For instance, we use this technique to create such ToolTips for the grid control we produce (10Tec iGrid ActiveX control—visit www.10Tec.com for more info).

Class CToolTip

VERSION 1.0 CLASS
BEGIN
  MultiUse            = -1    'True
  Persistable         = 0     'NotPersistable
  DataBindingBehavior = 0     'vbNone
  DataSourceBehavior  = 0     'vbNone
  MTSTransactionMode  = 0     'NotAnMTSObject
END
Attribute VB_Name             = "CToolTip"
Attribute VB_GlobalNameSpace  = False
Attribute VB_Creatable        = True
Attribute VB_PredeclaredId    = False
Attribute VB_Exposed          = False
Attribute VB_Ext_KEY          = "SavedWithClassBuilder" ,"Yes"
Attribute VB_Ext_KEY          = "Top_Level" ,"Yes"
Option Explicit

Private Declare Function OleTranslateColor Lib "OLEPRO32.DLL" _
        (ByVal OLE_COLOR As Long, ByVal HPALETTE As Long, _
         pccolorref As Long) As Long
Private Const CLR_INVALID = -1

'Initialization of New ClassNames
Private Const ICC_BAR_CLASSES = &H4      'toolbar, statusbar,
                                         'trackbar, tooltips
Private Declare Sub InitCommonControls Lib "comctl32.dll" ()
Private Declare Function InitCommonControlsEx Lib "comctl32.dll" _
        (lpInitCtrls As tagINITCOMMONCONTROLSEX) As Boolean

Private Type tagINITCOMMONCONTROLSEX
   dwSize As Long   ' size of this structure
   dwICC As Long    ' flags indicating which classes to be
                    ' initialized.
End Type

' ToolTip Styles
Private Const TTS_ALWAYSTIP = &H1
Private Const TTS_NOPREFIX  = &H2
Private Const TTS_BALLOON   = &H40  ' comctl32.dll v5.8 required

Private Const CW_USEDEFAULT = &H80000000

Private Const WM_USER = &H400

' ToolTip Messages
Private Const TTM_SETDELAYTIME    = (WM_USER + 3)
Private Const TTM_ADDTOOL         = (WM_USER + 4)
Private Const TTM_DELTOOL         = (WM_USER + 5)
Private Const TTM_NEWTOOLRECT     = (WM_USER + 6)
Private Const TTM_GETTOOLINFO     = (WM_USER + 8)
Private Const TTM_SETTIPBKCOLOR   = (WM_USER + 19)
Private Const TTM_SETTIPTEXTCOLOR = (WM_USER + 20)
Private Const TTM_SETMAXTIPWIDTH  = (WM_USER + 24)
Private Const TTDT_AUTOPOP        = 2
Private Const TTDT_INITIAL        = 3

Private Const TTM_TRACKACTIVATE   = (WM_USER + 17)
Private Const TTM_TRACKPOSITION   = (WM_USER + 18)
Private Const TTM_SETTOOLINFOA    = (WM_USER + 9)

Private Const TTF_IDISHWND        = &H1
Private Const TTF_CENTERTIP       = &H2
Private Const TTF_SUBCLASS        = &H10

Private Type RECT
        Left As Long
        Top As Long
        Right As Long
        Bottom As Long
End Type

Private Type TOOLINFO
    cbSize      As Long
    uFlags      As Long
    hwnd        As Long
    uId         As Long
    cRect       As RECT
    hinst       As Long
    lpszText    As String
End Type

Public Enum TTStyle
    ttStyleStandard = 1
    ttStyleBalloon  = 2
End Enum

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, l_
                                 pParam As Any) As Long
Private Declare Function DestroyWindow Lib "user32" _
                (ByVal hwnd 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 SendMessageLong Lib "user32" _
                Alias "SendMessageA" (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 hTT As Long
Dim TI As TOOLINFO

Private mvarObjHwnd As Long    ' local copy

Public Property Let TxtColor(ByVal clrData As OLE_COLOR)
   SendMessageLong hTT, TTM_SETTIPTEXTCOLOR, _
                        TranslateColor(clrData), 0&
End Property

Public Property Let BkColor(ByVal clrData As OLE_COLOR)
   SendMessageLong hTT, TTM_SETTIPBKCOLOR, _
                        TranslateColor(clrData), 0&
End Property

Public Property Let VisibleTime(ByVal lData As Long)
   SendMessageLong hTT, TTM_SETDELAYTIME, TTDT_AUTOPOP, lData
End Property

Public Property Let DelayTime(ByVal lData As Long)
   SendMessageLong hTT, TTM_SETDELAYTIME, TTDT_INITIAL, lData
End Property

Public Property Let TipWidth(ByVal lData As Long)
   SendMessageLong hTT, TTM_SETMAXTIPWIDTH, 0, lData
End Property

Public Property Let Style(ByVal ttsData As TTStyle)
   Dim lStyle As Long
   If hTT Then
      lStyle = GetWindowLong(hTT, GWL_STYLE)
      If ttsData = ttStyleBalloon Then lStyle = lStyle Or _
                                                TTS_BALLOON
      If ttsData = ttStyleStandard _
         And (lStyle And ttStyleBalloon) _
         Then lStyle = lStyle Xor TTS_BALLOON
      SetWindowLong hTT, GWL_STYLE, lStyle
   End If
End Property

Private Sub InitComctl32(dwFlags As Long)
   Dim icc As tagINITCOMMONCONTROLSEX
   On Error GoTo Err_OldVersion
   icc.dwSize = Len(icc)
   icc.dwICC = dwFlags
   InitCommonControlsEx icc
   On Error GoTo 0
   Exit Sub
Err_OldVersion:
   InitCommonControls
End Sub

Public Sub InitToolTip(ByVal ObjHwnd As Long, _
           Optional ByVal sTipText As String = "tooltip", _
           Optional bCenter As Boolean = False)
   mvarObjHwnd = ObjHwnd
   
   With TI
      .hwnd      = mvarObjHwnd
      .uFlags    = TTF_IDISHWND Or TTF_SUBCLASS
      If bCenter Then
         .uFlags = .uFlags Or TTF_CENTERTIP
      End If
      .uId       = mvarObjHwnd
      .lpszText  = sTipText
      .cbSize    = Len(TI)
   End With
   SendMessage hTT, TTM_ADDTOOL, 0, TI
End Sub

Public Sub RemoveToolTip()
   SendMessage hTT, TTM_DELTOOL, 0, TI
End Sub

Private Sub Class_Initialize()
   InitComctl32 ICC_BAR_CLASSES
   hTT = CreateWindowEx(0, "tooltips_class32", 0&, _
                        TTS_NOPREFIX Or TTS_ALWAYSTIP, _
                        CW_USEDEFAULT, _
                        CW_USEDEFAULT, _
                        CW_USEDEFAULT, _
                        CW_USEDEFAULT, 0&, 0&, App.hInstance, 0&)
   Style = ttStyleStandard
   TipWidth = 3000    ' Specify tip width to enable multiline
                      ' ToolTip
End Sub

Private Sub Class_Terminate()
   If hTT Then DestroyWindow hTT
End Sub

Private Function TranslateColor(ByVal oClr As OLE_COLOR, _
                 Optional hPal As Long = 0) As Long
    ' Convert Automation color to Windows color
    If OleTranslateColor(oClr, hPal, TranslateColor) Then
        TranslateColor = CLR_INVALID
    End If
End Function

Public Sub TrackPosition(ByVal x As Integer, ByVal y As Integer)
   SendMessageLong hTT, TTM_TRACKPOSITION, 0&, y * 65536 + x
End Sub

Public Sub ActivateTracking()
   SendMessage hTT, TTM_TRACKACTIVATE, 1, TI
End Sub

Public Sub DeactivateTracking()
   SendMessage hTT, TTM_TRACKACTIVATE, 0, TI
End Sub

Public Sub SetTooltipText(sText As String)
   TI.lpszText = sText
   SendMessage hTT, TTM_SETTOOLINFOA, 0&, TI
End Sub

Module mSubclass

Attribute VB_Name = "mSubclass"
Option Explicit

Public 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 Function SetWindowLong Lib "user32" _
        Alias "SetWindowLongA" _
        (ByVal hwnd As Long, _
         ByVal nIndex As Long, _
         ByVal dwNewLong As Long) As Long

Private Const GWL_WNDPROC  = -4

Private Const WM_MOUSEMOVE = &H200

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 Type POINTAPI
    x As Long
    y As Long
End Type

Private Type HDHITTESTINFO
    pt As POINTAPI
    flags As Long
    iItem As Long
End Type
Private Const HDM_FIRST    = &H1200
Private Const HDM_HITTEST  = HDM_FIRST + 6
Private Const HDM_GETITEMA = (HDM_FIRST + 3)
Private Const HDM_GETITEM  = HDM_GETITEMA

Private Type HD_ITEM
    mask As Long
    cxy As Long
    pszText As String
    hbm As Long
    cchTextMax As Long
    fmt As Long
    lParam As Long
    ' 4.70:
    iImage As Long
    iOrder As Long
End Type
Private Const HDI_LPARAM = &H8

Private Type TLoHiLong
   Lo As Integer
   Hi As Integer
End Type
Private Type TAllLong
   All As Long
End Type
Dim mLH As TLoHiLong, mAL As TAllLong


Private m_lPrevWndProc As Long
Private m_lCurHdrItem As Long

Public Sub Hook(ByVal pHwnd As Long)
   m_lPrevWndProc = SetWindowLong(pHwnd, GWL_WNDPROC, _
                                  AddressOf WindowProc)
   m_lCurHdrItem = -1
End Sub

Public Sub Unhook(ByVal pHwnd As Long)
   SetWindowLong pHwnd, GWL_WNDPROC, m_lPrevWndProc
End Sub

Function WindowProc(ByVal hwnd As Long, ByVal uMsg As Long, _
         ByVal wParam As Long, ByVal lParam As Long) As Long
   
   Dim hti As HDHITTESTINFO
   Dim lCol As Long
   
   If uMsg = WM_MOUSEMOVE Then
      ' The low and high words of lParam contains x and y
      ' coordinates of the mouse pointer respectively:
      mAL.All = lParam
      LSet mLH = mAL
      hti.pt.x = mLH.Lo
      hti.pt.y = mLH.Hi
      ' retrieving the index of the header item under the
      ' mouse pointer:
      SendMessage hwnd, HDM_HITTEST, 0&, hti
      ' if the current header changed...
      If hti.iItem <> m_lCurHdrItem Then
         m_lCurHdrItem = hti.iItem
         Form1.TT.RemoveToolTip
         If m_lCurHdrItem <> -1 Then
            Form1.TT.InitToolTip hwnd, "Multiline tooltip" & _
                                        vbCrLf & "for " & _
            Form1.ListView1.ColumnHeaders(m_lCurHdrItem + 1)
         End If
      End If
   End If
   
   WindowProc = CallWindowProc(m_lPrevWndProc, hwnd, uMsg, _
                               wParam, lParam)
End Function

Demonstration Form

VERSION 5.00
Object = "{831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0"; _
         "MSCOMCTL.OCX"
Begin VB.Form Form1 
   Caption            =   "Form1"
   ClientHeight       =   3195
   ClientLeft         =   60
   ClientTop          =   345
   ClientWidth        =   5745
   LinkTopic          =   "Form1"
   ScaleHeight        =   3195
   ScaleWidth         =   5745
   StartUpPosition    =   3    'Windows Default
   Begin MSComctlLib.ListView ListView1 
      Height          =   2595
      Left            =   120
      TabIndex        =   0
      Top             =   300
      Width           =   5475
      _ExtentX        =   9657
      _ExtentY        =   4577
      View            =   3
      LabelWrap       =   -1    'True
      HideSelection   =   -1    'True
      AllowReorder    =   -1    'True
      _Version        =   393217
      ForeColor       =   -2147483640
      BackColor       =   -2147483643
      BorderStyle     =   1
      Appearance      =   1
      NumItems        =   3
      BeginProperty ColumnHeader(1) _
                    {BDD1F052-858B-11D1-B16A-00C0F0283628}
         Text                   =   "Column 1"
         Object.Width           =   2540
      EndProperty
      BeginProperty ColumnHeader(2) _
                    {BDD1F052-858B-11D1-B16A-00C0F0283628}
         SubItemIndex           =   1
         Text                   =   "Column 2"
         Object.Width           =   2540
      EndProperty
      BeginProperty ColumnHeader(3) _
                    {BDD1F052-858B-11D1-B16A-00C0F0283628} 
         SubItemIndex           =   2
         Text                   =   "Column 3"
         Object.Width           =   2540
      EndProperty
   End
End
Attribute VB_Name               = "Form1"
Attribute VB_GlobalNameSpace    = False
Attribute VB_Creatable          = False
Attribute VB_PredeclaredId      = True
Attribute VB_Exposed            = False
Option Explicit

Private Declare Function FindWindowEx Lib "user32" _
                Alias "FindWindowExA" _
                (ByVal hWndParent As Long, _
                 ByVal hwndChildAfter As Long, _
                 ByVal lpszClass As String, _
                 ByVal lpszWindow As String) As Long

Dim m_HdrHwnd As Long
Public TT As CToolTip

Private Sub Form_Load()
   m_HdrHwnd = FindWindowEx(ListView1.hwnd, 0, _
                            "msvb_lib_header", vbNullString)
   Hook m_HdrHwnd
   
   Set TT = New CToolTip
End Sub

Private Sub Form_Unload(Cancel As Integer)
   Unhook m_HdrHwnd
End Sub

Downloads

Download demo project - 12 Kb


Comments

  • One Of The Keys For you to master the mizuno-world Is Actually Straight forward!

    Posted by Acuddence on 04/30/2013 01:29pm

    Hot questions regarding nike replied in addition to the reasons why you need to check out each word on this expose.[url=http://www.nikejpgolf.biz/]nike ゴルフ[/url] Yet another double sprain on nike [url=http://www.nikejpgolf.biz/nike-ゴルフボール-c-23.html]ナイキgolf[/url] Progressive questions regarding mizuno have been answered and consequently the reason why you should study every single term on this story. [url=http://www.nikejpgolf.biz/nike-アイアン-c-1.html]ゴルフ ナイキ[/url] Neutral piece of writing lets out 4 all new things of mizuno that not a soul is mentioning. [url=http://www.nikejpgolf.biz/nike-アイアン-c-1.html]ゴルフ ナイキ[/url] Their nike Venture Call - Users who loves practically nothing wins?!? [url=http://www.nikejpgolf.biz/nike-ゴルフシューズ-c-15.html]nike dunk[/url] Products and creation throughout Oregon -- nike has left without any goodbye [url=http://www.nikeyasuyi.com/]nike[/url] Things and creation throughout Idaho - mizuno has left without adios [url=http://www.nikeyasuyi.com/nikeナイキRunning-c-3.html]nike ランニング[/url] Some of the mizuno Corporate Speak -- Individuals who cares about nothing wins? [url=http://www.nikeyasuyi.com/nikeナイキDunk-c-9.html]nike dunk[/url] The nike Business Dialogue : Everyone who cares for practically nothing benefits?! [url=http://www.nikeyasuyi.com/nikeナイキDunk-c-9.html]nike dunk[/url] nike offers all new life to an old matter-- metallic traditional

    Reply
  • Nike Aura Max+instagram, wishes you confine the color to bear up on your feet!

    Posted by madytreathy on 04/22/2013 12:59pm

    Recollect in 2008, if not earlier, when Nike launched ahead of the separated shoe color projects, the slogan "Shoot Your Colours", "Nike PhotoiD" blueprint, [url=http://fossilsdirect.co.uk/glossarey.cfm]nike huarache[/url] reply has not been as avid as expected. Think, 2008 Canon IXUS 80 IS Digital file card arcade but purely 8 million pixels, Nokia, the plastic phone market is the only administration, NikeiD was advocate to color in the photos as a essence in return sneakers levy color, although provocative, but does bother some. Instagram which sort this passion make sport and elemental, Nike PHOTOiD homeopathic upgrade customization services, recently [url=http://markwarren.org.uk/property-waet.cfm]nike air max 90[/url] released a strange plan. That such iD can you appliance pictures as instagram account shoe color, for a short put up Nike Mood Max shoes and Nike Puff Max 1, Nike Show Max 90 953 options. Interested in children's shoes, you [url=http://markwarren.org.uk/goodbuy.cfm]nike free run uk[/url] can without exception go's legitimate website photoid.Nike.com, in reckoning to flick through other people's artistic industry, or you can try to upload your own instagram photo, base your own Nike Hauteur Max.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • Webinar on September 23, 2014, 2 p.m. ET / 11 a.m. PT Mobile commerce presents an array of opportunities for any business -- from connecting with your customers through mobile apps to enriching operations with mobile enterprise solutions. Join guest speaker, Michael Facemire, Forrester Research, Inc. Principal Analyst, as he discusses the new demands of mobile engagement and how application program interfaces (APIs) play a crucial role. Check out this upcoming webinar to learn about the new set of …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds