List box items can be represented by text strings, bitmaps, or both. If the list box is not large enough to display all the list box items at once, the list box provides a scroll bar. The user scrolls through the list box items, and applies or removes selection status as necessary. Selecting a list box item changes its visual appearance, usually by changing the text and background colors to those specified by the relevant operating system metrics. When the user selects or deselects an item, the system sends a notification message to the parent window of the list box.
A dialog box procedure is responsible for initializing and monitoring its child windows, including any list boxes. The dialog box procedure communicates with the list box by sending messages to it and by processing the notification messages sent by the list box.
Windows NT/2000/XP: For an ANSI application, the system converts the text in a list box to Unicode using CP_ACP. This can cause problems. For example, accented Roman characters in a non-Unicode list box in Microsoft® Windows®, Japanese version will come out garbled. To fix this, either compile the application as Unicode or use an owner-drawn list box.
For more information about dialog boxes, see
This section discusses the following topics.
There are two types of list boxes: single-selection (the default) and multiple-selection. In a single-selection list box, the user can select only one item at a time. In a multiple-selection list box, the user can select more than one item at a time. To create a multiple-selection list box, specify the LBS_MULTIPLESEL or the LBS_EXTENDEDSEL style.
There are many list box styles and window styles that control the appearance and operation of a list box. These styles indicate whether list box items are sorted, arranged in multiple columns, drawn by the application, and so on. The dimensions and styles of a list box are typically defined in a dialog box template included in an application's resources.
For a table of list box styles, see
The
Also, the
When an event occurs in a list box, the list box sends a notification message
to the dialog box procedure of the owner window. List box notification messages
are sent when a user selects, double-clicks, or cancels a list box item; when
the list box receives or loses the keyboard focus; and when the system cannot
allocate enough memory for a list box request. A notification message is sent as
a
A dialog box procedure is not required to process these messages; the default window procedure processes them.
An application should monitor and process the following list box notification messages.
| Notification message | Description |
|---|---|
| The user double-clicks an item in the list box. | |
| The list box cannot allocate enough memory to fulfill a request. | |
| The list box loses the keyboard focus. | |
| The user cancels the selection of an item in the list box. | |
| The selection in a list box is about to change. | |
| The list box receives the keyboard focus. |
A dialog box procedure can send messages to a list box to add, delete,
examine, and change list box items. For example, a dialog box procedure could
send an
A list box item is often referenced by its index, an integer that represents the item's position in the list box. The index of the first item in a list box is 0, the index of the second item is 1, and so on.
The following table describes how the predefined list box procedure responds to list box messages.
| Message | Response |
|---|---|
| Inserts a file into a directory list box filled by the DlgDirList function and retrieves the list box index of the inserted item. | |
| Adds a string to a list box and returns its index. | |
| Removes a string from a list box and returns the number of strings remaining in the list. | |
| Adds a list of filenames to a list box and returns the index of the last filename added. | |
| Returns the index of the first string in the list box that begins with a specified string.. | |
| Returns the index of the string in the list box that is equal to a specified string. | |
| Returns the index of the item that the mouse last selected. | |
| Returns the index of the item that has the focus rectangle. | |
| Returns the number of items in the list box. | |
| Returns the index of the currently selected item. | |
| Returns the scrollable width, in pixels, of a list box. | |
| Returns the value associated with the specified item. | |
| Returns the height, in pixels, of an item in a list box. | |
| Retrieves the client coordinates of the specified list box item. | |
| Retrieves the locale of the list box. The high-order word contains the country/region code and the low-order word contains the language identifier. | |
| Returns the selection state of a list box item. | |
| Returns the number of selected items in a multiple-selection list box. | |
| Creates an array of the indexes of all selected items in a multiple-selection list box and returns the total number of selected items. | |
| Retrieves the string associated with a specified item and the length of the string. | |
| Returns the length, in characters, of the string associated with a specified item. | |
| Returns the index of the first visible item in a list box. | |
| Allocates memory for the specified number of items and their associated strings. | |
| Inserts a string at a specified index in a list box. | |
| Retrieves the zero-based index of the item nearest the specified point in a list box. | |
| Removes all items from a list box. | |
| Selects the first string it finds that matches a specified prefix. | |
| Selects a specified range of items in a list box. | |
| Selects a specified range of items if the index of the first item in the range is less than the index of the last item in the range. Cancels the selection in the range if the index of the first item is greater than the last. | |
| Sets the item that the mouse last selected to a specified item. | |
| Sets the focus rectangle to a specified list box item. | |
| Sets the width, in pixels, of all columns in a list box. | |
| Sets the number of items in a list box. | |
| Selects a specified list box item. | |
| Sets the scrollable width, in pixels, of a list box. | |
| Associates a value with a list box item. | |
| Sets the height, in pixels, of an item or items in a list box. | |
| Sets the locale of a list box and returns the previous locale identifier. | |
| Selects an item in a multiple-selection list box. | |
| Sets the tab stops to those specified in a specified array. | |
| Scrolls the list box so the specified item is at the top of the visible range. |
The window procedure for the predefined list box window class carries out default processing for all messages that the list box does not process. When the list box procedure returns FALSE for a message, the predefined window procedure checks the message and performs default actions, as shown in the following table.
| Message | Default action |
|---|---|
| Moves the selection to the first item that begins with the character
the user typed. If the list box has the LBS_OWNERDRAW style, no action
occurs.
Multiple characters typed within a short interval are treated as a group, and the first item that begins with that series of characters is selected.
|
|
| Creates an empty list box. | |
| Destroys the list box and frees any resources it uses. | |
| Passes the message to the dialog box procedure or parent window process. | |
| If the control is visible, invalidates the rectangle so the strings can be painted gray. | |
| Erases the background of a list box. If the list box has the LBS_OWNERDRAW style, the background is not erased. | |
| Returns DLGC_WANTARROWS | DLGC_WANTCHARS, indicating the default list box procedure processes the arrow keys and WM_CHAR messages. | |
| Returns a handle to the current font for the list box. | |
| Scrolls the list box horizontally. | |
| Processes virtual keys for scrolling. The virtual key is the index of the item to move the caret to. The selection is not changed. | |
| Turns the caret off and destroys it. Sends an LBN_KILLFOCUS notification message to the owner of the list box. | |
| Tracks the mouse in the list box client area. This enables the user to cancel a selection if the mouse button is released outside the list box client area. | |
| Tracks the mouse in the list box client area. This enables the user to cancel a selection if the mouse button is released outside the list box client area. | |
| Tracks the mouse in the list box client area. This enables the user to cancel a selection if the mouse button is released outside the list box client area. | |
| Tracks the mouse in the list box client area. This enables the user to cancel a selection if the mouse button is released outside the list box client area. | |
| Performs a subclassed paint operation by using the list box handle to the device context (DC). | |
| Turns the caret on and sends an LBN_SETFOCUS notification message to the owner of the list box. | |
| Sets a new font for the list box. | |
| Sets or clears the redraw flag based on the value of wParam. | |
| Resizes the list box to an integral number of items. | |
| Scrolls the list box vertically. |
The predefined list box procedure passes all other messages to
An application can create an owner-drawn list box to take
responsibility for painting list items. The parent window or dialog box of an
owner-drawn list box (its owner) receives
The owner of an owner-drawn list box must process the WM_DRAWITEM message. This message is sent whenever a portion of the list box must be redrawn. The owner may need to process other messages, depending on the styles specified for the list box.
An application can create an owner-drawn list box by specifying the LBS_OWNERDRAWFIXED or LBS_OWNERDRAWVARIABLE style. If all list items in the list box are the same height, such as strings or icons, an application can use the LBS_OWNERDRAWFIXED style. If list items are of varying height, bitmaps of different size, for example, an application can use the LBS_OWNERDRAWVARIABLE style.
The owner of an owner-drawn list box can process a
If the information displayed in an owner-drawn list box includes text, an
application can keep track of the text for each list item by specifying the
LBS_HASSTRINGS style. List boxes with the LBS_SORT style are sorted based on
this text. If a list box is sorted, but is not of the LBS_HASSTRINGS style, the
owner must process the
In an owner-drawn list box, the owner must keep track of list items
containing information other than or in addition to text. One convenient way to
do this is to save the handle to the information as item data using the LB_SETITEMDATA
message. To free data objects associated with items in a list box, the owner can
process the
For an example of an owner-drawn list box, see
The following example demonstrates how a dialog box procedure creates a simple list box and fills it with the names of people on a softball team. When a name in the list is selected, additional information about the player is displayed in the dialog box. The following illustration shows the dialog box.

The list box has the LBS_STANDARD style, a combination of LBS_SORT,
LBS_NOTIFY, WS_VSCROLL, and WS_BORDER. The code initializes the dialog box
while processing the
#define BUFFER MAX_PATH
#define NAMELENGTH 15
#define POSITIONLENGTH 20
#define TEAMSIZE 15
typedef struct
{
TCHAR tchName[NAMELENGTH];
TCHAR tchPosition[POSITIONLENGTH];
int nGamesPlayed;
int nInningsPlayed;
double xBattingAverage;
TCHAR tchFoodName[NAMELENGTH];
} Player;
Player Roster[] =
{
{"Alan", "Center field", 17, 56, .375, "Cannelloni"},
{"Colin", "Pitcher", 26, 96, .456, "Lefse"},
{"Cindy", "Second base", 13, 58, .207, "Tequila"},
{"Dave", "First base", 28, 138, .508, "Suds"},
{"David", "Center field", 18, 101, .612, "Bok Choy"}
{"Jack", "Pitcher", 27, 110, .542, "Animal Crackers"},
{"Julie", "Right field", 22, 101, .509, "Mashed Potatoes"},
{"Karen", "Second base", 26, 140, .238, "Pez"},
{"Kathie", "Left field", 26, 127, .353, "Baba Ganouj"},
{"Matt", "Shortstop", 24, 112, .579, "Oats"},
{"Miriam", "Right field", 24, 112, .393, "Zotz"},
{"Pete", "Shortstop", 26, 90, .608, "Beet"},
{"Seth", "Center field", 20, 76, .407, "Otter Pop"},
{"Suzanna", "Catcher", 16, 53, .286, "Toast"},
{"Wendy", "Third base", 25, 154, .493, "Ham"},
};
BOOL APIENTRY DlgTeamProc(
HWND hDlg, // window handle to dialog box
UINT message, // type of message
UINT wParam, // message-specific information
LONG lParam) // message-specific information
{
TCHAR tchBuffer[BUFFER];
int nItem;
int i;
HWND hwndList;
switch (message)
{
case WM_INITDIALOG:
{
hwndList = GetDlgItem(hDlg, IDL_SOFTBALL);
// Initialize the list box (fill it with player names).
for (i = 0; i < TEAMSIZE; i++)
{
SendMessage(hwndList, LB_ADDSTRING, 0,
(LPARAM) Roster[i].tchName);
SendMessage(hwndList, LB_SETITEMDATA, i, (LPARAM) i);
}
SetFocus(hwndList);
return FALSE;
}
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDL_SOFTBALL:
switch (HIWORD(wParam))
{
case LBN_SELCHANGE:
// Show the selected player's statistics.
hwndList = GetDlgItem(hDlg, IDL_SOFTBALL);
nItem = SendMessage(hwndList, LB_GETCURSEL,
0, 0);
i = SendMessage(hwndList, LB_GETITEMDATA,
nItem, 0);
SetDlgItemText(hDlg, IDS_POS,
Roster[i].tchPosition);
SetDlgItemText(hDlg, IDS_GAME,
_itoa(Roster[i].nGamesPlayed,
tchBuffer, 10));
SetDlgItemText(hDlg, IDS_INN,
_itoa(Roster[i].nInningsPlayed,
tchBuffer, 10));
SetDlgItemText(hDlg, IDS_BA,
_gcvt(Roster[i].xBattingAverage,
3, tchBuffer));
SetDlgItemText(hDlg, IDS_FOOD,
Roster[i].tchFoodName);
return TRUE;
}
break;
case IDOK:
case IDCANCEL:
// Destroy the dialog box.
EndDialog(hDlg, TRUE);
return TRUE;
default:
return FALSE;
}
default:
return FALSE;
}
}
The following example demonstrates how to display the content of the current directory in a list box and enable the user to delete one file at a time.
In addition to the standard list box styles, this list box has the
LBS_MULTICOLUMN and LBS_HSCROLL styles. The code initializes the list box by
using the
#define BUFFER MAX_PATH
BOOL APIENTRY DlgDelFileProc(
HWND hDlg, // window handle to dialog box
UINT message, // type of message
UINT wParam, // message-specific information
LONG lParam)
{
DWORD cchCurDir;
LPTSTR lpszCurDir;
LPTSTR lpszFileToDelete;
int nItem;
int nTotal;
TCHAR tchBuffer[BUFFER+1];
BOOL fResult;
switch (message)
{
case WM_INITDIALOG:
// Initialize the list box by filling it with files from
// the current directory.
lpszCurDir = tchBuffer;
GetCurrentDirectory(cchCurDir, lpszCurDir);
lpszCurDir[BUFFER+1] = '\0';
DlgDirList(hDlg, lpszCurDir, IDL_FILES, IDS_PATHTOFILL, 0);
SetFocus(GetDlgItem(hDlg, IDL_FILES));
return FALSE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
// When the user presses the DEL (IDOK) button,
// delete the selected file.
lpszFileToDelete = tchBuffer;
DlgDirSelectEx(hDlg, lpszFileToDelete, MAX_PATH,
IDL_FILES);
fResult = DeleteFile(lpszFileToDelete);
if (!fResult)
{
MessageBox(hDlg, "Could not delete file.",
NULL, MB_OK);
}
else
{
nItem = SendMessage(GetDlgItem(hDlg,
IDL_FILES), LB_GETCURSEL, 0, 0);
nTotal = SendMessage(GetDlgItem(hDlg,
IDL_FILES), LB_DELETESTRING, nItem, 0);
// Update the list box.
if (nTotal > nItem)
{
SendMessage(GetDlgItem(hDlg, IDL_FILES),
LB_SETCURSEL, nItem, 0);
}
else
{
SendMessage(GetDlgItem(hDlg, IDL_FILES),
LB_SETCURSEL, nTotal, 0);
}
}
return TRUE;
case IDCANCEL:
// Destroy the dialog box.
EndDialog(hDlg, TRUE);
return TRUE;
default:
return FALSE;
}
default:
return FALSE;
}
}
The following example displays and initializes the dialog box used in the
preceding example. However, this code uses the LBS_MULTIPLESEL style to enable
the user to select more than one file at a time. When the user chooses the Delete
button, the example sends the
#define BUFFER MAX_PATH
#define BIGBUFF 8192
BOOL APIENTRY DlgDelFilesProc(
HWND hDlg, // window handle to dialog box
UINT message, // type of message
UINT wParam, // message-specific information
LONG lParam)
{
DWORD cchCurDir;
LPTSTR lpszCurDir;
LPTSTR lpszFileToDelete;
int nSelItems;
int nSelItemsInBuffer;
TCHAR tchBuffer[BUFFER+1];
TCHAR tchMsgBuff[BUFFER+1];
int nBuffer[BIGBUFF];
int i;
BOOL fResult;
HWND hListBox;
HRESULT hr;
switch (message) {
case WM_INITDIALOG:
// Initialize the list box by filling it with files from
// the current directory.
lpszCurDir = tchBuffer;
GetCurrentDirectory(cchCurDir, lpszCurDir);
lpszCurDir[BUFFER+1] = '\0';
DlgDirList(hDlg, lpszCurDir, IDL_FILES, IDS_PATHTOFILL, 0);
SetFocus(GetDlgItem(hDlg, IDL_FILES));
return FALSE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
// When the user presses the Delete (IDOK)
// button, delete all the selected files.
lpszFileToDelete = tchBuffer;
hListBox = GetDlgItem(hDlg, IDL_FILES);
nSelItems = SendMessage(hListBox,
LB_GETSELCOUNT, 0, 0);
nSelItemsInBuffer = SendMessage(hListBox,
LB_GETSELITEMS, 512, (LPARAM) nBuffer);
if (nSelItems > nSelItemsInBuffer)
{
MessageBox(hDlg, "Too many items selected.",
NULL, MB_OK);
}
else
{
// Go through the list backwards because after
// deleting an item the indices change for
// every subsequent item. By going backward,
// the indices are never invalidated.
for (i = nSelItemsInBuffer - 1; i >= 0; i--)
{
SendMessage(hListBox, LB_GETTEXT,
nBuffer[i],
(LPARAM) lpszFileToDelete);
fResult = DeleteFile(lpszFileToDelete);
if (!fResult)
{
hr = StringCchPrintf(tchMsgBuff,
BUFFER,
"Could not delete file: %s "
"GetLastError = %u",
(LPARAM) lpszFileToDelete);
if (FAILED(hr))
{
//
// TODO: write error handler
//
}
// Call app-defined error handler.
ErrorHandler(tchMsgBuff);
}
else
{
SendMessage(hListBox, LB_DELETESTRING,
nBuffer[i], 0);
}
}
SendMessage(hListBox, LB_SETCARETINDEX, 0, 0);
}
return TRUE;
case IDCANCEL:
// Destroy the dialog box.
EndDialog(hDlg, TRUE);
return TRUE;
default:
return FALSE;
}
default:
return FALSE;
}
}
The following example shows how to draw a list box that contains five owner-drawn items: four drawing implements and a fork. Each list item appears as a bitmap followed by the name of the object. A button prompts the user to select one item that is not like the others. Choosing the button with the fork selected displays a "You're right!" message and closes the dialog box. Choosing the button with any other list item selected displays a "Try again!" message.
The list box has the LBS_OWNERDRAW and LBS_HASSTRINGS styles, in addition
to the standard list box styles. The code initializes the list box by sending
the LB_ADDSTRING message to set the text, and then sends the
#define XBITMAP 80
#define YBITMAP 20
#define BUFFER MAX_PATH
HBITMAP hbmpPencil, hbmpCrayon, hbmpMarker, hbmpPen, hbmpFork;
HBITMAP hbmpPicture, hbmpOld;
void AddItem(HWND hwnd, LPSTR lpstr, HBITMAP hbmp)
{
int nItem;
nItem = SendMessage(hwndList, LB_ADDSTRING, 0, lpstr);
SendMessage(hwndList, LB_SETITEMDATA, nItem, hbmp);
}
DWORD APIENTRY DlgDrawProc(
HWND hDlg, // window handle to dialog box
UINT message, // type of message
UINT wParam, // message-specific information
LONG lParam)
{
int nItem;
TCHAR tchBuffer[BUFFER];
HBITMAP hbmp;
HWND hListBox;
TEXTMETRIC tm;
int y;
HDC hdcMem;
LPMEASUREITEMSTRUCT lpmis;
LPDRAWITEMSTRUCT lpdis;
RECT rcBitmap;
HRESULT hr;
size_t * pcch;
switch (message)
{
case WM_INITDIALOG:
// Load bitmaps.
hbmpPencil = LoadBitmap(hinst, MAKEINTRESOURCE(700));
hbmpCrayon = LoadBitmap(hinst, MAKEINTRESOURCE(701));
hbmpMarker = LoadBitmap(hinst, MAKEINTRESOURCE(702));
hbmpPen = LoadBitmap(hinst, MAKEINTRESOURCE(703));
hbmpFork = LoadBitmap(hinst, MAKEINTRESOURCE(704));
// Retrieve list box handle.
hListBox = GetDlgItem(hDlg, IDL_STUFF);
// Initialize the list box text and associate a bitmap
// with each list box item.
AddItem(hListBox, "pencil", hbmpPencil);
AddItem(hListBox, "crayon", hbmpCrayon);
AddItem(hListBox, "marker", hbmpMarker);
AddItem(hListBox, "pen", hbmpPen);
AddItem(hListBox, "fork", hbmpFork);
SetFocus(hListBox);
SendMessage(hListBox, LB_SETCURSEL, 0, 0);
return TRUE;
case WM_MEASUREITEM:
lpmis = (LPMEASUREITEMSTRUCT) lParam;
// Set the height of the list box items.
lpmis->itemHeight = 20;
return TRUE;
case WM_DRAWITEM:
lpdis = (LPDRAWITEMSTRUCT) lParam;
// If there are no list box items, skip this message.
if (lpdis->itemID == -1)
{
break;
}
// Draw the bitmap and text for the list box item. Draw a
// rectangle around the bitmap if it is selected.
switch (lpdis->itemAction)
{
case ODA_SELECT:
case ODA_DRAWENTIRE:
// Display the bitmap associated with the item.
hbmpPicture =(HBITMAP)SendMessage(lpdis->hwndItem,
LB_GETITEMDATA, lpdis->itemID, (LPARAM) 0);
hdcMem = CreateCompatibleDC(lpdis->hDC);
hbmpOld = SelectObject(hdcMem, hbmpPicture);
BitBlt(lpdis->hDC,
lpdis->rcItem.left, lpdis->rcItem.top,
lpdis->rcItem.right - lpdis->rcItem.left,
lpdis->rcItem.bottom - lpdis->rcItem.top,
hdcMem, 0, 0, SRCCOPY);
// Display the text associated with the item.
SendMessage(lpdis->hwndItem, LB_GETTEXT,
lpdis->itemID, (LPARAM) tchBuffer);
GetTextMetrics(lpdis->hDC, &tm);
y = (lpdis->rcItem.bottom + lpdis->rcItem.top -
tm.tmHeight) / 2;
hr = StringCchLength(tchBuffer, BUFFER, pcch);
if (FAILED(hr))
{
// TODO: write error handler
}
TextOut(lpdis->hDC,
XBITMAP + 6,
y,
tchBuffer,
pcch);
SelectObject(hdcMem, hbmpOld);
DeleteDC(hdcMem);
// Is the item selected?
if (lpdis->itemState & ODS_SELECTED)
{
// Set RECT coordinates to surround only the
// bitmap.
rcBitmap.left = lpdis->rcItem.left;
rcBitmap.top = lpdis->rcItem.top;
rcBitmap.right = lpdis->rcItem.left + XBITMAP;
rcBitmap.bottom = lpdis->rcItem.top + YBITMAP;
// Draw a rectangle around bitmap to indicate
// the selection.
DrawFocusRect(lpdis->hDC, &rcBitmap);
}
break;
case ODA_FOCUS:
// Do not process focus changes. The focus caret
// (outline rectangle) indicates the selection.
// The IDOK button indicates the final
// selection.
break;
}
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
// Get the selected item's text.
nItem = SendMessage(GetDlgItem(hDlg, IDL_STUFF),
LB_GETCURSEL, 0, (LPARAM) 0);
hbmp = SendMessage(GetDlgItem(hDlg, IDL_STUFF),
LB_GETITEMDATA, nItem, 0);
// If the item is not the correct answer, tell the
// user to try again.
//
// If the item is the correct answer, congratulate
// the user and destroy the dialog box.
if (hbmp != hbmpFork)
{
MessageBox(hDlg, "Try again!", "Oops", MB_OK);
return FALSE;
}
else
{
MessageBox(hDlg, "You're right!",
"Congratulations.", MB_OK);
// Fall through.
}
case IDCANCEL:
// Destroy the dialog box.
EndDialog(hDlg, TRUE);
return TRUE;
default:
return FALSE;
}
case WM_DESTROY:
// Free any resources used by the bitmaps.
DeleteObject(hbmpPencil);
DeleteObject(hbmpCrayon);
DeleteObject(hbmpMarker);
DeleteObject(hbmpPen);
DeleteObject(hbmpFork);
return TRUE;
default:
return FALSE;
}
return FALSE;
}