Jump to content

PMGuide - List-Box Controls

From EDM2
Revision as of 05:24, 28 April 2025 by Martini (talk | contribs) (Created page with "{{IBM-Reprint}} {{PMGuide}} A ''list box'' is a control window that displays several text items at a time, one or more of which can be selected by the user. This chapter explains how to create and use list-box controls in PM applications. == About List Boxes == An application uses a list box when it requires a list of selectable fields that is too large for the display area or a list of choices that can change dynamically. Each list item contains a text string and a ha...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation

Presentation Manager Programming Guide and Reference
  1. How to Use this Book
  2. Device Functions
  3. Direct Manipulation Functions
  4. Dynamic Data Formatting Functions
  5. Hooks and Procedures
  6. Profile Functions
  7. Spooler Functions
  8. Window Functions
  9. Message Processing
  10. Data Types
  11. Errors
  12. Atom Tables
  13. Button Controls
  14. Clipboards
  15. Combination Box
  16. Container Controls
  17. Control Windows
  18. Cursors
  19. Dialog Windows
  20. Direct Manipulation
  21. Drawing in Windows
  22. Dynamic Data Exchange
  23. Entry-Field Controls
  24. File Dialog Controls
  25. Font Dialog Controls
  26. Frame Windows
  27. Hooks
  28. Initialization Files
  29. Keyboard Accelerators
  30. List-Box Controls
  31. Menus
  32. Messages and Message Queues
  33. Multiple-Line Entry Field Controls
  34. Mouse and Keyboard Input
  35. Mouse Pointers and Icons
  36. Notebook Controls
  37. Painting and Drawing
  38. Presentation Parameters
  39. Resource Files
  40. Scroll-Bar Controls
  41. Slider Controls
  42. Spin Button Controls
  43. Static Controls
  44. Title-Bar Controls
  45. Value Set Controls
  46. Windows
  47. Window Classes
  48. Window Procedures
  49. Window Timers
  50. Appendices
  51. Notices
  52. Glossary

A list box is a control window that displays several text items at a time, one or more of which can be selected by the user. This chapter explains how to create and use list-box controls in PM applications.

About List Boxes

An application uses a list box when it requires a list of selectable fields that is too large for the display area or a list of choices that can change dynamically. Each list item contains a text string and a handle. Usually, the text string is displayed in the list-box window; but the handle is available to the application to reference other data associated with each of the items in the list.

A list box always is owned by another window that receives messages from the list box when events occur, such as when a user selects an item from the list box. Typically, the owner is a dialog window (as shown in the following figure,) or the client window of an application frame window. The client- or dialog-window procedure defined by the application responds to messages sent from the list box.

A list box always contains a scroll bar for use when the list box contains more items than can be displayed in the list-box window. The list box responds to mouse clicks in the scroll bar by scrolling the list; otherwise, the scroll bar is disabled.

The maximum number of items permitted in a list box is 32767.

Using List Boxes

An application uses a list-box control to display a list in a window. List boxes can be displayed in standard application windows, although they are more commonly used in dialog windows. In either case, notification messages are sent from the list box to its owner window, enabling the application to respond to user actions in the list.

Once a list box is created, the application controls the insertion and deletion of list items. Items can be inserted at the end of the list, automatically sorted into the list, or inserted at a specified index position. Applications can turn list drawing on and off to speed up the process of inserting numerous items into a list.

The owner-window procedure of the list box receives messages when a user manipulates the list-box data. Most default list actions (for example, highlighting selections and scrolling) are handled automatically by the list box itself. The application controls the responses when the user chooses an item in the list, either by double-clicking the item or by pressing Enter after an item is highlighted. The list box also notifies the application when the user changes the selection or scrolls the list.

Normally, list items are text strings drawn by a list box. An application also can draw and highlight the items in a list. This enables the application to create customized lists that contain graphics. When an application creates a list box with the LS_OWNERDRAW style, the owner of the list box receives a WM_DRAWITEM message for each item that should be drawn or highlighted. This is similar to the owner-drawn style for menus, except that the owner-drawn style applies to the entire list rather than to individual items.

Creating a List-Box Window

List boxes are WC_LISTBOX class windows and are predefined by the system. Applications can create list boxes by calling WinCreateWindow, using WC_LISTBOX as the window-class parameter.

A list box passes notification messages to its owner window, so an application uses its client window, rather than the frame window, as the owner of the list. The client-window procedure receives the messages sent from the list box. For example, to create a list box that completely fills the client area of a frame window, an application would make the client window the owner and parent of the list-box window, and make the list-box window the same size as the client window. This is shown in the following code fragment.

    #define ID_LISTWINDOW   250

    HWND hwndClient,hwndList;
    RECTL rcl;

                                            /* How big is the
                                               client window? */
    WinQueryWindowRect(hwndClient, &rcl);

                                            /* Make a list-box
                                               window.          */
    hwndList = WinCreateWindow(hwndClient,    /* Parent          */
        WC_LISTBOX,                         /* Class           */
        "",                                 /* Name            */
        WS_VISIBLE | LS_NOADJUSTPOS,        /* Style           */
        0, 0,                               /* x, y            */
        rcl.xRight, rcl.yTop,                /* cx, cy          */
        hwndClient,                         /* Owner           */
        HWND_TOP,                           /* Behind          */
        ID_LISTWINDOW,                      /* ID              */
        NULL,                               /* Control data    */
        NULL);                              /* parameters      */

Because the list box draws its own border, and a frame-window border already surrounds the client area of a frame window due to the adjacent frame controls, the effect is a double-thick border around the list box. You can change this effect by calling WinInflateRect to overlap the list-box border with the surrounding frame-window border, resulting in only one list-box border.

Notice that the code specifies the list-box window style LS_NOADJUSTPOS. This ensures that the list box is created exactly the specified size. If the LS_NOADJUSTPOS style is not specified, the list-box height is rounded down, if necessary, to make it a multiple of the item height. Enabling a list box to adjust its height automatically is useful for preventing partial items being displayed at the bottom of a list box.

Using a List Box in a Dialog Window

List boxes most commonly are used in dialog windows. A list box in a dialog box is a control window, like a push button or an entry field. Typically, the application defines a list box as one item in a dialog template in the resource-definition file, as shown in the following resource compiler source-code fragment.

    DLGTEMPLATE IDD_OPEN
    BEGIN
        DIALOG "Open...", IDD_OPEN, 35, 35, 150, 135,
               FS_DLGBORDER, FCF_TITLEBAR
            BEGIN
                LISTBOX         IDD_FILELIST, 15, 15, 90, 90
                PUSHBUTTON      "Drive", IDD_DRIVEBUTTON, 115, 70, 30, 14
                DEFPUSHBUTTON   "Open", IDD_OPENBUTTON, 115, 40, 30, 14
                PUSHBUTTON      "Cancel", IDD_CANCELBUTTON, 115, 15, 30, 14
            END
    END

Once the dialog resource is defined, the application loads and displays the dialog box as it would normally. The application inserts items into the list when processing the WM_INITDLG message.

A dialog window with a list box usually has an OK button. The user can select items in the list, and then indicate a final selection by double-clicking, pressing Enter, or clicking the OK button. When the dialog-window procedure receives a message indicating that the user has clicked the OK button, it queries the list box to determine the current selection (or selections, if the list allows multiple selections), and then responds as though it had received a WM_CONTROL message with the LN_ENTER notification code.

Adding or Deleting an Item in a List Box

Applications can add items to a list box by sending an LM_INSERTITEM or LM_INSERTMULTITEMS message to the list-box window; items are deleted using the LM_DELETEITEM message. Items in a list are specified with a 0-based index (beginning at the top of the list). A new list is created empty; the application initializes the list by inserting items. LM_INSERTMULTITEMS allows up to 32767 items to be inserted as a group, while LM_INSERTITEM adds items one-by-one to a list.

The application specifies the text and position for each new item. It can specify an absolute-position index or one of the following predefined index values:

Value Meaning
LIT_END Insert item at end of list.
LIT_SORTASCENDING Insert item alphabetically ascending into list.
LIT_SORTDESCENDING Insert item alphabetically descending into list.

If a large number of items are to be inserted into a list box at one time, use of LM_INSERTMULTITEMS is more efficient than use of LM_INSERTITEM. The same positioning flags are used. When LIT_SORTASCENDING or LIT_SORTDESCENDING is specified with LM_INSERTMULTITEMS, new items are inserted before the updated list is sorted. If items are being added using several LM_INSERTMULTITEMS messages, LIT_END should be specified for all messages except the last; this will avoid unnecessary multiple sorts of the list.

If no text array is specified, empty items are inserted into the list. This is very useful for list boxes created with LS_OWNERDRAW style, which do not use text strings.

The application must send an LM_DELETEITEM message and supply the absolute-position index of the item when deleting items from a list. The LM_DELETEALL message deletes all items in a list.

One way an application can speed up the insertion of list items is to suspend drawing until it has finished inserting items. This is a particularly valuable approach when using a sorted insertion process (when inserting one item can cause rearrangement of the entire list). You can turn off list drawing by calling WinEnableWindowUpdate, specifying FALSE for the enable parameter, and then calling WinShowWindow. This forces a total update when insertion is complete. The following code fragment illustrates this concept:

    HWND hwndFileList;

    /* Disable updates while filling the list. */
    WinEnableWindowUpdate(hwndFileList, FALSE);
        ...
        ... /* Send LM_INSERTITEM messages to insert all new items. */
        ...

    /* Now cause the window to update and show the new information.  */
    WinShowWindow(hwndFileList, TRUE);

Notice that this optimization is unnecessary if an application is adding list items while processing a WM_INITDLG message, because the list box is not visible, and the list-box routines are internally optimized.

Responding to a User Selection in a List Box

When a user chooses an item in a list, the primary notification an application receives is a WM_CONTROL message, with the LN_ENTER control code sent to the owner window of the list. Within the window procedure for the owner window, the application responds to the LN_ENTER control code by querying the list box for the current selection (or selections, in the case of an LS_MULTIPLESEL or LS_EXTENDEDSEL list box).

The LN_ENTER control code notifies the application that the user has selected a list item. A WM_CONTROL message with an LN_SELECT control code is sent to the list-box owner whenever a selection in a list changes, such as when a user moves the mouse pointer up and down a list while pressing the mouse button. In this case, items are selected but not yet chosen. An application can ignore LN_SELECT control codes when the selection changes, responding only when the item is actually chosen. Or an application can use LN_SELECT to display context-dependent information that changes rapidly with each selection made by the user.

Handling Multiple Selections

When a list box has the style LS_MULTIPLESEL or LS_EXTENDEDSEL, the user can select more than one item at a time. An application must use different strategies when working with these types of lists. For example, when responding to an LN_ENTER control code, it is not sufficient to send a single LM_QUERYSELECTION message, because that message will find only the first selection. To find all current selections, an application must continue sending LM_QUERYSELECTION messages, using the return index of the previous message as the starting index of the next message, until no items are returned.

Creating an Owner-Drawn List Item

To draw its own list items, an application must create a list that has the style LS_OWNERDRAW: the owner window of the list box must respond to the WM_MEASUREITEM and WM_DRAWITEM messages.

When the owner window receives a WM_MEASUREITEM message, it must return the height of the list item. All items in a list must have the same height (greater than or equal to 1). The WM_MEASUREITEM message is sent when the list box is created, and every time an item is added. You can change the item height by sending an LM_SETITEMHEIGHT message to the list-box window. The maximum width of a list box created with the LM_HORZSCROLL style can be set using an LM_SETITEMWIDTH message.

The owner window receives a WM_DRAWITEM message whenever an item in an owner-drawn list should be drawn or highlighted. Although it is quite common for an owner-drawn list to draw items, it is less common to override the system-default method of highlighting. (This method inverts the rectangle that contains the item.) Do not create your own highlighting unless, for some reason, the system-default method is unacceptable to you.

The WM_DRAWITEM message contains a pointer to an OWNERITEM data structure. The OWNERITEM structure contains the window identifier for the list box, a presentation-space handle, a bounding rectangle for the item, the position index for the item, and the application-defined item handle. This structure also contains two fields that determine whether a message draws, highlights, or removes the highlighting from an item. The OWNERITEM structure has the following form:

    typedef struct _OWNERITEM { /* oi */
        HWND    hwnd;
        HPS     hps;
        ULONG   fsState;
        ULONG   fsAttribute;
        ULONG   fsStateOld;
        ULONG   fsAttributeOld;
        RECTL   rclItem;
        LONG    idItem;
        ULONG   hItem;
    } OWNERITEM;

When the item must be drawn, the owner window receives a WM_DRAWITEM message with the fsState field set differently from the fsStateOld field. If the owner window draws the item in response to this message, it returns TRUE, telling the system not to draw the item. If the owner window returns FALSE, the system draws the item, using the default list-item drawing method.

You can get the text of a list item by sending an LM_QUERYITEMTEXT message to the list-box window. You should draw the item using the hps and rclItem arguments provided in the OWNERITEM structure.

If the item being drawn is currently selected, the fsState and fsStateOld fields are both TRUE; they both will be FALSE if the item is not currently selected. The window receiving a WM_DRAWITEM message can use this information to highlight the selected item at the same time it draws the item. If the owner window highlights the item, it must leave the fsState and fsStateOld fields equal to each other. If the system provides default highlighting for the item (by inverting the item rectangle), the owner window must set the fsState field to 1 and the fsStateOld field to 0 before returning from the WM_DRAWITEM message.

The owner window also receives a WM_DRAWITEM message when the highlight state of a list item changes. For example, when a user clicks an item, the highlighting must be removed from the currently selected item, and the new selection must be highlighted. If these items are owner-drawn, the owner window receives one WM_DRAWITEM message for each unhighlighted item and one message for the newly highlighted item. To highlight an item, the fsState field must equal TRUE, and the fsStateOld field must equal FALSE. In this case, the application should highlight the item and return the fsState and fsStateOld fields equal to FALSE, which tells the system not to highlight the item. The application also can return the fsState and fsStateOld fields with two different (unequal) values and the list box will highlight the item (the default action).

To remove highlighting from an item, the fsState field must equal FALSE and the fsStateOld field must equal TRUE. In this case, the application removes the highlighting and returns both the fsState and the fsStateOld fields equal to FALSE. This tells the system not to attempt to remove the highlighting. The application also can return the fsState and fsStateOld fields with two different (unequal) values, and the list box will remove the highlighting (the default response). The following code fragment shows these selection processes:

    OWNERITEM *poi;

    case WM_DRAWITEM:

        /* Convert mp2 into an OWNERITEM structure pointer.                   */
        poi = (POWNERITEM) PVOIDFROMMP(mp2);

        /* Test to see if this is drawing or highlighting/unhighlighting.  */
        if (poi->fsState != poi->fsStateOld) {

            /* This is either highlighting or unhighlighting.              */
            if (poi->fsState) {
                ...
                ... /* Highlight the item.                                  */
                ...
            }
            else {
                ...
                ... /* Remove the highlighting.                             */
                ...
            }

            /* Set fsState = fsStateOld to tell system you did it.          */
            poi->fsState = poi->fsStateOld = 0;

            return TRUE;  /* Tells list box you did the highlighting.         */

        }
        else {
            ...
            ... /* Draw the item.                                           */
            ...
            if (poi->fsState) {  /* Checks to see if item is selected    */
                ...
                ... /* Highlight the item.                                  */
                ...
                /* Set fsState = fsStateOld to tell system you did it.    */
            }
            return TRUE;  /* Tells list box you did the drawing.            */
        }

Default List-Box Behavior

The following table lists all the messages handled by the predefined list-box window-class procedure.

Message Description
LM_DELETEALL Deletes all items in the list.
LM_DELETEITEM Removes the specified item from the list, redrawing the list as necessary. Returns the number of items remaining in the list.
LM_INSERTITEM Inserts a new item into the list according to the position information passed with the message.
LM_INSERTMULTITEMS Inserts one or more items into a list box at one time.
LM_QUERYITEMCOUNT Returns the number of items in the list.
LM_QUERYITEMHANDLE Returns the specified item handle.
LM_QUERYITEMTEXT Copies the text of the specified item to a buffer supplied by the message sender.
LM_QUERYITEMTEXTLENGTH Returns the text length of the specified item.
LM_QUERYSELECTION For a single-selection list box, returns the zero-based index of the currently selected item. For a multiple-selection list box, returns the next selected item or LIT_NONE if no more items are selected.
LM_QUERYTOPINDEX Returns the zero-based index to the item currently visible at the top of the list.
LM_SEARCHSTRING Searches the list for a match to the specified string.
LM_SELECTITEM Selects the specified item. If the list is a single-selection list, deselects the previous selection. Sends a WM_CONTROL message (with the LN_SELECT code) to the owner window.
LM_SETITEMHANDLE Sets the specified item handle.
LM_SETITEMHEIGHT Sets the item height for the list. All items in the list have the same height.
LM_SETITEMTEXT Sets the text for the specified item.
LM_SETITEMWIDTH Sets the maximum width of a list box created with the LS_HORZSCROLL style.
LM_SETTOPINDEX Shows the specified item as the top item in the list window, scrolling the list as necessary.
WM_ADJUSTWINDOWPOS If the list box has the style LS_NOADJUSTPOS, makes no changes to the SWP structure and returns FALSE. Otherwise, adjusts the height of the list box so that a partial item is not shown at the bottom of the list. Returns TRUE if the SWP structure is changed.
WM_BUTTON2DOWN Returns TRUE; the message is ignored.
WM_BUTTON3DOWN Returns TRUE; the message is ignored.
WM_CHAR Processes virtual keys for line and page scrolling. Sends an LN_ENTER notification code for the Enter key. Returns TRUE if the key is processed; otherwise, passes the message to the WinDefWindowProc function.
WM_CREATE Creates an empty list box with a scroll bar.
WM_DESTROY Destroys the list and deallocates any memory allocated during its existence.
WM_ENABLE Enables the scroll bar if there are more items than can be displayed in a list-box window.
WM_MOUSEMOVE Sets the mouse pointer to the arrow shape and returns TRUE to show that the message was processed.
WM_PAINT Draws the list box and its items.
WM_HSCROLL Handles scrolling indicated by the list-box horizontal scroll bar.
WM_VSCROLL Handles scrolling indicated by the list-box vertical scroll bar.
WM_SETFOCUS If the list box is gaining the focus, creates a cursor and sends an LN_SETFOCUS notification code to the owner window. If the list box is losing the focus, this message destroys the cursor and sends an LN_KILLFOCUS notification code to the owner window.
WM_TIMER Uses timers to control automatic scrolling that occurs when a user drags the mouse pointer outside the window.