Announcement

Collapse
No announcement yet.

how to determine if a scrollbar exists (Listview)

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

  • how to determine if a scrollbar exists (Listview)

    Hello,

    I am looking for a way to determine if a scrollbar exists on a listView control. I tried this:
    Code:
    LOCAL si AS SCROLLINFO
    LOCAL retval, w, h AS LONG
                  si.cbSize = SIZEOF(si)
                  si.fMask  = %SIF_ALL
                  retval= getscrollinfo(GetDlgItem(hdlg, %ID_ListView), %SB_HORZ,  si)
    This did not work. I would think that if there is no scroll bar, then the "revtal" variable would be zero--but it is 1. Then I thought that maybe there is some variable in the scrollinfo structure that could be used. However, I found none. The nMax and nMin both have strange values although there is no scrollbar?!?
    The next attempt was to see how long the rect of one of the listview's item is:
    Code:
    local rc as rect, hlv as long
                  CONTROL HANDLE CBHNDL, %ID_LIstView TO hLV
                  rc.nleft = %LVIR_BOUNDS
                  SendMessage(hlv, %LVM_GETITEMRECT, 1, VARPTR(rc))
    I thought if the rect's length is as wide as the controls's width, then that might signal no scrollbar. Oddly enough, it appears that when I combine both attempts then I see something that might work. The scrollinfo.nMax is just a pixel shorter than the rc.nright. So maybe:
    Code:
    IF scrollinfo.nMax + 1 = (rect.NRight - Rect.NLeft) then
      'there is no scrollbar
    else
      'there is a scrollbar
    end if
    Does this make sense? Can somebody think of a better way of doing it?

    I have thought of looping through all the columns to total the columns' widths and then comparing that do the size of the control; however, that seems like a guess and check idea that ignores any scrollbar data. Any comments or insight would be greatly appreciated?
    Bill Scharf

  • #2
    Are you sure you can't learn whatever it is you are trying to learn without mucking about trying to figure out if the scrollbar is visible?

    I mean, you can use Listview_Get[SUB]ItemRect for any row and/or column.. that is automatically adjusted for the presence/absence of a scrollbar.

    ???

    For that matter, if ListView_GetItemRect does not return the same width or height as does GetClientRect(), you may assume the listview's vertical and/or horizontal scroll bars are in play. (I'm pretty sure of that, but you'll have to give it a try)
    Last edited by Michael Mattias; 1 Dec 2008, 01:49 PM.
    Michael Mattias
    Tal Systems Inc. (retired)
    Racine WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      No, I am not sure. It turns out that my above assmption is wrong.

      The greater and more complex underlying issue is back to a previous post about parallel scrolling listviews. If one listview has a horizontal scrollbar then windows automatically makes the veritcal scrollbar one row bigger. Let me show you this example:
      Code:
      #COMPILE EXE
      #DIM ALL
      #REGISTER ALL
      
      %USEMACROS = 1
      #INCLUDE "h:\source08\winapi\WIN32API.INC"
      #INCLUDE "h:\source08\winapi\COMMCTRL.INC"
      
      %ID_LVA_LV          = 100       ' Main listview identifier #1
      %ID_LVB_LV          = 200       ' Main listview identifier #2
      %IDTEST             = 300
      GLOBAL hDlg, hDlgLv AS DWORD
      GLOBAL h1, h2 AS LONG
      
      '------------------------------------------------------------------------------
      ' Listview Subclass Procedure
      ' This subclass callback is for both listivews, despite the fact that they have
      '      two different parent dialogs.
      '------------------------------------------------------------------------------
      CALLBACK FUNCTION ListView_SubclassProc
        STATIC sbUpdating AS BYTE
        STATIC ControlID, secondaryID, ControlHdlg, SecondaryHDLG, hctl, hctl2 AS LONG
        LOCAL i AS LONG
        LOCAL ms_lvi AS LV_ITEM
      
        SELECT CASE CBMSG
      
               CASE %WM_VSCROLL, %WM_MOUSEWHEEL , %WM_KEYFIRST TO %WM_KEYLAST, %WM_MOUSEFIRST TO %WM_MOUSELAST'Note HSCROLL is not needed
                          'the left ListView should not be allowed to scroll right
                          IF CBMSG = %WM_KEYDOWN AND CBWPARAM = %VK_RIGHT AND GetDlgCtrlID(CBHNDL) = %ID_LVA_LV THEN'
                              EXIT FUNCTION
                          END IF
                          ControlID   =IIF&(GetDlgCtrlID(CBHNDL) = %ID_LVA_LV, %ID_LVA_LV, %ID_LVB_LV)
                          secondaryID = IIF&(GetDlgCtrlID(CBHNDL) = %ID_LVA_LV, %ID_LVB_LV, %ID_LVA_LV)
                          ControlHdlg = IIF&(GetDlgCtrlID(CBHNDL) = %ID_LVA_LV, hDlgLv, hdlg)
                          SecondaryHDLG =  IIF&(GetDlgCtrlID(CBHNDL) = %ID_LVB_LV, hDlgLv, hdlg)
                          'post a custom message.  THis allows all the scrolling to complete
                          'without this, the scrolling will trigger before it is complete and you might be off by 1 row
                          PostMessage CBHNDL, %WM_USER + 401, 0, 0
      
               CASE %WM_USER + 401
                   LOCAL rc1 AS RECT, rc2 AS RECT
                   rc1.nLeft = %LVIR_BOUNDS
                   CONTROL HANDLE ControlHdlg, ControlID TO hctl
                   CONTROL HANDLE SecondaryHDLG, secondaryID TO hctl2
                   'find how far down the initiating control is scrolled
                   SendMessage hctl, %LVM_GETITEMRECT, 0, VARPTR(rc1)
                   'find how far down the secondary control is
                   rc2.nLeft = %LVIR_BOUNDS
                   i=SendDlgItemMessage (SecondaryHDLG,secondaryID,%LVM_GETITEMRECT, 0, VARPTR(rc2))
                   'scrolling the secondary control to where the initiating control is
                   i=SendDlgItemMessage (SecondaryHDLG, secondaryID, %LVM_SCROLL, 0,rc2.nTop - rc1.nTop)
                   'get the selected one from the initiating control and select the same one in the secondary control
                    i=ListView_GetNextItem(hctl, -1, %LVNI_FOCUSED OR %LVNI_SELECTED)
                    IF i > -1 THEN'select the other one
                                  ms_lvi.stateMask = %LVIS_FOCUSED OR %LVIS_SELECTED
                                  ms_lvi.state = %LVIS_FOCUSED OR %LVIS_SELECTED
                                  SendMessage hctl2, %LVM_SETITEMSTATE, -1, VARPTR(ms_lvi)
                                  SendMessage hctl2, %LVM_SETITEMSTATE, i, VARPTR(ms_lvi)
                    END IF
      
               CASE %WM_DESTROY
                    FUNCTION = CallWindowProc(GetWindowLong(CBHNDL, %GWL_USERDATA), CBHNDL, CBMSG, CBWPARAM, CBLPARAM)
                    SetWindowLong CBHNDL, %GWL_WNDPROC, GetWindowLong(CBHNDL, %GWL_USERDATA)
                    EXIT FUNCTION
      
        END SELECT
      
        FUNCTION = CallWindowProc(GetWindowLong(CBHNDL, %GWL_USERDATA), CBHNDL, CBMSG, CBWPARAM, CBLPARAM)
      END FUNCTION
      
      '------------------------------------------------------------------------------
      ' Main dialog callback procedure
      '------------------------------------------------------------------------------
      CALLBACK FUNCTION dlgMain
        LOCAL i AS LONG, lvi AS LV_ITEM, lvc AS LV_COLUMN, zText AS ASCIIZ * 1024, rc AS RECT
      
        SELECT CASE CBMSG
      
               CASE %WM_INITDIALOG
                    ' Prepare example listview items...
                    lvi.mask    = %LVIF_TEXT
                    lvi.pszText = VARPTR(zText)
                    FOR i = 1 TO 24
                        lvi.iItem = i-1
                        zText     = STR$(i)+":00 " + "Appointment"
                        CONTROL SEND CBHNDL, %ID_LVB_LV, %LVM_INSERTITEM, 0, VARPTR(lvi)
                        zText     = STR$(i)+":00"
                        CONTROL SEND hDlgLv, %ID_LVA_LV, %LVM_INSERTITEM, 0, VARPTR(lvi)
                    NEXT i
                  ' Set column info...
                    GetClientRect GetDlgItem(CBHNDL, %ID_LVb_LV), rc
                    zText       = "Employee2"
                    lvc.mask    = %LVCF_WIDTH OR %LVCF_TEXT
                    lvc.pszText = VARPTR(zText)
                    lvc.cx      = rc.nRight-2
                    CONTROL SEND CBHNDL, %ID_LVB_LV, %LVM_INSERTCOLUMN, 0, VARPTR(lvc)
                    zText       = "Employee1"
                    CONTROL SEND CBHNDL, %ID_LVB_LV, %LVM_INSERTCOLUMN, 0, VARPTR(lvc)
                    zText       = "Time"
                    CONTROL SEND hDlgLv, %ID_LVA_LV, %LVM_INSERTCOLUMN, 0, VARPTR(lvc)
                     FOR i = 0 TO 24
                           ztext = "Another appt at" + STR$(i+1) + ":00"
                           ListView_SetitemText(h1, i, 1, ztext)
                     NEXT i
                    ' Subclass listviews
                    SetWindowLong GetDlgItem(hDlgLv, %ID_LVA_LV), %GWL_USERDATA, SetWindowLong(GetDlgItem(hDlgLv, %ID_LVA_LV), %GWL_WNDPROC, CODEPTR(ListView_SubclassProc))
                    SetWindowLong GetDlgItem(CBHNDL, %ID_LVB_LV), %GWL_USERDATA, SetWindowLong(GetDlgItem(CBHNDL, %ID_LVB_LV), %GWL_WNDPROC, CODEPTR(ListView_SubclassProc))
      
                    
               CASE %WM_COMMAND
                    ' Exit program...
                    IF (CBCTL = %IDOK) AND (CBCTLMSG = %BN_CLICKED) THEN DIALOG END CBHNDL
                    IF (CBCTL = %IDTEST) AND (CBCTLMSG = %BN_CLICKED) THEN
                        checkit(CBHNDL)
                    END IF
               CASE %WM_SIZE
                   'LOCAL dlgxx, dlgyy AS LONG
                   'IF CBLPARAM<>%SIZE_MINIMIZED THEN
                   '   DIALOG GET SIZE CBHNDL TO DlgXX, DlgYY
                   '   CONTROL SET SIZE CBHNDL, %ID_LVB_LV  , dlgxx-20,129
                   'end if
                   
      
        END SELECT
      
      END FUNCTION
      '------------------------------------------------------------------------------
      ' Secondary callback procedure
      '------------------------------------------------------------------------------
      CALLBACK FUNCTION dlgMain2
            'subclassing this dialog's ListView could go here if you wanted...but why not put them in the same place
      END FUNCTION
      
      '------------------------------------------------------------------------------
      ' Program Start Point
      '------------------------------------------------------------------------------
      
      
      FUNCTION PBMAIN
        LOCAL lStyle AS LONG
        DIALOG NEW 0, "Parallel ListView Test", , , 250, 153, %DS_MODALFRAME OR %WS_CAPTION OR %WS_SYSMENU OR %WS_THICKFRAME OR %WS_POPUP TO hDlg
        DIALOG NEW hDlg, "", 0, 0, 65-2, 130-3-10, %WS_CHILD OR %WS_VISIBLE TO hDlgLv
      
        CONTROL ADD $WC_LISTVIEW, hDlgLv, %ID_LVA_LV, "", 0, 0, 75, 129,  %WS_CHILD OR %WS_VISIBLE OR _
                      %WS_TABSTOP OR %LVS_REPORT OR %LVS_SHOWSELALWAYS OR %LVS_SINGLESEL, %WS_EX_LEFT OR _
                      %WS_EX_CLIENTEDGE OR %WS_EX_RIGHTSCROLLBAR
        CONTROL ADD $WC_LISTVIEW, hDlg, %ID_LVB_LV, "", 64, 0, 125, 129,  %WS_CHILD OR %WS_VISIBLE OR _
                      %WS_TABSTOP OR %LVS_REPORT OR %LVS_SHOWSELALWAYS OR %LVS_SINGLESEL, %WS_EX_LEFT OR _
                      %WS_EX_CLIENTEDGE OR %WS_EX_RIGHTSCROLLBAR
        CONTROL HANDLE hDlgLv, %ID_LVa_LV TO h2
        CONTROL HANDLE hDlg, %ID_LVb_LV TO h1
        CONTROL ADD BUTTON, hDlg, %IDOK, "Exit", 100, 135, 50, 15
        CONTROL ADD BUTTON, hDlg, %IDtest, "test", 160, 135, 50, 15
        'The listviews lack style...so let's add styles
        '  This is done separately and after all controls are added just so I could play around with various styles
          lStyle = ListView_GetExtendedListViewStyle(h2)
          ListView_SetExtendedListViewStyle(h2, lStyle OR %LVS_EX_FULLROWSELECT _
              OR %LVS_EX_GRIDLINES)
          lStyle = ListView_GetExtendedListViewStyle(h1)
          ListView_SetExtendedListViewStyle(h1, lStyle OR %LVS_EX_FULLROWSELECT _
              OR %LVS_EX_GRIDLINES)
      
        DIALOG SHOW MODELESS hDlgLv CALL dlgMain2
        DIALOG SHOW MODAL hDlg CALL dlgMain
      END FUNCTION
      
      
      FUNCTION checkit(hdlg AS LONG) AS LONG
      LOCAL si AS SCROLLINFO
      LOCAL billstest, w, h, hlv, ww, hh AS LONG
      LOCAL rc AS rect
      
                    si.cbSize = SIZEOF(si)
                    si.fMask  = %SIF_ALL
                    billstest = getscrollinfo(GetDlgItem(hdlg, %ID_LVB_LV), %SB_HORZ,  si)
                    SLEEP 1
                    IF si.nmax = 0 AND si.nmin = 0 THEN
                              MSGBOX "no scroll bar"
                    END IF
                    CONTROL GET SIZE hdlg, %ID_LVB_LV TO w , h
                    SLEEP 1
                    CONTROL HANDLE hdlg, %ID_LVB_LV TO hLV
                    'ListView_GetItemRect hlv&, 1, rc, %LVIR_BOUNDS
                    rc.nleft = %LVIR_BOUNDS
                    SendMessage(hlv, %LVM_GETITEMRECT, 1, VARPTR(rc))
                     SLEEP 1
                    DIALOG UNITS hdlg, w, h TO PIXELS ww, hh
                    SLEEP 1
                    'if si.nmax + 1 = (rc.nright-rc.Nleft) then
                    '      msgbox "no scrollbar"
                    'end if
                    'if si.npage = si.NMax -1 then
                    '      msgbox "no scrollbar"
                    'end if
                    IF (rc.nRight+20-rc.Nleft) > ww THEN
                          MSGBOX "scroll bar"
                    END IF
      END FUNCTION
      If you compile this sample program then the right-hand side listview scrolls with the left-hand side listview--no problems. But obviously I have accounted for the scroll bar by making the "employee" listivew a bit longer. Now if you uncomment these lines:
      Code:
                   LOCAL dlgxx, dlgyy AS LONG
                   IF CBLPARAM<>%SIZE_MINIMIZED THEN
                      DIALOG GET SIZE CBHNDL TO DlgXX, DlgYY
                      CONTROL SET SIZE CBHNDL, %ID_LVB_LV  , dlgxx-20,129
                   END IF
      then run the code again, you will see no scrollbar. I can now scroll the time listview down to "24:00" while on the same line the employee listview is at "23:00". I have thought of several senarios that might work to correct this, but each one has boiled down to working with the scrollbar or without the scrollbar.
      As always, I am open to any ideas. I have a lot of work to do and am trying to get this fixed ASAP.



      Right now (as you can see from the code above) I am exploring if the item's rect is bigger than the control's width. After lots of "guess and check" it appears that I have to adjust it by 20. On my VIsta Business machine this will work:
      IF (ItemRect.nRight+20-ItemRect.Nleft) > widthOfControl THEN
      MSGBOX "scroll bar"
      END IF
      That offset makes me think it might be theme or OS dependent, so I am not sure of this solution either.

      Any other ideas or obvious holes in the logic?
      Bill Scharf

      Comment


      • #4
        Bill --

        I spent well over an hour with your code, and I'm pretty confused.

        To make it easier for other people to help you, I'd suggest making your variable and equate names more descriptive and consistent. For example...

        Code:
        CONTROL HANDLE hDlgLv, %ID_LVa_LV TO h2
        CONTROL HANDLE hDlg, %ID_LVb_LV TO h1
        A is 2 and B is 1? That makes it much more difficult to keep them straight. Maybe use A and B for everything, or better yet use Main and Child. Left and Right?

        What does Lv mean?

        hctl? hctl2?

        If you make your source code more human-readable, more humans will be able to help you.

        I agree with MCM: I don't think you need to detect the scroll bar. That's a hack. If you straighten out the main code, the problem will disappear.

        -- Eric
        "Not my circus, not my monkeys."

        Comment


        • #5
          I synched up a couple of listviews HORIZONTALLY by subclassing and catching WM_HSCROLL. Never even thought about scrollbars.

          I would think you could do the same catching WM_VSCROLL.
          Michael Mattias
          Tal Systems Inc. (retired)
          Racine WI USA
          [email protected]
          http://www.talsystems.com

          Comment


          • #6
            Use the following code to check if scrollbars are visible in a window:

            Code:
            IF (GetWindowLong(hWndListView, %GWL_STYLE) AND %WS_VSCROLL) THEN
              ' Vertical scrollbar is visible
            END IF  
            
            IF (GetWindowLong(hWndListView, %GWL_STYLE) AND %WS_HSCROLL) THEN
              ' Horizontal scrollbar is visible
            END IF
            Dominic Mitchell
            Phoenix Visual Designer
            http://www.phnxthunder.com

            Comment


            • #7
              You can also use the GetScrollBarInfo function.
              Dominic Mitchell
              Phoenix Visual Designer
              http://www.phnxthunder.com

              Comment


              • #8
                Thank you all for your help.
                Eric-
                I am sorry my code example was not in very good style. I admit it is very bad. This is just a sample quickly through together because the real app is about 10,000 lines. The "1" and "2" notation was to denote which listview was the originator of the message. The "2" was the listview which needed to get the same message. The "A" and "B" were to denote which listviews were which. The "A" is on the left and "B" on the right. "LV" means listview. "hctl" is what I (and I thought many others?) used for "handle of the control." I thought this was like 'hdlg' was common for the handle of the dialog. I appreciate your help and your time.

                MCM-
                As for synching to listviews using only the WM_VSCROLL command, this will fail to keep the listviews lined up if
                1. The listviews have a different amount of rows OR
                2. The listviews have the same number of rows, but one listview has a horizontal scrollbar and one does not.
                My problem is that the listview columns for the left-most listview are user-controlled. The user could have 1 or 50. So the user may have a scrollbar or not.

                Dominic-
                I have tried the GetScrollBarInfo function and had no success. Maybe I don't know what to look for...? I have tried:
                Code:
                local si as SCROLLINFO
                x= getscrollinfo(GetDlgItem(hdlg, %ID_LISVIEW), %SB_HORZ,  si)
                I test "x" and it is "1" every time. So I test si.nMax and si.nMin and I cannot determine anything to denote "no scrollbar." Then I think I went insane and started tying to find relationships between the scrollinfo structure, the width of the control, and the rect of the listview item. That is how I ended up with this:
                Code:
                              IF (RectOfListViewItem.nRight+20-RectOfListViewItem.Nleft) > Width of control THEN
                                    MSGBOX "scroll bar"
                              END IF
                Anyway, I will look into (GetWindowLong(hWndListView, %GWL_STYLE) AND %WS_HSCROLL) and I thank you very much.

                My thought was that detecting a scrollbar would allow me to resize the listview to account for that extra line of scrolling that windows adds. After hours and hours I have found another way around this, but it is a bad hack. I will post that separately.
                Bill Scharf

                Comment


                • #9
                  Ok, this is is NOT a great solution, but does work. THe problem was that listview B (the one with a horizontal scrollbar) could scroll down another line. This is corrected if a message is sent to listview A. This is any message for which the subclassing procedure is trapping. So, I send an empty %WM_MOUSEMOVE to listView A and then the listview corrects itself. Here are the changes in the subclassing procedure:
                  Code:
                  CALLBACK FUNCTION ListView_SubclassProc
                    STATIC sbUpdating AS BYTE
                    STATIC ControlID, secondaryID, ControlHdlg, SecondaryHDLG, hctl, hctl2, correctIt AS LONG
                    LOCAL i AS LONG
                    LOCAL ms_lvi AS LV_ITEM
                  
                    SELECT CASE CBMSG
                  
                           CASE %WM_VSCROLL, %WM_MOUSEWHEEL , %WM_KEYFIRST TO %WM_KEYLAST, %WM_MOUSEFIRST TO %WM_MOUSELAST'Note HSCROLL is not needed
                                      'the left ListView should not be allowed to scroll right
                                      IF CBMSG = %WM_KEYDOWN AND CBWPARAM = %VK_RIGHT AND GetDlgCtrlID(CBHNDL) = %ID_LVA_LV THEN'
                                          EXIT FUNCTION
                                      END IF
                                      ControlID   =IIF&(GetDlgCtrlID(CBHNDL) = %ID_LVA_LV, %ID_LVA_LV, %ID_LVB_LV)
                                      secondaryID = IIF&(GetDlgCtrlID(CBHNDL) = %ID_LVA_LV, %ID_LVB_LV, %ID_LVA_LV)
                                      ControlHdlg = IIF&(GetDlgCtrlID(CBHNDL) = %ID_LVA_LV, hDlgLv, hdlg)
                                      SecondaryHDLG =  IIF&(GetDlgCtrlID(CBHNDL) = %ID_LVB_LV, hDlgLv, hdlg)
                                      'post a custom message.  THis allows all the scrolling to complete
                                      'without this, the scrolling will trigger before it is complete and you might be off by 1 row
                                      PostMessage CBHNDL, %WM_USER + 401, 0, 0
                                      debugprint ("cbmsg=" + STR$(CBMSG))
                  '*************Hack Starts here**********************
                  'I could have specified messages about selecting, scrolling, etc, but it seems 
                  'to catch everything if I just look for mousemove.
                                      IF CBMSG <> %WM_MOUSEMOVE THEN
                                          correctIt = 1
                                      ELSE
                                          correctIt = 0
                                      END IF
                  
                           CASE %WM_USER + 401
                               LOCAL rc1 AS RECT, rc2 AS RECT
                               rc1.nLeft = %LVIR_BOUNDS
                               CONTROL HANDLE ControlHdlg, ControlID TO hctl
                               CONTROL HANDLE SecondaryHDLG, secondaryID TO hctl2
                               'find how far down the initiating control is scrolled
                               SendMessage hctl, %LVM_GETITEMRECT, 0, VARPTR(rc1)
                               'find how far down the secondary control is
                               rc2.nLeft = %LVIR_BOUNDS
                               i=SendDlgItemMessage (SecondaryHDLG,secondaryID,%LVM_GETITEMRECT, 0, VARPTR(rc2))
                               'scrolling the secondary control to where the initiating control is
                               i=SendDlgItemMessage (SecondaryHDLG, secondaryID, %LVM_SCROLL, 0,rc2.nTop - rc1.nTop)
                               'get the sel0ected one from the initiating control and select the same one in the secondary control
                                i=ListView_GetNextItem(hctl, -1, %LVNI_FOCUSED OR %LVNI_SELECTED)
                                IF i > -1 THEN'select the other one
                                              ms_lvi.stateMask = %LVIS_FOCUSED OR %LVIS_SELECTED
                                              ms_lvi.state = %LVIS_FOCUSED OR %LVIS_SELECTED
                                              SendMessage hctl2, %LVM_SETITEMSTATE, -1, VARPTR(ms_lvi)
                                              SendMessage hctl2, %LVM_SETITEMSTATE, i, VARPTR(ms_lvi)
                                END IF
                  '***********Core of the Hack is here********************
                                IF ControlID = %ID_LVB_LV AND CORRECTIT=1 THEN
                  'now we send a message to listview "A" so that everything will line up once again.
                                      sendmessage hctl2, %WM_MOUSEMOVE, 0, 0
                                END IF
                  
                           CASE %WM_DESTROY
                                FUNCTION = CallWindowProc(GetWindowLong(CBHNDL, %GWL_USERDATA), CBHNDL, CBMSG, CBWPARAM, CBLPARAM)
                                SetWindowLong CBHNDL, %GWL_WNDPROC, GetWindowLong(CBHNDL, %GWL_USERDATA)
                                EXIT FUNCTION
                  
                    END SELECT
                  
                    FUNCTION = CallWindowProc(GetWindowLong(CBHNDL, %GWL_USERDATA), CBHNDL, CBMSG, CBWPARAM, CBLPARAM)
                  END FUNCTION
                  Bill Scharf

                  Comment


                  • #10
                    As for synching to listviews using only the WM_VSCROLL command, this will fail to keep the listviews lined up if
                    1. The listviews have a different amount of rows OR
                    2. The listviews have the same number of rows, but one listview has a horizontal scrollbar and one does not.
                    Huh?

                    If the two listview controls have different numbers of items (rows), they cannot be synched; at some point one listview is simply going to run out of rows.

                    And as long as you synch to the TOP row the presence of a horizontal scrollbar is moot... and why you'd even have a horizontal scrollbar in this case is a mystery.

                    Wait a minute...why are you using two separate listview controls instead of one listview control with more subitems (columns)?

                    I must be missing something.


                    MCM
                    Michael Mattias
                    Tal Systems Inc. (retired)
                    Racine WI USA
                    [email protected]
                    http://www.talsystems.com

                    Comment


                    • #11
                      MCM-

                      Thank you for your reponse. I will try to start from the beginning and give a quick overview:
                      1. I was given a project with many requires, among which are:
                      A. Use a listview as a grid-type control
                      B. No third-party tools or controls are allowed--only that which is in PB
                      C. The first column is time and the rest of the columns represent employees.
                      D. There can be 1 to 50 columns for employees.
                      E. The user must be able to scroll through the employee columns horizontally, but the time column must stay on the screen. I refer to this as the time column being "frozen" desipite the horizontal scrolling of the other columns.

                      2. Issues to overcome
                      A. There are no other PB controls that seem to be of any help.
                      B. To "freeze" the time column, I could try a few different approaches:
                      i. Constantly change the first column visible to be the time column. This does not work well as it will scroll a little, then jump.
                      ii. Use two different listviews with the same amount of lines and try to keep them synched vertically. This seemed to have no big draw-backs.
                      iii. Use dynamically changing labels. The labels could change as the employee listview scrolls. The problem is that the label itself cannot scroll.
                      C. In going with option ii above, I synched them vertically. It worked well, but not perfectly. The problem exists when one listview has a scrollbar and the other does not.

                      I think I need to stop here and try to draw a picture with words. Picture two listviews are the same size and shape. They are right beside each other. Each one is perfectly full. They have no scollbars, but are 1 pixel away in either direction from getting a scrollbar. Now make the column in listview #1 a pixel wider. Instantly you have a horizontal scrollbar. That scrollbar now covers up the bottom row of listview #1. That is where the problem is. Now, to see the bottom row, you must scroll down one row--but if you scroll down, then listview one is out of synch with listview 2.

                      The idea was that when a scrollbar exists, make the listview itself a row bigger, so that this cannot happen. Then the listviews will appear to always be the same size, and when a scrollbar appears, it will look like it is appearing underneath the listview instead of on the bottom row of the listview.

                      Does this make more sense? This time of the year is a lot of long hours, so my brian may be a little fried.
                      Bill Scharf

                      Comment


                      • #12
                        Listview with movable vertical and horizontal splitter windows

                        You might find some usefull ideas here..
                        http://www.powerbasic.com/support/pb...ad.php?t=24702
                        Rgds, Dave

                        Comment


                        • #13
                          You can also do it by making the employee listview control minus the built-in horizontal scrollbar, and adding a separate horizontal scrollbar UNDERNEATH that control.

                          Scroll the employee listview horizontally (Listview_Scroll) in response to notification messages from the standlone scrollbar, and vertically in response to scroll notifications from the time listview control.

                          I think the picture looks like
                          Code:
                          TIME     SMITH    JONES     BROWN   GREEN   JOHNSON   WILSON .....
                          8:00          X
                          9:00                    X
                          10:00  
                          .....
                                       >[ Scrollbar to scroll employees horizontally                           ]<
                          Michael Mattias
                          Tal Systems Inc. (retired)
                          Racine WI USA
                          [email protected]
                          http://www.talsystems.com

                          Comment


                          • #14
                            I have tried the GetScrollBarInfo function and had no success. Maybe I don't know what to look for...? I have tried:
                            Code:
                            local si as SCROLLINFO
                            x= getscrollinfo(GetDlgItem(hdlg, %ID_LISVIEW), %SB_HORZ,  si)
                            GetScrollInfo <> GetScrollBarInfo
                            Dominic Mitchell
                            Phoenix Visual Designer
                            http://www.phnxthunder.com

                            Comment


                            • #15
                              Thank you all for your suggestions. I have spent days searching the PB forums on this project and would have pulled my hair out otherwise. I think MCM's separate scrollbar idea is good as well. The issue still exists though--when should the listview control have a scroll bar and when should it not. Our users would not want a scrollbar there which did nothing. I would still have to find a way to show/hide it when needed.

                              Dominic
                              I am sorry for mistyping "getscrollinfo." I did use that function and could not figure out how to tell when no scrollbar exists. The function always returns 1 with or without a scrollbar, and I could not find any member of the si structure that could help. Do you know how to tell if a scrollbar exists from what is returned by GetScrollInfo?
                              Bill Scharf

                              Comment


                              • #16
                                The back of my mind tells me this is like syncing 2 listviews in VB in the days of ole'

                                (Look out when a glimmer from the darkness of the back of my mind comes to light)

                                If I remember right, then vbnet.mvps.org might be what you need to port to PB
                                Engineer's Motto: If it aint broke take it apart and fix it

                                "If at 1st you don't succeed... call it version 1.0"

                                "Half of Programming is coding"....."The other 90% is DEBUGGING"

                                "Document my code????" .... "WHYYY??? do you think they call it CODE? "

                                Comment


                                • #17
                                  I will have a look at your code later if I find the time.

                                  I am sorry for mistyping "getscrollinfo." I did use that function and could not figure out how to tell when
                                  no scrollbar exists. The function always returns 1 with or without a scrollbar, and I could not find any
                                  member of the si structure that could help. Do you know how to tell if a scrollbar exists from what
                                  is returned by GetScrollInfo?
                                  Why are you using GetScrollInfo? This function is not what you want to use to determine the visible state
                                  of scrollbars.
                                  Either Check the window style as shown previously, or use the GetScrollBarInfo function.
                                  For example,
                                  Code:
                                    LOCAL tsbi        AS SCROLLBARINFO
                                    LOCAL hWndList    AS DWORD
                                    LOCAL fVert       AS LONG
                                    LOCAL fHorz       AS LONG
                                    
                                    hWndList = GetDlgItem(hWndParent, %IDC_FORM1_LISTVIEW1)
                                  
                                    tsbi.cbSize = SIZEOF(tsbi)
                                    GetScrollBarInfo hWndList, %OBJID_HSCROLL, tsbi
                                    fHorz = ((tsbi.rgstate(0) AND %STATE_SYSTEM_INVISIBLE) <> %STATE_SYSTEM_INVISIBLE)
                                    GetScrollBarInfo hWndList, %OBJID_VSCROLL, tsbi
                                    fVert = ((tsbi.rgstate(0) AND %STATE_SYSTEM_INVISIBLE) <> %STATE_SYSTEM_INVISIBLE)
                                    
                                    MSGBOX "horz,vert:" + FORMAT$(fHorz) + "," + FORMAT$(fVert)
                                  Dominic Mitchell
                                  Phoenix Visual Designer
                                  http://www.phnxthunder.com

                                  Comment


                                  • #18
                                    when should the listview control have a scroll bar and when should it not. Our users would not want a scrollbar there which did nothing. I would still have to find a way to show/hide it when needed.
                                    You just hide the separate scrollbar control if the total width of the data columns (add 'em up!) is less than the client width of the employee listview control.

                                    You know this when you insert the columns. If they resize the listview control just check again on WM_SIZE. (not that I can envision a user-resizeable child control for this application). If they resize the entire window and you adjust that control's width to fit, just check the column widths again.
                                    Michael Mattias
                                    Tal Systems Inc. (retired)
                                    Racine WI USA
                                    [email protected]
                                    http://www.talsystems.com

                                    Comment


                                    • #19
                                      Thank you all. I am truly grateful! I have multiple options now. Thank you thank you! I will report back with which solution works best for our app.
                                      Bill Scharf

                                      Comment


                                      • #20
                                        One other thing I forgot... if your user resizes the columns of the employee listview by dragging the divider in the header control, you'll have to detect that to see if the total column widths are now greater/less than the control width so you can hide or show the separate scrollbar control.

                                        Here's some code to get you started.

                                        I think you will want to detect "HDN_ENDTRACK" only instead of HDN_TRACK, since if the user is dragging the divider he can't be using the scrollbar at the same time so showing/hiding the scrollbar can be done when he finishes dragging.


                                        Code:
                                        #IF NOT %DEF(%LVUNION)
                                         UNION LvUnion
                                            NMHDR  AS NMHDR
                                            NMLV   AS NMLISTVIEW
                                            NMIA   AS NMITEMACTIVATE
                                            LVDI   AS LV_DISPINFO
                                            LVCD   AS NMLVCUSTOMDRAW
                                            NMLVK  AS NMLVKEYDOWN
                                            NMTVDI AS TV_DISPINFO
                                            NMTV   AS NM_TREEVIEW
                                            ' NOTE NEXT IS NOT THE SAME AS NMHDR.
                                            HEADER AS NMHEADER
                                         END UNION
                                         %LVUNION = 1
                                        #ENDIF
                                        
                                        FUNCTION DlgProc .....
                                        
                                        
                                        LOCAL pLVU AS LVUNION PTR 
                                        
                                           SELECT CASE AS LONG uMSG 
                                        
                                        
                                             CASE %WM_NOTIFY
                                                 pLVU =  lparam 
                                        
                                                 IF @plvu.nmhdr.idFrom = %ID_HEADER THEN
                                                      SELECT CASE AS LONG @plvu.nmhdr.code
                                                          CASE %HDN_TRACK   ' pNMHEader
                                                              STDOUT "HDN_TRACK NOTFICATION FROM HEADER iTiem =" & FORMAT$(@pLVU.header.iItem)
                                                              ' item is always the item on the left of the divider
                                                              ' check out cxy, I should see it shirink and grow
                                                              IF (@[email protected] AND %HDI_WIDTH) THEN
                                                                  STDOUT "Cxy=" & FORMAT$(@[email protected])
                                                                  ' return the new width of column iItem here.
                                                              END IF
                                                              ' set the listview column to match the header...
                                                              'IF @plvu.HEADER.iItem = 0  THEN
                                                                  Listview_SetColumnWidth GetDlgITem(hWnd, %ID_LV), @plvu.HEADER.iItem, @[email protected]
                                                             ' END IF
                                                              ' WORKS PERFECTLY! for column zero
                                                              ' also seems to be working for all columns.
                                        
                                                     ......
                                        MCM
                                        Michael Mattias
                                        Tal Systems Inc. (retired)
                                        Racine WI USA
                                        [email protected]
                                        http://www.talsystems.com

                                        Comment

                                        Working...
                                        X