Scrollable+list+boxes

//by Richard Russell, June 2010//

There are three main ways in which a **scroll bar** may be used with a list box:


 * 1) If the number of items is greater than will fit in the height of the list box, a vertical scroll bar is automatically added.  This is the default behaviour.
 * 2) If the number of items is greater than will fit in the height of the list box, and both the **WS_HSCROLL** and **LBS_MULTICOLUMN** style bits are set, a horizontal scroll bar is added.  The items can then be scrolled through horizontally.
 * 3) If the longest item will not fit across the width of the list box, it is possible to arrange for a horizontal scroll bar to be added.  However, this requires extra code (see below).

Items 1 and 2 are covered in the main [|Help documentation]. Although the use of the **WS_HSCROLL** and **LBS_MULTICOLUMN** style bits is described only in the section covering list boxes in a dialogue box, it is equally applicable to a [|listbox created on the main output window].

Item 3 is considerably more complicated to achieve, partly for the reasons outlined in this [|Microsoft article]. To make it easier, the code listed below provides two procedures: **PROC_addstring** and **PROC_delstring**; if these are called instead of sending the LB_ADDSTRING and LB_DELETESTRING messages to the list box, the horizontal scroll bar will be controlled automatically.

In order to enable a horizontal scroll bar, it is necessary to set the **WS_HSCROLL** style bit (&100000). When creating the list box in a dialogue box this value should be included in the final parameter of **PROC_listbox**, for example: code format="bb4w" WS_HSCROLL = &100000 PROC_listbox(dlg%, "", id%, x%, y%, cx%, cy%, WS_HSCROLL) code Similarly, when creating the list box on the output window include the value in the final parameter of **FN_listbox**, as follows: code format="bb4w" WS_HSCROLL = &100000 hlb% = FN_listbox("", x%, y%, cx%, cy%, id%, WS_HSCROLL) code The **WS_HSCROLL** style may be combined with any others required, but the code listed here is //__not__// compatible with the **LBS_USETABSTOPS** or **LBS_MULTICOLUMN** styles.

Once created (and, in the case of a dialogue box, displayed) the listbox may be populated with string items. Instead of sending the usual LB_ADDSTRING message, call the **PROC_addstring** routine, which enables correct behaviour of the horizontal scroll bar. In the case of a dialogue box it is first necessary to discover the handle of the list box as follows: code format="bb4w" SYS "GetDlgItem", !dlg%, id% TO hlb% code Where **dlg%** is the value returned from **FN_newdialog** and **id%** is the ID number of the list box. In the case of a list box on the main output window, the handle is returned from **FN_listbox** as shown above.

In either case, to populate the listbox use code similar to the following: code format="bb4w" PROC_addstring(hlb%, text1$) PROC_addstring(hlb%, text2$) REM etc. code You can, of course, add the items within a loop if more convenient.

To delete an item from a list box, call the **PROC_delstring** routine instead of sending the LB_DELETESTRING message: code format="bb4w" PROC_delstring(hlb%, index%) code where **index%** is the index number (starting at zero) of the item you wish to delete. If that item was longer than the others, the horizontal scroll bar will be adjusted (or removed) accordingly. Note that, by default, the items in a list box created using **PROC_listbox** are sorted into alphabetical order; therefore the index numbers of the items typically won't correspond to the order in which they were added. If necessary use the **LB_FINDSTRING** or **LB_FINDSTRINGEXACT** message to discover a string's index.

To empty the listbox of all its contents, call the **PROC_resetlb** routine below.

Finally, here are the routines which control the horizontal scroll bar: code format="bb4w" DEF PROC_addstring(hlb%, text$) LOCAL E%, I%, max%, nitems% LB_ADDSTRING = &180 LB_GETCOUNT = &18B LB_GETITEMDATA = &199 LB_SETHORIZONTALEXTENT = &194 LB_SETITEMDATA = &19A SYS "SendMessage", hlb%, LB_GETCOUNT, 0, 0 TO nitems% IF nitems% THEN FOR I% = 0 TO nitems%-1 SYS "SendMessage", hlb%, LB_GETITEMDATA, I%, 0 TO E%         IF E%>max% THEN max% = E%        NEXT ENDIF E% = FN_hextent(hlb%, text$+" ") IF E%>max% SYS "SendMessage", hlb%, LB_SETHORIZONTALEXTENT, E%, 0 SYS "SendMessage", hlb%, LB_ADDSTRING, 0, text$ TO I%     SYS "SendMessage", hlb%, LB_SETITEMDATA, I%, E%      ENDPROC DEF PROC_delstring(hlb%, index%) LOCAL E%, I%, max%, nitems% LB_DELETESTRING = &182 LB_GETCOUNT = &18B LB_GETITEMDATA = &199 LB_SETHORIZONTALEXTENT = &194 SB_TOP = 6 WM_HSCROLL = &114 SYS "SendMessage", hlb%, LB_GETCOUNT, 0, 0 TO nitems% IF nitems% THEN FOR I% = 0 TO nitems%-1 SYS "SendMessage", hlb%, LB_GETITEMDATA, I%, 0 TO E%         IF E%>max% IF I%<>index% THEN max% = E%        NEXT ENDIF SYS "SendMessage", hlb%, WM_HSCROLL, SB_TOP, 0 SYS "SendMessage", hlb%, LB_SETHORIZONTALEXTENT, max%, 0 SYS "SendMessage", hlb%, LB_DELETESTRING, index%, 0 ENDPROC

DEF PROC_resetlb(hlb%) LB_DELETESTRING = &182 LB_RESETCONTENT = &184 LB_SETHORIZONTALEXTENT = &194 SB_TOP = 6 WM_HSCROLL = &114 SYS "SendMessage", hlb%, WM_HSCROLL, SB_TOP, 0 SYS "SendMessage", hlb%, LB_SETHORIZONTALEXTENT, 0, 0 SYS "SendMessage", hlb%, LB_DELETESTRING, 0, 0 SYS "SendMessage", hlb%, LB_RESETCONTENT, 0, 0 ENDPROC

DEF FN_hextent(hwnd%, text$) LOCAL hdc%, hfnew%, hfold%, size{} DIM size{x%,y%} WM_GETFONT = &31 SYS "GetDC", hwnd% TO hdc% SYS "SendMessage", hwnd%, WM_GETFONT, 0, 0 TO hfnew% SYS "SelectObject", hdc%, hfnew% TO hfold% SYS "GetTextExtentPoint32", hdc%, text$, LEN(text$), size{} SYS "SelectObject", hdc%, hfold% SYS "ReleaseDC", hwnd%, hdc% = size.x% code Note that, for convenience, this code uses the 'user data' associated with each list box item to contain the length of the item in pixels. Therefore the 'user data' facility is not available for other purposes in your program.