MORE INFORMATION
The following files are available for download from the Microsoft
Download Center:
For additional information about how to download Microsoft Support files, click the following article number to view the article in the Microsoft Knowledge Base:
119591 How to Obtain Microsoft Support Files from Online Services
Microsoft scanned this file for viruses. Microsoft used the most current virus-detection software that was available on the date that the file was posted. The file is stored on security-enhanced servers that help to prevent any unauthorized changes to the file.
ListHorz.exe contains a complete sample application demonstrating a
list box with a horizontal scroll bar. The necessary support functions are
contained in a C module that can be compiled and linked to any Windows-based application.
ListHscr.exe contains the complete sources for a DLL that contains the
necessary list box support functions. These functions are exactly the same
as those in ListHorz.exe. Included in this archive is an application that
uses the services of the DLL to perform the same functions as the
application in ListHorz.exe.
The following is an outline of the information presented in this article:
A. Windows support for a horizontal scroll bar on a list box
- Overview
Software Library sample code
B. New list box messages: LB_SETHORIZONTALEXTENT and LB_GETHORIZONTALEXTENT
- Default setting for extents is zero.
- Messages only change internal value -- do not affect the
visibility of the scroll bar.
C. Making the scroll bar visible when adding or inserting a string.
- Must change extent before adding a string.
- Must redraw the list box after adding a string.
D. Hiding the scroll bar when deleting a string
- Must change the extent before deleting a string.
- Must redraw the list box after deleting a string.
- Send WM_HSCROLL message with SB_TOP parameter to scroll the list
box to the left in case it is scrolled right.
- Special Handling is needed for LB_RESETCONTENT.
E. Calculating horizontal pixel extents of strings:
- Use GetTextExtent function.
- Font considerations: use WM_GETFONT message.
- Use GetTextMetrics and add one tmAveCharWidth.
F. Maintenance of all extents in list box.
G. Sample code
- Support functions as an additional C file -- static linking to application.
- Support functions in a DLL, multiple list boxes supported.
Windows Support for Horizontal Scroll Bar in a List Box
Microsoft Windows version 3.0 is the first version that recognizes the
WS_HSCROLL window style for list boxes. This style adds a horizontal scroll
bar to the list box. However, the scroll bar does not appear automatically
when a string that is too long to display in a list box is added to that
list box. Similarly, when the last string longer than the list box is
removed, Windows will not hide the scroll bar. This behavior is different
from that of the vertical scroll bar of a list box, which is added and
removed as needed.
An application must maintain the width, in pixels, of each string in the
list box. The LB_SETHORIZONTALEXTENT list box message controls both the
scrolling range and the visibility of a horizontal scroll bar. In this
article, the term "extent" is defined to be the width of an object in
screen pixels. Each string has an extent as does the list box itself.
The other sections of this article provide more detailed information about
special considerations that must occur when dealing with horizontal scroll
bars in list boxes.
New List Box Messages:
LB_SETHORIZONTALEXTENT and LB_GETHORIZONTALEXTENT
Two messages have been added to Windows 3.0 to support horizontal
scroll bars in list boxes:
Message Description
-------------------------------------------------------------------
LB_SETHORIZONTALEXTENT Sets the width in pixels by which a list
box can be scrolled to the value of wParam
in the message.
LB_GETHORIZONTALEXTENT Retrieves the width in pixels by which a
list box can be scrolled.
By default, the horizontal scroll bar extent of a list box is 0 (zero).
Because 0 is less than the width of the list box client area, Windows will
not add a scroll bar to the window until the scroll bar extent is changed
to a value larger than the list box extent.
However, the LB_SETHORIZONTALEXTENT message does not affect the visibility
of a horizontal scroll bar. If a scroll bar is visible, sending this
message with a small extent specified will not remove it. Likewise, if a
scroll bar is not present, sending this message with a large extent will
not create one.
The next two sections of this article explain how to add and remove a
horizontal scroll bar as strings are added and deleted. The major point is
that Windows will only show or hide the scroll bar when a string is added,
inserted, or deleted.
Making the Scroll Bar Visible When Adding or Inserting a String
When a string with an extent larger than the width of the list box is to be
added, an application must send the LB_SETHORIZONTALEXTENT message before
sending an LB_ADDSTRING or LB_INSERTSTRING message.
During the process of adding or inserting a string, Windows compares the
horizontal scroll bar extent stored in the list box to the width of the
list box client area. If the client area extent is smaller than the scroll
bar extent, the scroll bar is made visible the next time the list box is
drawn.
The client area width of the list box does not include the width of the
vertical scroll bar, if it is visible. Consider a list box without a
vertical scroll bar, that is filled with strings. Each of these strings is
slightly narrower than the list box. When another string is added, and the
vertical scroll bar is caused to appear, Windows discovers that the
horizontal scroll bar extent is now greater than the width of the list box,
and adds a horizontal scroll bar.
If the scroll bar extent is less than the width of the client area of the
list box, the status of the scroll bar remains unchanged.
If the list box is not drawn after the string is added, the scroll bar will
not appear. Therefore, if the WM_SETREDRAW message is used to turn redraw
off, adding a string will not show the horizontal scroll bar until the list
box is redrawn.
Hiding the Scroll Bar When Deleting a String
Windows only removes the horizontal scroll bar during the processing of an
LB_DELETESTRING message. Therefore, if the string to be deleted is the
longest in the list box, the horizontal scroll bar extent must be changed
to the next smaller extent value before that string is deleted. After the
string is deleted, Windows compares the stored scroll bar extent to the
width of the client area of the list box, and if the extent is smaller, the
scroll bar is removed.
If the list box is not drawn after the string is deleted, the scroll bar
will not disappear. Therefore, if the WM_SETREDRAW message is used to turn
redraw off, deleting a string will not remove the horizontal scroll bar
until the list box is redrawn.
However, if the list box is scrolled to the right by as little as one
pixel, the scroll bar will remain visible, regardless of the extent that is
set. This is done so that the user can always scroll back to the extreme
left. If the scroll bar was removed, the list box might be left in a state
where it is scrolled to the right by some amount without any way to scroll
back completely to the left.
To work around this problem, always scroll the list box to the extreme left
before deleting the longest string. If a shorter string is deleted, the
extent will remain the same and the horizontal scroll bar will not be
affected anyway. Only scroll the list box if the longest string is being
deleted.
The list box can be scrolled either completely to the left (in the case
where a long string still exists in the list box) or just enough so that
the next longest string is visible, assuming that the list box requires a
scroll bar. The sample code in the Software Library (described above)
always scrolls the list box completely left, using the WM_HSCROLL message,
as follows:
SendMessage(hList, WM_HSCROLL, SB_TOP, 0L);
The LB_RESETCONTENT message does not affect the state of the horizontal
scroll bar even though all strings in the list box are deleted. Before an
LB_RESETCONTENT message is sent, an application must perform the following
steps:
- Send a LB_SETHORIZONTALEXTENT message with an extent of 0 (zero).
- Send a WM_HSCROLL message to scroll the list box completely to the
left. This method is given above.
- Send an LB_DELETESTRING message with an index of 0 (zero). This
will delete the first string, if there is one, and will also remove
the scroll bar.
If any strings are present in the list box, the first is removed by the
LB_DELETESTRING message and the scroll bar is removed, because the extent
has been set to zero. The LB_RESETCONTENT message will then remove the
remaining strings. If there are no strings in the list box, the
LB_DELETESTRING message will return an error. However, because there are no
strings in the list box, there should be no scroll bar in the first place.
Calculating Horizontal Extents of Strings
The previous discussion mentions the extents of strings, but provides no
methods for determining these values.
Pixel extents of strings are calculated by the GetTextExtent function. This
is a GDI call that sums the pixel width of each character in a string using
the font that is currently selected into a display context represented by
an hDC.
If lpString holds a representative string and hWndListBox is the window
handle to the list box, the following steps are required to determine the
size of each string:
- Declare the following variables:
DWORD dwExtent;
HDC hDCListBox;
HFONT hFontOld, hFontNew;
TEXTMETRICS tm;
- Use GetDC to retrieve the handle to the display context for the
list box and store it in hDCListBox:
hDCListBox = GetDC(hWndListBox);
- Send the list box a WM_GETFONT message to retrieve the handle to
the font that the list box is using, and store this handle in
hFontNew:
hFontNew = SendMessage(hWndListBox, WM_GETFONT, NULL, NULL);
- Use SelectObject to select the font into the display context.
Retain the return value from the SelectObject call in hFontOld:
hFontOld = SelectObject(hDCListBox, hFontNew);
- Call GetTextMetrics to get additional information about the font
being used:
GetTextMetrics(hDC, (LPTEXTMETRIC)&tm);
- For each string, the value of the extent to be used is calculated
as follows:
dwExtent = GetTextExtent(hDCListBox, lpString, strlen(lpString))
+ tm.tmAveCharWidth;
- After all the extents have been calculated, select the old font
back into hDCListBox and then release it:
SelectObject(hDCListBox, hFontOld);
ReleaseDC(hWndListBox, hDCListBox);
There is a reason for step 5 and adding the additional width to the string
in step 6 above. If the largest value returned by GetTextExtent is used as
the parameter in the LB_SETHORIZONTALEXTENT message, the longest string
will not show completely when the list box is scrolled completely to the
right. A few pixels are clipped by the right border on the list box.
The tmAveCharWidth field in the TEXTMETRIC structure provides a consistent
number of pixels to add to the length of the string, no matter what font is
presently in use. This buffer space keeps the strings from being clipped.
As a side note, the value of tmAveCharWidth is the number of pixels by
which the list box is scrolled horizontally when the scroll bar arrows are
clicked. If a fixed pitch font is used, the list box is scrolled
horizontally by one character for each click.
Maintenance of All Extents in List Box
Many possible methods can be used to maintain a list of the extents of the
strings in the list box. One of the most convenient methods is to use
property lists, as shown in the sample code.
Every window has a property list associated with it. Each property is a
string and an associated data handle. A window stores and retrieves each
data handle using the string that labels the handle.
A sorted list of the extents for strings in the list box can be saved in a
local or global memory object. This allows each window to keep its own list
and does not require that the application maintain a mapping from the list
box to the data handle itself. The list of extents should be sorted in
descending order, so that the first extent in the list is that of the
longest string in the list box. Keeping this list sorted also allows the
application to use a binary search to find an extent in the list when one
is being inserted or deleted.
When a new string is added, insert that string's extent into the list,
maintaining the sorted order. If the new extent is placed at the top of the
list, send an LB_SETHORIZONTALEXTENT message to the list box specifying the
new extent. Do not send the message for an extent that is not the largest.
When a string is deleted, remove that string's extent from the list. If the
extent is the first in the list, then that string is the longest in the
list box. In this case, send an LB_SETHORIZONTALEXTENT to the list box
specifying the next largest extent in the list. If a smaller string is
deleted, do not send the message.
When an LB_RESETCONTENT message is used, clear the entire list of extents.
Send an LB_SETHORIZONTALEXTENT message specifying an extent of 0 (zero),
followed by an LB_DELETESTRING as outlined in the last part of the section
above titled "Hiding the Scroll Bar When Deleting a String."
Sample Code
As mentioned above, there are two archives in the Microsoft Software
Library: LISTHORZ and LISTHSCR. Each of these samples provides five support
functions that greatly facilitate the maintenance of horizontal scroll bars
in list boxes.
In LISTHORZ, these functions are found in the file LISTHELP.C. This file
can be compiled separately and linked into an application.
LISTHSCR contains the source files for a DLL that provides these five
support functions. The C file for the DLL is exactly the same as in
LISTHORZ, except that its name has changed. This archive also includes a
LISTHORZ program that uses the services of the DLL to perform the same
functions as the program in the other archive. The file LISTHAPI.H is the
include file containing prototypes of the functions exported by the DLL.
Also included is an import library, LISTHSCR.LIB, that is generated by the
makefile.
The DLL is most useful because it can support horizontal scroll bars in any
number of list boxes in any number of applications.
The remainder of this article documents the support functions:
BOOL FInitListboxExtents(HWND hList)
Allocates local memory to store a list of string extents for the
list box identified by hList. The handle to this local memory is
saved in the property list of the list box. This function should be
called after the list box is created, such as during WM_INITDIALOG
processing.
Parameters:
hList HWND Handle to the list box that will use a horizontal scroll bar.
Return Value:
BOOL TRUE if there are no errors, FALSE if memory could
not be allocated.
BOOL FFreeListboxExtents(HWND hList)
Frees the memory allocated for the extent list of the list box
identified by hList. The property that stores the memory handle set
by FInitListboxExtents is removed. This function should be called
when the list box is being destroyed.
Parameters:
hList HWND Handle to the list box that was previously used
with FInitListboxExtents
Return Value:
BOOL TRUE if there are no errors, FALSE if memory could
not be freed, in which case the property is not
removed.
void ResetListboxExtents(HWND hList)
Removes all previously saved extents in the extent list. This is
accomplished by calling FFreeListboxExtents and FInitListboxExtents
in succession. The horizontal extent of the list box is set to 0
(zero) and any horizontal scroll bar is removed. This function
should be called before an LB_RESETCONTENT message is sent to the
list box.
Parameters:
hList HWND Handle to the list box that will be reset.
Return Value:
none
WORD WAddExtentEntry(HWND hList, LPSTR psz)
Adds an extent entry into the list box's extent list. The extent
added is that of the string pointed to by psz using the current font
in the list box. If the extent added is larger than any other in the
extent list, an LB_SETHORIZONTALEXTENT message is sent to the list
box with this new extent.
This function must be called before the string is added to the list
box with LB_ADDSTRING or LB_INSERTSTRING.
Parameters:
hList HWND Handle to the list box to which the string is to
be added.
psz LPSTR Pointer to string that is to be added. This
function must be called before the string is added
so that the horizontal scroll bar will be properly
maintained.
Return Value:
WORD One of these three values:
0 if the string added was not the longest string
in the list box and the visibility of the
horizontal scroll bar did not change.
The extent of the string added, if the added
string was the longest and the visibility of
the scroll bar may have changed.
-1 for an error.
WORD WRemoveExtentEntry(HWND hList, WORD iSel)
Removes the extent entry of the string identified by index iSel in
the list box. If the string to be removed is the longest in the list
box, the list box is scrolled completely to the left and the
horizontal extent is set to that of the next longest string.
This function must be called before an LB_DELETESTRING message is
sent to the list box.
Parameters:
hList HWND Handle to the list box from which a string is to
be removed.
iSel WORD Index of the string to be removed.
Return Value:
WORD One of these three values:
0 if the string removed was not the longest in the
list box and the visibility of the horizontal
scroll bar did not change
The extent of the string deleted, if the deleted
string was the longest and the visibility of
the scroll bar may have changed.
-1 for an error.