ID: Q112640
Using the Microsoft Windows 3.1 SDK, when page down is used in an owner- draw variable list box, the selection is changed to the last visible item in the list box (as it should). However, at this point no subsequent page downs will work.
To address the page-down problem, the owner-draw variable list box is subclassed. In the subclass procedure, WM_KEYDOWN is processed. When wParam is VK_NEXT, the PAGE DOWN key was pressed, and the actual paging down is implemented. The ODVARLB sample shows how to work around this problem.
The following file is available for download from the Microsoft Software Library:
~ Odvarlb.exe (size: 31920 bytes)
For more information about downloading files from the Microsoft Software
Library, please see the following article in the Microsoft Knowledge Base:
ARTICLE-ID: Q119591
TITLE : How to Obtain Microsoft Support Files from
Online Services
NOTE: This is not a problem with Win32-based applications.
The steps required for implementing the page down are as follows:
1. Find out how many items exist in the list box. LB_GETCOUNT message can
be used to get this information:
// Get maximum number of items in the list box.
iMaxItems = SendMessage(hWnd,LB_GETCOUNT,0,0L);
2. Find out which item now has the selection; this item will be moved to
the top of the list box on a page down. This can be done by sending an
LB_GETCURSEL to the list box:
// Get the index for the item which is to be at the top.
iTopIdx = SendMessage(hWnd,LB_GETCURSEL,0,0L);
3. If the item that has the focus now is the last item in the list then we
cannot page down further, so just return.
// If the focus is already on the last item, we have reached
// the end of the list so just return.
if (iTopIdx != (iMaxItems -1))
// Continue we have more work to do.
4. Otherwise, get the height of the list box by calling GetClientRect():
// Get the client area rectangle of the list box.
GetClientRect(hWnd, &rc);
5. Then find out how many items will fit in the list box; because this is
an owner-draw variable list box, some more elaborate calculation is
necessary. To do this cleanly, a WM_MEASUREITEM message is sent to the
parent of the list box and WM_MEASUREITEM code calculates the height for
each of the items one by one. Then the height of each item is
accumulated and is compared with the list box's height until the last
item in the current page is located.
// Get the index for the last visible item in the list
// box based on the new top item.
iBottomIdx = GetLBIndex(hWnd,iTopIdx,iMaxItems,rc.bottom);
The GetLBIndex() function finds the last item that should be visible
after the page down is completed. Below is the code for GetLBIndex:
int GetLBIndex(HWND hLB,int iTopIdx,UINT iMaxLBItems, int lbHeight)
{
MEASUREITEMSTRUCT mis;
int ItmHeights = 0; // Keeps track of total item heights.
// Set up the measure item structures fields.
mis.CtlType = ODT_LISTBOX;
mis.itemID = iTopIdx;
while (mis.itemID <= (iMaxLBItems - 1))
{
// Get the items measurement, height in particular.
SendMessage(GetParent(hLB),WM_MEASUREITEM,
0, (LPARAM)(LPMEASUREITEMSTRUCT)&mis);
// Add it to our total height.
ItmHeights += mis.itemHeight;
// If the total item height is still less than
// the list box:
if (ItmHeights <= lbHeight)
// Unless we have reached the end of out list.
if (mis.itemID == (iMaxLBItems - 1))
// In this case, return the index to the last item in the list.
return (mis.itemID);
else
// Check the next item, we want to cycle through again.
++mis.itemID ;
else
// We have found the last item to be displayed in the list box
// so return it.
return (--mis.itemID);
}
return (iTopIdx);
}
6. When the height of the list box is less than or equal to the height of
an item, the list box displays only one item or part of an item. In this
situation some special-case handling is required. When the user presses
the PAGE DOWN key, the next item in the list becomes visible. Below is
the code used to adjust the top and bottom index for this special case:
// Special case: When the height of the list box is less than or
// equal to the height of an item, the list box displays only one
// item or part of an item at a time. So, when paging down we need
// to display the next item in the list.
if (iBottomIdx <= iTopIdx)
iBottomIdx = ++iTopIdx;
7. Now the item in step 2 (or 6, iTopIdx) is placed at the top of the list
and the selection (highlighted) is given to the last item, which is the
item obtained in step 5 (or 6, iBottomIdx). The scroll bar thumb
position is automatically updated.
// Move the item that had the focus to the top, then set the
// selection to the last visible item in the list box.
SendMessage(hWnd,LB_SETCURSEL,(WPARAM) iBottomIdx, 0L);
SendMessage(hWnd,LB_SETTOPINDEX,(WPARAM) iTopIdx, 0L);
NOTE: Just doing this will cause flashing so the painting is turned off
in the list box until the page down process is complete. Then the entire
list box is invalidated and the list box window is updated to reflect
the changes.
// Turn off all painting to prevent flashing.
SendMessage(hWnd, WM_SETREDRAW, (WPARAM) FALSE,0L);
// Move the item that had the focus to the top, then set the
// focus to the last visible item in the list box.
SendMessage(hWnd,LB_SETCURSEL,(WPARAM) iBottomIdx, 0L);
SendMessage(hWnd,LB_SETTOPINDEX,(WPARAM) iTopIdx, 0L);
// We are done, so turn on painting. Invalidate the list box window
// for painting and then update the list window.
SendMessage(hWnd, WM_SETREDRAW, (WPARAM) TRUE,0L);
InvalidateRect(hWnd,NULL,TRUE);
UpdateWindow(hWnd);
However, adding the WM_SETREDRAW messages to eliminate flashing prevents
the scroll bar from being updated. To address this problem, the scroll
bar position is retrieved via GetScrollPos() and then reset and painted
via SetScrollPos().
pos = GetScrollPos(hWnd,SB_VERT);
SetScrollPos(hWnd,SB_VERT,pos,TRUE);
The code for this workaround is the ODLB.C file. All the necessary code
for this workaround is actually contained in two functions,
SbListSubProc() - The subclass procedure for the list box.
GetLBIndex() - Finds the last visible item in the list box
after paging down.
To see the demonstration, select the list box menu item and then page down!
The font on the list box can also be changed using the Change Font menu option. Changing the font demonstrates how the code works with a different number of visible items in the list box.
Additional query words: owner draw listbox Keywords : kbfile kbsample kb16bitonly kbCtrl kbListBox kbSDKPlatform kbGrpUser kbWinOS310
Last Reviewed: January 3, 1999