List Boxes


The List Box Control

The list box control is defined by the window class "listbox". List boxes display a set of strings in columns within a rectangle. Strings may be added or removed by sending messages to the list box. A list box notifies its owner of a selection via the command message. Upon receving such a notification, the owner of a list box may determine which item has been selected.

A list box can be a single select or a mulitple select list box; depending upon the list box style. A multiple select list box allows multiple items to be selected simultaneously. The selected item or items are highlighted in reverse video. When a list box has the input focus, it displays a dotted line around an item in the list box. By definition, this is the item that has the cursor positioned over it. In a single select list box, the item upon which the cursor is positioned may be selected using the space bar. The arrow keys move both the cursor and the current selection and can scroll the list box. The page up and page down keys may also be used to change the position of the cursor. Pressing a letter moves the cursor and the selection to the first or next item that begins with the letter. An item may also be selected by single or double clicking the item with the mouse.

Multiple Selection List Boxes

In a multiple selection list box, the space bar toggles the selected state of the item that currently possesses the cursor. The arrow keys deselect all previously selected items and move the cursor and selection to the newly selected item. However, the ctrl key plus an arrow key moves the cursor without modifying the existing selection.

The shift keys combined with the arrow keys may be used to extend the selection in a multiselect list box. Clicking or double clicking on an item removes the selection from all other items and selects the specified item. However, clicking an item whilst holding down the shift key toggles the selection of the particular item without changing the selection state of other items.

List Box Styles

Apart from standard window styles, the styles applicable specifically to list boxes may be found in the enumeration listbox_style. The style listbox_style::notify should be included if the owner of the list box is to receive notifications of selection (otherwise, the owner is required to interrogate the list box to ind the item that was selected). When listbox_style::notify is not specified, no command messages are received by the owner.

By default, a list box displays its items without drawing a surrounding border. A border may be included in a list box by adding the window style style::border. The standard window style style::vertical_scroll may be included to ensure that the list box has a vertical scroll bar. The composite style listbox_style::standard includes notifications, sorting, a vertical scroll bar and a border, as shown below.

listbox_style::standard = listbox_style::notify | listbox_style::sort | style::vertical_scroll | style::border

The standard styles style::size_box and style::caption may also be applied to a list box; in which case, the list box may be adjusted in size and moved within the parent.

Filling a List Box

A list box may be filled by sending messages to the list box. Text strings within a list box may be referenced by a 0-based index. The index 0 corresponds to the top most string within the list box. When adding a string via a message, the list box returns one of the values found in the enumeration listbox_return.

If the style listbox_style::sort is applied to the list box, or if the strings are to be added in a specific order, a string may be added as shown below.

send_message(listbox,listbox_message::add_string,0,(parameter)string);

If the style listbox_style::sort is not applied, strings may be inserted into a list box by specifying an index value as shown below.

send_message(listbox,listbox_message::insert_string,index,(parameter)string);

If the specified index is -1, the string is added to the bottom of the list box. When adding a string with a specific non-negative index, any strings below that index have their indexes adjusted accordingly.

A string may be deleted from a list box as shown below.

send_message(listbox,listbox_message::delete_string,index);

Normally, a list box updates itself for each string that is inserted. If a number of strings are to be inserted, the programmer may wish to prevent repetitive updates by using the message message::set_redraw. The following example disables updates to a list box.

send_message(listbox,message::set_redraw,false);

When all the items have been added, the call:

send_message(listbox,message::set_redraw,true);

may be made to enable updates. The above statements manipulate the list box style listbox_style::no_redraw.

Obtaining Information About Selection

When working with the selection of items, some messages are used for single selection list boxes and some are used for multiple selection list boxes. Single selection list boxes will be discussed first.

Selections in a Single Select List Box

Usually, it is the operator that determines the selection within a list box using the mouse or keyboard. A program may select an item in a list box as shown below.

send_message(listbox,listbox_message::set_selection,index);

Setting the index to -1 removes the selection from any item. Strings may also be used to select an item with the following call.

send_message(listbox,listbox_message::select_string,index,search);

The parameter index is the index following which the search commences. The search is for an item whose initial characters match the given string. If an index of -1 is specified, the search commences at the top of the list box. The index of the matching item is returned and listbox_return::error is returned if no item matches the string.

When a command notification is received by the owner of a list box, the owner may use the following call to query the index of the currently selected item.

int selected = send_message(listbox,listbox_message::get_selection);

A program may query the length of text associated with an item of a given index using the following call.

int length = send_message(listbox,listbox_message::get_text_length,index);

The actual text may be queried as follows.

int length = send_message(listbox,listbox_message::get_text,index,buffer);

Selection in a Multiple Select List Box

For multiselect list boxes, the messages listbox_message::set_selection and listbox_message::get_selection may not be used. Neither may the message listbox_message::select_string be used. Instead, to select an item without affecting the selection of other items, the following call may be made:

send_message(listbox,listbox_message::set_selection_state,value,index);

where, 'value' is non-zero to select and highlight the item and zero to deselect the item. The selection state of a particular item may be determined via the following call:

int selected = send_message(listbox,listbox_message::get_selection_state,index);

where, the value of selected is non-zero if the item is selected and zero otherwise.

An Example of a List Box

An example of an application containing a list box is the topic of this section. A snapshot of the sample in execution is shown below.

A display (static) control is used to display an environment variable that is selected via the list box immediately below. When the selection in the list box is changed, the display field is updated with the value of the environment variable. The list box and display handles are declared in the client data strcture.

Most of the work in creating and initializing the list box is performed during the processing of the message message::create - as shown below.

    case message::create:
    {
        window_data* data = new window_data();
        set_window_pointer(window_handle, 0, (void*)data);

        handle device_context = get_device_context(window_handle);

        text_metrics<character> text_metrics_get;
        get_text_metrics(device_context, &text_metrics_get);

        release_device_context(window_handle, device_context);

        data->listbox = create_window("listbox",
            "",
            style::child | style::visible | listbox_style::_standard,
            use_default,
            use_default,
            use_default,
            use_default,
            window_handle,
            (handle)1);

        move_window(data->listbox,
            text_metrics_get.average_character_width,
            text_metrics_get.height * 3,
            text_metrics_get.average_character_width * 16
            + get_system_metrics(system_metric::vertical_scroll_width),
            text_metrics_get.height * 5);

        data->display = create_window("static",
            "",
            style::child | style::visible | display_style::left,
            use_default,
            use_default,
            use_default,
            use_default,
            window_handle,
            (handle)2);

        move_window(data->display,
            text_metrics_get.average_character_width,
            text_metrics_get.height,
            text_metrics_get.average_character_width * maximum_environment,
            text_metrics_get.height);

        character* environment_save = get_environment_strings<character>();
        character* environment = environment_save;
        while (*environment)
        {
            character* equals = lstrchr(environment, '=');
            *equals = '\0';

            if (environment != equals)
                send_message(data->listbox, listbox_message::add_string, 0, (parameter)environment);

            environment = (equals + 1) + string_length(equals + 1) + 1;
        }

        free_environment_strings(environment_save);
    }
    break;

Once the list box and display field have been created, they are positioned and the environment block is queried. A while loop then steps through the environment block picking out the keys and skipping over the values of the variables (the function lstrchr finds a character within a string). Each key is added to the list box.

When a selection is made in the list box, the owner (being the client) is notified via message::command - which has been coded as shown below.

case message::command:
    {
        window_data* data = (window_data*)get_window_pointer(window_handle, 0);

        if (low_part(parameter1) == 1 && high_part(parameter1) == listbox_notify::selection_change)
        {
            int i = (int)send_message(data->listbox, listbox_message::get_selection);

            character buffer[maximum_environment + 1];

            int length = (int)send_message(data->listbox,
                listbox_message::get_text,
                (parameter)i,
                (parameter)buffer);

            get_environment_variable(buffer,
                buffer + length + 1,
                maximum_environment - (length + 1));

            *(buffer + length) = '=';

            set_window_text(data->display, buffer);
        }
    }
    break;

When the notification of a selection change is received from the list box, the application queries the selection and then obtains its text. Following this, the text is used to query the associated environment variable. An equals sign is placed neatly between the key and its value and the resultant text is sent to the display control.