Announcement

Collapse
No announcement yet.

How to place tooltips on column headers in a ListView

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • How to place tooltips on column headers in a ListView

    I have the following ListView program which has a tooltips on a close button ( thanks to Borje) and
    I need to have tooltips place on the column headers. How do I do that ?




    Code:
    #COMPILE EXE
    #DIM ALL
    
    
    '  LV Checkbox
     ' with highlighted row if its  checkbox is selected
    #INCLUDE "WIN32API.INC"
    #INCLUDE "COMMCTRL.INC"
    
    %IDC_LISTVIEW = 1001
    %IDC_BUTTON1  = 1002
    %IDC_BUTTON2  = 1003
    
    
     GLOBAL KountChk AS LONG
     GLOBAL chkRow()  AS LONG
     GLOBAL hDlg AS DWORD
    
    
    
    '======================
    FUNCTION PBMAIN()
     ' Need to allocate more than 5 rows for chkRow()
     ' because user might repeatedly check and uncheck it
       REDIM chkRow(1 TO 100)
       ShowMain %HWND_DESKTOP
    END FUNCTION
    
    
    
    
    
    '============================================
    CALLBACK FUNCTION MainProc()
     LOCAL NMLV AS NMLISTVIEW
     LOCAL Checked AS LONG
     LOCAL lplvcd AS NMLVCUSTOMDRAW PTR
     LOCAL pLVDI AS LV_DISPINFO PTR
     LOCAL irr AS LONG
    
    
      SELECT CASE AS LONG CB.MSG
        CASE %WM_INITDIALOG
          STATIC hListView AS DWORD
          CONTROL HANDLE CB.HNDL, %IDC_LISTVIEW TO hListView
    
    
        CASE %WM_NOTIFY
          IF CB.NMID = %IDC_LISTVIEW THEN         ' ListView Notifications
            ' Obtain the NM_LISTVIEW UDT
            TYPE SET NMLV = CB.NMHDR$(SIZEOF(NMLV))
    
            SELECT CASE NMLV.hdr.code
    
              CASE %LVN_ITEMCHANGED
                  ' when a check box is checked or unchecked it displays
                  ' its status at the caption
                  Checked = ListView_GetCheckState(hListView, NMLV.iItem)
                  IF Checked < 0  THEN
                   ' when checked
                    IF KountChk < 5 THEN
                        DIALOG SET TEXT CB.HNDL, "Row"+STR$(NMLV.iItem+1) +" Checked: "+STR$(Checked)
                        KountChk = KountChk + 1
                        chkRow(KountChk) = NMLV.iItem+1
                    ELSE
                         MSGBOX " Exceeded 5 checked rows " + $CRLF + _
                           " You are allow to copy a maximum of 5 rows only" ,,"Error"
                        ' uncheck this box "
                        ListView_SetCheckState hListView, NMLV.iItem, 0
                        MSGBOX " checked rows " + STR$(chkRow(1)) + " , " + STR$(chkRow(2)) _
                         + " , " + STR$(chkRow(3)) + " , " + STR$(chkRow(4)) + ",  " + STR$(chkRow(5))
                     END IF
                  ELSE
                    ' when unchecked
                      DIALOG SET TEXT CB.HNDL, "Row" + STR$(NMLV.iItem+1) +" Checked: " + STR$(Checked)
                      ' see if user had uncheck an already checked box
                        FOR irr = 1 TO 100
                            IF chkrow(irr) = (NMLV.iItem + 1) THEN
                              '   removes it from the chkrow array
                                  chkrow(irr) = 0
                                  KountChk = KountChk - 1
                                  EXIT FOR
                            END IF
                        NEXT irr
                  END IF
    
    
              CASE %NM_CUSTOMDRAW
                         lplvcd = CB.LPARAM
    
                         SELECT CASE @lplvcd.nmcd.dwDrawStage
    
                            CASE %CDDS_PrePaint
                               FUNCTION = %CDRF_NOTIFYITEMDRAW
                               EXIT FUNCTION
    
                            CASE %CDDS_ItemPrePaint
                              '  paint the selected rows only
                              IF ListView_GetCheckState(hListView, @lplvcd.nmcd.dwItemSpec) = 0 THEN
                                  @lplvcd.clrTextBk = %WHITE
                                  @lplvcd.clrText = %BLACK
                               ELSE
                                '  paint the background for the selected rows only
                                   @lplvcd.clrTextBk = %RGB_PALETURQUOISE
                                   @lplvcd.clrText = %BLACK
                               END IF
    
                               FUNCTION = %CDRF_DODEFAULT
                               EXIT FUNCTION   'Tell the list view to draw itself
                         END SELECT
    
            END SELECT
           END IF
    
    
    
      END SELECT
    END FUNCTION
    
    
    
    '===============================
    ' Display and load in the data for the ListView
    FUNCTION DispListView(BYVAL hDlg AS DWORD, BYVAL lID AS LONG, BYVAL lColCnt AS LONG, _
            BYVAL lRowCnt AS LONG) AS LONG
      LOCAL lCol   AS LONG
      LOCAL lRow   AS LONG
      LOCAL lStyle AS LONG
    
      LISTVIEW GET STYLEXX hDlg, lID TO lStyle
      LISTVIEW SET STYLEXX hDlg, lID, lStyle OR  %LVS_EX_CHECKBOXES OR %LVS_EX_FULLROWSELECT
    
      ' Load column headers.
      FOR lCol = 1 TO lColCnt
        LISTVIEW INSERT COLUMN hDlg, lID, lCol, USING$("Column #", lCol), 0, 0
      NEXT lCol
    
      ' Load sample data.
      FOR lRow = 1 TO lRowCnt
        LISTVIEW INSERT ITEM hDlg, lID, lRow, 0, USING$("Column # Row #", lCol, lRow)
        FOR lCol = 1 TO lColCnt
          LISTVIEW SET TEXT hDlg, lID, lRow, lCol, USING$("Column # Row #", lCol, lRow)
        NEXT lCol
      NEXT lRow
    
      ' Auto size columns.
      FOR lCol = 1 TO lColCnt
        LISTVIEW FIT HEADER hDlg, lID, lCol
      NEXT lCol
    END FUNCTION
    
    
    
    
    '===================================
    ' create and show dialog
    FUNCTION ShowMain(BYVAL hParent AS DWORD) AS LONG
    
    
      DIALOG NEW hParent, "ListView - check", 187, 95, 320, 146, %WS_CAPTION OR %WS_SYSMENU, TO hDlg
    
      CONTROL ADD LISTVIEW, hDlg, %IDC_LISTVIEW, "Listview", 5, 5, 310, 110, _
          %WS_TABSTOP OR %WS_VISIBLE OR _
          %WS_BORDER OR %LVS_REPORT OR %LVS_SINGLESEL OR _
          %LVS_EX_DOUBLEBUFFER, %WS_EX_CLIENTEDGE  OR %LVS_EX_INFOTIP
    
    
    
    
      ' setup listview for 3 columns and 300 rows
        DispListView hDlg, %IDC_LISTVIEW, 3, 300
    
    
    
    
      ' --------------------------------------------------------
       LOCAL rtc AS RECT
       GetClientRect(hDlg, rtc)
       rtc.nRight  = rtc.nRight - 3
       rtc.nLeft   = rtc.nRight - GetSystemMetrics(%SM_CYCAPTION)- 2
       rtc.nBottom = -3
       rtc.nTop    = 3 - GetSystemMetrics(%SM_CYCAPTION)
       SetToolTipsArea (hDlg, rtc,  " Close and Start the computation ")
      ' --------------------------------------------------------
    
         DIALOG SHOW MODAL hDlg, CALL MainProc
    
    END FUNCTION
    
    
    
    
    
    
    
    '====================================================================
    ' Set tooltips for any given area in a dialog or control
    ' (I like %TTS_BALLOON style - remove if you don't like it)
    '--------------------------------------------------------------------
    FUNCTION SetToolTipsArea (BYVAL hWnd AS DWORD, ruc AS RECT, BYVAL sTxt AS STRING) AS LONG
      LOCAL hInst AS DWORD, ti AS TOOLINFO
      STATIC hToolTip AS DWORD
    
      hInst = GetModuleHandle("")
    
      IF hToolTip  = 0 THEN
         hToolTip = CreateWindowEx(0, "tooltips_class32", "", %TTS_ALWAYSTIP OR %TTS_BALLOON, _
                                      0,0,0,0,0, BYVAL %NULL, hInst, BYVAL %NULL)
      END IF
    
      ti.cbSize   = SIZEOF (ti)
      ti.uFlags   = %TTF_SUBCLASS
      ti.hwnd     = hWnd
      ti.hinst    = hInst
      ti.lpszText = STRPTR(sTxt)
      ti.rect    = ruc
    
      FUNCTION = SendMessage(hToolTip, %TTM_ADDTOOL, 0, BYVAL VARPTR(ti))
    END FUNCTION

  • #2
    Hi Anne,

    Try this...

    Pierre

    Code:
      'To add at WM_INITDIALOG end...
     CASE %WM_INITDIALOG
      ...
      STATIC hListviewHeader     AS DWORD  'Listview header handle
      STATIC ListviewColumnCount AS DWORD  'Listview column count
      LOCAL  index               AS WORD   'Used for looping
      LOCAL  ListviewColumnRect  AS RECT   'Used to store a column rect
      hListviewHeader     = SendMessage(hListView, %LVM_GETHEADER, 0, BYVAL 0) 'Get listview header handle
      ListviewColumnCount = SendMessage(hListviewHeader, %HDM_GETITEMCOUNT, 0, BYVAL 0) 'Get listview column count
      DIM ListviewHeaderToolTipId(1 TO ListviewColumnCount) AS STATIC WORD 'Memorize listview header tooltip id 
      FOR index = 1 TO ListviewColumnCount 'For all listview column
        SendMessage(hListviewHeader, %HDM_GETITEMRECT, index - 1, VARPTR(ListviewColumnRect)) 'Get listview header column rectangle
        ListviewHeaderToolTipId(index) = SetToolTipsArea(hListviewHeader, _ 'Set tooltip using Börje function
                                                         ListviewColumnRect, _
                                                         "ListviewHeader" & STR$(index))
      NEXT
     
     ...
     
     'To add in CDDS_ItemPrePaint beginning...
     CASE %CDDS_ItemPrePaint
       FOR index = 1 TO ListviewColumnCount
         SendMessage(hListviewHeader, %HDM_GETITEMRECT, index - 1, VARPTR(ListviewColumnRect)) 'Get listview header column rectangle
         SetToolTipsArea(hListviewHeader, ListviewColumnRect, "", _
                         ListviewHeaderToolTipId(index)) 'Set new tooltip rect using Börje function
       NEXT
     
     ...
     
     'Replace SetToolTipsArea with this one...
     FUNCTION SetToolTipsArea(BYVAL hWnd AS DWORD, ttRect AS RECT, BYVAL sTxt AS STRING, OPTIONAL RecChangeId AS WORD) AS LONG
      'Set tooltips for any given area in a dialog or control
      LOCAL  ToolInf  AS TOOLINFO
      STATIC hToolTip AS DWORD
      STATIC hInst    AS DWORD
      STATIC ID       AS WORD
     
      IF hToolTip  = 0 THEN
        hInst    = GetModuleHandle("")
        hToolTip = CreateWindowEx(0, "tooltips_class32", "", %TTS_ALWAYSTIP OR %TTS_BALLOON, _
                                  0, 0, 0, 0, 0, BYVAL %NULL, hInst, BYVAL %NULL)
      END IF
     
      ToolInf.cbSize   = SIZEOF(TOOLINFO)
      ToolInf.uFlags   = %TTF_SUBCLASS 'OR %TTF_IDISHWND 'Indicates that the uId member is the window handle to the tool. If this flag is not set, uId is the tool's identifier.
      ToolInf.hwnd     = hWnd
      ToolInf.hinst    = hInst
      ToolInf.rrect    = ttRect
    
      IF VARPTR(RecChangeId) = 0 THEN 'New tooltip
        ToolInf.lpszText = STRPTR(sTxt)
        INCR ID
        ToolInf.uId = ID
        SendMessage(hToolTip, %TTM_ADDTOOL, 0, BYVAL VARPTR(ToolInf))  'Returns TRUE if successful
      ELSE 'Change rect for an existing tooltip
        ToolInf.uId = RecChangeId
        SendMessage(hToolTip, %TTM_NEWTOOLRECT, 0, BYVAL VARPTR(ToolInf))
      END IF
      FUNCTION = ID
     
     END FUNCTION
    Note that to do a cleaner work, one could subclass the listview
    to get HDN_ENDTRACKW listview header notification to update
    the listview tooltips rect instead of using CDDS_ItemPrePaint.
    Last edited by Pierre Bellisle; 16 Apr 2021, 04:56 PM.

    Comment


    • #3
      Thanks so much Pierre, it works well

      But Borje code contains InitCommonControls
      as shown below

      Then I read that this command is obsolete and is replace by InitCommonControlsEX


      How do I replace or code this command InitCommonControlsEX ?


      Code:
       '====================================================================
       ' Set tooltips for any given area in a dialog or control ' (I like %TTS_BALLOON style - remove if you don't like it)
       '--------------------------------------------------------------------  
      FUNCTION SetToolTipsArea (BYVAL hWnd AS DWORD, rc AS RECT, BYVAL sTxt AS STRING) AS LONG  
       LOCAL hInst AS DWORD, ti AS TOOLINFO  
       STATIC hToolTip AS DWORD      hInst = GetModuleHandle("")    
      IF hToolTip  = 0 THEN    
        [COLOR=#FF0000][B]InitCommonControls[/B][/COLOR]    
        hToolTip = CreateWindowEx(0, "tooltips_class32", "", %TTS_ALWAYSTIP OR %TTS_BALLOON, _
                                        0,0,0,0,0, BYVAL %NULL, hInst, BYVAL %NULL)  
       END IF    
      ti.cbSize   = SIZEOF (ti)
         ti.uFlags   = %TTF_SUBCLASS
         ti.hwnd     = hWnd
        ti.hinst    = hInst  
      ti.lpszText = STRPTR(sTxt)  
      ti.rect    = rc  
        FUNCTION = SendMessage(hToolTip, %TTM_ADDTOOL, 0, BYVAL VARPTR(ti))
      
      END FUNCTION

      Comment


      • #4
        go to -
        https://msdn.microsoft.com/en-us/dn308572.aspx
        search on InitCommonControls
        first link on search results is "InitCommonControls function (Windows)", click link
        You are correct, That no longer does anything, also note it took no parameters.
        Click link to "InitCommonControlsEX"
        Note it does take a parameter, a pointer to a structure "C" speak for UDT.
        The name of the PB UDT is probably the same or very similar.
        Click on link to structure.
        (for a bit later) - dwSize member should be 64 (two dwords)
        In dwICC table, item ICC_Win95_CLASSES mentions Tool-Tips.
        (The name of that constant/equate in PB includes is probably %ICC_Win95_CLASSES

        In your PB code create a variable of that type.
        Set members to 64 and %ICC_Win95_Classes.
        Get VARPTR of the variable, Put pointer as parameter in the call to InitCommonControlsEX.

        I hope saying how I got to answer is more help than simple answer of implementing code.

        Cheers,
        Dale

        Comment


        • #5
          How to call InitCommonControlsEx()

          Pierre

          Code:
          LOCAL icc AS INIT_COMMON_CONTROLSEX
          icc.dwSize = SIZEOF(INIT_COMMON_CONTROLSEX)
          icc.dwICC  = %ICC_LISTVIEW_CLASSES OR %ICC_BAR_CLASSES 'Cumulative class adding of listview, toolbar, statusbar, trackbar, and tooltip.
          InitCommonControlsEx(icc)
          Last edited by Pierre Bellisle; 16 Apr 2021, 04:56 PM.

          Comment


          • #6
            Thanks Pierre and Dale

            According to MSDN, in regards to InitCommonControlsEX() :

            Ensures that the common control DLL (Comctl32.dll) is loaded, and
            registers specific common control classes from the DLL
            An application must call this function before creating a common control.

            So can we place this call inside PBMain() before we add controls like Dialog, ListView etc
            as in the code below by calling InitCom
            first before adding Dialog and Listview controls ?

            Code:
            '======================
            FUNCTION PBMAIN()
             ' Need to allocate more than 5 rows for chkRow()
             ' because user might repeatedly check and uncheck it
               REDIM chkRow(1 TO 100)
               [B][COLOR=#FF0000]InitCom[/COLOR][/B]
               ShowMain %HWND_DESKTOP
            END FUNCTION
            
            
            '================================
            '  Ensures that the common control DLL (Comctl32.dll) is loaded, and
            '  registers specific common control classes from the DLL
            '  An application must call this function before creating a common control.
            SUB InitCom
               LOCAL icc AS INIT_COMMON_CONTROLSEX
               icc.dwSize = SIZEOF(INIT_COMMON_CONTROLSEX)
              'Cumulative class adding of listview, toolbar, statusbar, trackbar, and tooltip.
               icc.dwICC  = %ICC_LISTVIEW_CLASSES OR %ICC_BAR_CLASSES
               InitCommonControlsEx(icc)
            END SUB
            
            
            
            '===================================
            ' create and show dialog
            FUNCTION ShowMain(BYVAL hParent AS DWORD) AS LONG
            
              DIALOG NEW hParent, "ListView - check", 187, 95, 320, 146, %WS_CAPTION OR %WS_SYSMENU, TO hDlg
            
              CONTROL ADD LISTVIEW, hDlg, %IDC_LISTVIEW, "Listview", 5, 5, 310, 110, _
                  %WS_TABSTOP OR %WS_VISIBLE OR _
                  %WS_BORDER OR %LVS_REPORT OR %LVS_SINGLESEL OR _
                  %LVS_EX_DOUBLEBUFFER, %WS_EX_CLIENTEDGE  OR %LVS_EX_INFOTIP
            
            
              ' setup listview for 3 columns and 300 rows
                DispListView hDlg, %IDC_LISTVIEW, 3, 300
            
              ' --------------------------------------------------------
               LOCAL rtc AS RECT
               GetClientRect(hDlg, rtc)
               rtc.nRight  = rtc.nRight - 3
               rtc.nLeft   = rtc.nRight - GetSystemMetrics(%SM_CYCAPTION)- 2
               rtc.nBottom = -3
               rtc.nTop    = 3 - GetSystemMetrics(%SM_CYCAPTION)
               SetToolTipsArea (hDlg, rtc,  " Close and Start the computation ")
              ' --------------------------------------------------------
            
               DIALOG SHOW MODAL hDlg, CALL MainProc
            
            END FUNCTION


            Comment


            • #7
              Have you tried to do nothing? As far as I know, the latest PBWIN compiler calls InitCommonControls when using DDT. Otherwise, how could support common controls like the ListView?

              What I don't know is if it enables all the classes or only these that are supported by DDT.
              Forum: http://www.jose.it-berater.org/smfforum/index.php

              Comment


              • #8
                Yes Jose, when I commented off InitCom , it also works. Perhaps it is psychology to have InitCom first ?

                Comment


                • #9
                  Hey,

                  I think I remember a PowerBASIC saying about the use of InitCommonControls(Ex)
                  not being necessary anymore in some circumstances, but could not find the article to get the details.
                  I think it was needed with PB/Win 7.x and prior.
                  On my side, I almost never use it and never had any problems...

                  Pierre



                  Comment


                  • #10
                    ff you had bothered to actually read the link provided by Mr. Yarker in Post#4, you'd know when to call which version of the Common Controls initialization function and how to call it. If you then read the PB help, you'd know how to DECLARE and CALL external functions. You'd also know how you can work around the fact that apparently the compiler is sometimes calling this function for you, whether you like it or not.

                    FWIW, none of the responses you have received in this thread are complete, except Mr. Yarkers.. but that DOES require you know - or find out - which specific controls are part of the Microsoft Common Controls.

                    Yes, I could have given you the one sentence answer to your conundrum. No, I didn't and no, I won't.
                    Michael Mattias
                    Tal Systems (retired)
                    Port Washington WI USA
                    [email protected]
                    http://www.talsystems.com

                    Comment

                    Working...
                    X