Using the ListBox Class

Using the ListBox Class

The ListBox class is used to create a list box control and provides access to all of the features that are available to a standard Win32 list box. In addition, the ListBox class neatly wraps up much of the functionality required to create an owner-drawn list box, so you can easily take over the task of rendering the list box and its items if you want to provide a different user interface for your control. An owner-drawn list box can include list items that contain information other than, or in addition to, text strings.

You don’t directly interact with items stored in the list box control—instead, you’ll usually work with the following list box collection classes, which are used to represent items stored in the list box:

  • ListBox.ObjectCollection  Contains all items displayed in the list box control

  • ListBox.SelectedIndex  Contains the index of each selected item in the list box control

  • ListBox.SelectedObjectCollection  Contains a reference to each selected item in the list box control

  • ListBox.SelectedIndexCollection  Contains the indexes to the selected items in the list box control

Of these classes, the one you’ll most often use is ListBox.ObjectCollection because it contains an entry for each item in the list box. A reference to the collection is obtained through the Items property, like this:

private void RemoveItems(ListBox listBox)
{
    ListBox.ObjectCollection items = listBox.Items;
    if(items.Count != 0)
    {
        
    

    }
}

Typically, you won’t extract and hold a reference to ObjectCollection; instead, you’ll just use the Items property as if it were the actual collection, as shown in the following code, which determines the number of items in the list box:

private void RemoveItems(ListBox listBox)
{
    if(listBox.Items.Count != 0)
    {
        
    

    }
}
Adding Items to a List Box

To add an item to a list box, you must add a reference to the item to the ObjectCollection object maintained by the ListBox class. To add an item at the end of the list box, use the Add method, as shown here:

private void newItem_Click(object sender, System.EventArgs e)
{
    AddListBoxItemForm dlg = new AddListBoxItemForm();
    DialogResult result = dlg.ShowDialog(this);
    if(result == DialogResult.OK)
    {
        string newItem = dlg.NewItem;
        listBox.Items.Add(newItem);
    }
}

To add an item at a specific position in the list box, use the Insert method, passing a 0-based index for the new item, like this:

private void newItem_Click(object sender, System.EventArgs e)
{
    
    

    listBox.Items.Insert(0, newItem);
}

You must be sure that the index passed to Insert is a valid index for inserting items into the list box. The index must be no smaller than 0 and no larger than the number of items currently in the list box. If you pass a smaller or larger value as the index, an ArgumentOutOfRangeException exception will be thrown.

When adding multiple items to a list box, you can optimize your interaction with the ListBox class by using the AddRange method, which enables you to add multiple items at once. There are two overloaded versions of AddRange. The following code passes an array of objects to AddRange, which will insert each array element into the list box:

private void InitListBox()
{
    string [] names = {"Rob", "John", "Alice", "Jen", "Gwen" };
    listBox.Items.AddRange(names);
}

You also can pass a reference to an existing ListBox.ObjectCollection instance, which simplifies the task of copying list box contents from one control to another, as shown here:

private void CopyLeftToRight()
{
    rightListBox.Items.AddRange(leftListBox.Items);
}

You’re not limited to adding simple strings to a list box. Instances of more complex types can be added. As long as the types override the ToString method, the ListBox class will insert the string returned from the ToString method into the list box control. For example, consider a class declaration such as the following:

public class Sailboat
{
    public Sailboat(string name, int length)
    {
        _name = name;
        _length = length;
    }
    public string Name
    {
        set { _name = value; }
        get { return _name; }
    }
    public int Length
    {
        set { _length = value; }
        get { return _length; }
    }
    override public string ToString()
    {
        return _name;
    }
    string _name;
    int    _length;
}

Because the Sailboat class overrides the ToString method, Sailboat objects can be added to the list box, as shown here:

private void newBoat_Click(object sender, System.EventArgs e)
{
    Sailboat larrysBoat = new Sailboat("Sayonara", 65);
    Sailboat phillipesBoat = new Sailboat("Pegasus", 40);
    listBox.Items.Add(larrysBoat);
    listBox.Items.Add(phillipesBoat);
}

The ListBox class will invoke Sailboat.ToString and add the sailboat’s name to the list box control.

Removing Items from a List Box

To remove an item from a list box, you must remove the item from the ListBox object’s collection of items. To remove an item at a specific location, pass the item’s index to the RemoveAt method, as shown here:

listBox.Items.RemoveAt(index);

The index passed to RemoveAt must be within the valid range for the current list box, with a value not less than 0 and no larger than the index of the last item in the list box. If an invalid index is passed to RemoveAt, an ArgumentOutOfRangeException exception will be thrown.

If you have a reference to the object to be removed, you can pass an object reference to the Remove method, which will search through the items stored in the collection and remove the first object in the collection that’s equivalent to the object passed as a parameter, as shown here:

listBox.Items.Remove(larrysBoat);

The Clear method provides an efficient way to remove every item in the list box:

listBox.Items.Clear();
Preventing Repainting When Updating a List Box

When an item is added to or removed from a list box, the list box is repainted. The preferred way to add multiple items to a list box is to use the AddRange method, which enables you to add multiple items in a single operation. However, you might need to add items to the list box one at a time and call Add or Insert once for each item to be added. Also, there’s no method to remove multiple items in a single call. This can be a problem if you’re adding or removing multiple items from the list box, as the repainting can become distracting.

To avoid unnecessary repainting, use the BeginUpdate and EndUpdate methods, as shown here:

private void updateListBox()
{
    listBox.BeginUpdate();
    foreach(SailboatSlip slip in Marina)
    {
        listBox.Items.Add(slip.boat);
    }
    listBox.EndUpdate();
}

As shown in this example, the BeginUpdate method turns off the redrawing of the list box control. After all of the items have been updated, the EndUpdate method is called to redraw the list box and restore its default behavior.

Selecting List Box Items

There are a number of ways to interact with the selected items in a list box. You can elect to retrieve either index information about the items selected in a list box or references to the objects that underlie the selected items.

Retrieving the Selected Index

To retrieve the index of the currently selected item, use the SelectedIndex property. This property will return –1 if no item is selected; otherwise, it returns the index value of the selected item. The following code illustrates how the Selected­Item property is typically used to determine which item should be affected by an operation on the list box—in this case, the selected item is removed:

if(listBox.Items.Count != 0)
{
    int index = listBox.SelectedIndex;
    if(index != -1)
    {
        listBox.Items.RemoveAt(index);
    }
    else
    {
        MessageBox.Show("You must select an item to remove",
                        "Can't remove item",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Hand);
    }
}
else
{
    MessageBox.Show("The list box is empty",
                    "Nothing to remove",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Hand);
}

If the list box supports multiple selections, the SelectedIndex property will return the index of one of the selected items. The SelectedIndex property is useful when further work on the control is anticipated, such as removing the currently selected item based on its index. However, there are times when you really want to retrieve the object stored in the list box rather than its index—in these cases, you can use the SelectedItem property.

Retrieving a Reference to the Selected Item

The SelectedItem property returns a reference to the object that underlies the selected item in the list box. For example, if a list box contains Sailboat objects, the following code returns the Sailboat object that’s currently selected by the user:

Sailboat boat = (Sailboat)listBox.SelectedItem;
if(boat)
{
    
    

}

As with the SelectedIndex property, SelectedItem will return one of the selected items if the list box supports multiple selections.

Working with Multiple-Selection List Boxes

When dealing with a list box that supports multiple selections, you can choose to retrieve the set of selected indexes or items. To fetch the selected indexes for a list box, use the SelectedIndices property, as shown here:

ListBox.SelectedIndexCollection indices = listBox.SelectedIndices;

Because many items can be selected in a multiple-selection list box at any given time, the SelectedIndices property returns an instance of SelectedIndex­Collection, which contains the indexes of all items currently selected in the list box. The following code provides an example of how you can use the Selected­IndexCollection object to determine the currently selected indexes:

ListBox.SelectedIndexCollection indices = listBox.SelectedIndices;
foreach(int index in indices)
{
    Trace.WriteLine("Selected index – " + index.ToString());
}

Watch out for code such as the following. If you modify the contents of the list box, the SelectedIndexCollection is updated as the loop progresses, and you won’t remove the items you expect.

ListBox.SelectedIndexCollection indices = listBox.SelectedIndices;
int selected = indices.Count;
if(indices.Count > 0)
{
    foreach(int index in indices)
    {
        listBox.Items.RemoveAt(index);
    }
}

In this code, the first iteration of the loop removes the first selected item and then sets index to the value of the second selected item. Because one selected item has already been removed, however, one item has been skipped. As items are removed from the list box, the foreach loop skips past every second selected item. You can avoid this problem by removing items in reverse order, as shown in the following code:

ListBox.SelectedIndexCollection indices = listBox.SelectedIndices;
int selected = indices.Count;
if(indices.Count > 0)
{
    for(int n = selected - 1; n >= 0; n--)
    {
        int index = indices[n];
        listBox.Items.RemoveAt(index);
    }
}

By removing indexes in reverse order, as shown here, the collection ordering remains valid and the proper items are removed from the list.

Another way to reference multiple selected items in a list box is to use the SelectedItems property. This property returns an instance of SelectedObject­Collection, which is used much like SelectedIndexCollection except that it stores a reference to each selected item instead of its index. As shown in the following code, this enables you to perform operations on the objects stored in the list box:

ListBox.SelectedObjectCollection selectedItems = listBox.SelectedItems;
foreach(Sailboat boat in selectedItems)
{
    DisplayBoatInfo(boat);
}
Selecting Items Programmatically

Although items in a list box are typically selected by the user, you can also select items programmatically. A simple way to set an item as selected is to use the SelectedIndex property, as shown here:

listBox.SelectedIndex = 3;

You can’t clear the selection state of a single item in a multiple-selection list box using the SelectedIndex property, but you can clear all of the selected items for any type of list box by setting the SelectedIndex property to –1, as shown here:

listBox.SelectedIndex = -1;

A more flexible way to select or deselect an item is to use the SetSelected method, passing the item’s index and a Boolean value that represents the selection state, as shown in this example:

listBox.SetSelected(0, true);

The SetSelected method works with the current selection mode of the list box. For single-selection list boxes, a selection of a new item will deselect any currently selected item. For list boxes that support multiple selections, selecting a new item doesn’t automatically deselect any currently selected items.

Handling Selection Events

It’s often useful to know exactly when selections in a list box control have changed. For example, if you provide help text or populate additional controls based on the current list box selection, you can immediately update your user interface when the user selects a new item. Three selection events are raised by the ListBox class, as follows:

  • SelectedIndexChanged

  • SelectedValueChanged

  • DoubleClick

The first two events are raised whenever an item is selected or deselected in the list box control. These events receive the generic EventArgs parameter, with no information about the newly selected or deselected items. Instead, you’re expected to use these events to trigger a reexamination of the list box to determine which item or items are currently selected.

A DoubleClick event is generated when an item in the list box is double-clicked by the user. DoubleClick is often used as shown in the following code:

private void listBox_DoubleClick(object sender, System.EventArgs e)
{
    Sailboat boat = (Sailboat)listBox.SelectedItem;
    string message = string.Format("Sailboat: {0}, length: {1}",
                                    boat.Name,
                                    boat.Length);
    MessageBox.Show(message);
}

In this example, the MessageBox.Show method displays a new form that contains information based on the selected item. This is a common shortcut that helps to simplify navigation for experienced users.

Using Other List Box Properties

The ListBox class exposes a number of other useful properties that simplify your interaction with list box controls. The more commonly used ListBox properties are listed here:

  • MultiColumn  Enables the display of items in multiple columns

  • SelectionMode  Enables you to define how items are selected in the list box

  • Sorted  Enables automatic sorting of list box items

  • TopIndex  Sets or returns the topmost visible item in the list box

The MultiColumn property enables the list box to display items in multiple columns within the list box. When this property is set to true, as shown in the following code, items will be displayed in as many columns as required to avoid the need for vertical scrolling:

listBox.MultiColumn = true;

Normally, the width of each column is set to a default value. You can specify a value using the ColumnWidth property, however, passing the number of pixels required for the width, as shown here:

listBox.ColumnWidth = 100;

The SelectionMode property determines how list box items are selected. Values for this property are taken from the SelectionMode enumeration, shown in Table 12-8.

The SelectionMode property has a default value of SelectionMode.One.

By default, items in a list box aren’t sorted. However, it’s often convenient to sort the items in a list box. If the user is expected to select items in a list box, the task is generally much easier when the items are ordered. The ListBox class exposes the Sorted property, which, when set to true, automatically keeps a list box control properly sorted alphabetically, as shown here:

private void sortCheckBox_CheckedChanged(object sender,
                                         System.EventArgs e)
{
    listBox.Sorted = sortCheckBox.Checked;
}

In this example, a CheckedChanged event handler for a check box control is used to specify the sorting behavior for the list box. When the user selects the check box, the list box is sorted. When the check box is cleared, the order of items in the list box is no longer preserved.



Part III: Programming Windows Forms