Teach Yourself Visual C++® 5 in 24 Hours

Previous chapterNext chapterContents


- Hour 7 -
Using List Box and Combo Box Controls

List boxes and combo boxes are two types of controls that are used often in Windows programming. The list box often is used to enable a user to select from a large number of possible choices, whereas the combo box is a combination of the list box and edit controls. In this hour, you will learn about these controls and use them in simple examples.

Using Loops

In Hour 5, "Button Controls," you learned about using conditional expressions to control the flow of execution in C++ programs. Another way to control the flow of execution in your program is to execute sequences, also known as loops or iterations. Popular uses for loops include waiting for user input, printing a certain number of reports, or reading input from a file until an End Of File (EOF) mark is detected. Three different loop statements are used in C++:

Using the while Loop

The while loop is used to execute a statement as long as a test expression evaluates as true. Listing 7.1 shows an example of a while loop.

TYPE: Listing 7.1. Executing a while loop 10 times.

CString szMsg;
szMsg.Format("This is loop number %d", nLoopCounter);
int nLoopCounter = 0;
while(nLoopCounter < 10)
{
    nLoopCounter++;
    AfxMessageBox(szMsg);
}
AfxMessageBox("The loop is finished");

In Listing 7.1, the compound statement following the while loop is executed as long as nLoopCounter is less than 10. When nLoopCounter is equal to 10, the condition tested by while becomes false, and the next statement following the block controlled by while is executed. In this example, a compound statement is executed; however, a single statement can also be executed.

Compound statements were discussed in Hour 5. A compound statement is a group of statements that are enclosed within curly braces.

Using a do-while Loop

A relative of the while loop is the do-while loop. The do-while loop is used when a statement or series of statements must be executed at least once. Listing 7.2 is an example of a do-while loop used to check an input character for Q.

TYPE: Listing 7.2. Using the do-while loop to test for user input in a console mode program.

#include <iostream>
using namespace std;

int main()
{
    char ch;

    do{
        cout << "\nPress `Q' to exit ->";
        cin >> ch;
        // Ignore input until a carriage return.
        cin.ignore( 120, `\n');
    }while( ch != `Q' );

    cout << "Goodbye" << endl;

    return 0;
}

Using the for Loop

The for loop is often used in C++ programs to write a very compact loop statement. The for loop enables you to write loops in a more compact style than is possible using while loops. Listing 7.3 is equivalent to Listing 7.1, except that it has been rewritten using the for loop.

TYPE: Listing 7.3. Using a for loop to display a message 10 times.

CString szMsg;
szMsg.Format("This is loop number %d", nLoopCounter);
for(int nLoopCounter = 0; nLoopCounter < 10; nLoopCounter++)
{
    AfxMessageBox(szMsg);
}
AfxMessageBox("The loop is finished");

There are four components to every for statement:

for( expression1; expression2; expression3 )
    statement1

When the for loop begins, expression1 is executed. This is usually where you declare loop counters. As long as expression2 is true, the statement controlled by the loop is executed. After the controlled statement (statement1) has been performed, expression3 is executed. Loop counters are usually incremented in expression3.


Just a Minute: In the example provided in Listing 7.3, the expression nLoopCounter++ was used as a way to increment the value of nLoopCounter by one. To decrement the value, you can use nLoopCounter--.


Time Saver: As a rule of thumb, if the loop is executed a fixed number of times, it's usually easier to use for instead of while. However, if you are waiting for an event to occur, or if the number of loops isn't easily predicted, it's better to use while.

What Are List Boxes?

New Term: List box controls are used to contain a list of items available for selection. The user can select items by using the keyboard or by clicking an individual item using a mouse.

New Term: A single selection list box enables one item to be selected at a time. This is the default style for a list box.

New Term: A multiple selection list box enables multiple items to be selected at one time.

A list box normally is found in a dialog box, control bar, or other window that contains controls. List boxes often are used to contain a large number of items. If some items cannot be displayed, a scrollbar is displayed to help the user navigate through the list.

Why Use a List Box?

List boxes are the simplest control that enables an arbitrary number of items to be displayed to a user. List boxes are often used to display lists of information that are extracted from databases or reports. Because the list box doesn't have to be sized, it is well-suited for this type of data. When a sorted list box is used, it's easy for a user to search through a large number of text items and make a selection.

List boxes are also extremely easy to program. If you have created a list box object, you can add an item to the list box with just one line of code, like this:

listBox.AddString("Gwen");

No other control is as flexible and easy to use for both the programmer and the user.


Just a Minute: The list box is also the first control you have seen that uses indexes. Whenever an item is selected, inserted, or deleted, a zero-based index is used to identify the item. This index can be synchronized with a database index or used to identify the item in other ways.

MFC Support for List Boxes

You normally add list boxes to a dialog box resource just as you added buttons and edit controls in the previous two hours. After you have added the control, use ClassWizard to add message-handling functions and associate the control with a CListBox object.

You can use the MFC CListBox class to manage and interact with the list box control. Like other control classes, CListBox is derived from CWnd, and most CWnd functions can be used with CListBox objects. You will see more details about the CListBox later, in the section, "Using the CListBox Class."

Adding a List Box to a Dialog Box

For demonstration purposes, create a dialog box-based project named ListBox using AppWizard, following the steps presented in Hour 4, "Dialog Boxes and C++ Classes." Click the ResourceView tab in the project workspace. Open the dialog box editor by double-clicking the IDD_LISTBOX_DIALOG icon in the Dialog resource folder.

Adding a list box to IDD_LISTBOX_DIALOG, the main dialog box, is just like adding a button or edit control. Either drag and drop a list box control from the control palette to the main dialog box, or select the list box control on the tool palette using the mouse, and click the desired position in the main dialog box. Figure 7.1 shows the IDD_LISTBOX_DIALOG with a list box control.

Figure 7.1.
The main dialog box used in the ListBox sample program.

Open the Properties dialog box for the list box by right-clicking the control and selecting Properties from the shortcut menu. Change the resource ID to IDC_LIST. Set all other properties to their default values.

List Box Properties

Just like other controls, list boxes have properties that you can configure using the Developer Studio resource editor. Some of these properties are available in other controls, and some are unique to list boxes. These properties are available for a list box control:

Using the CListBox Class

Like control classes used in previous hours, the MFC CListBox class makes your life much easier by providing a C++ class that hides control messages and provides an easy-to-use interface. To attach a CListBox object to a list box control, use ClassWizard as you have for controls in previous hours:

1. Open ClassWizard.

2. Select the CDialog-derived class that manages the dialog box; in this case, CListBoxDlg.

3. Select the Member Variables tab.

4. Select the control ID representing the control associated with the new member variable; in this case, IDC_LIST.

5. Click the button labeled Add Variable. An Add Member Variable dialog box appears. Enter the control's name, category, and variable type; then click OK. For this example, use the values from Table 7.1.

Table 7.1. Values used to add a CListBox member variable for CListBoxDlg.

Control ID Variable Name Category Type
IDC_LIST m_listBox Control CListBox

Adding an Item to a List Box

There are two ways to add a text string to a list box:

m_listBox.AddString( "Rene'" );
m_listBox.InsertString( 0, "Alex" );

Both the InsertString and AddString functions return the position of the new item. If an error occurs when adding an item, LB_ERR is returned from the AddString or InsertString functions. If the list box is full, LB_ERRSPACE is returned. Using the source code from Listing 7.4, add three strings to the IDC_LIST list box during the CListBoxDlg::OnInitDialog member function. There already are several lines of code in CListBoxDlg::OnInitDialog; add the three AddString statements after the //TODO comment supplied by AppWizard.

TYPE: Listing 7.4. Using AddString to add strings to a list box.

// TODO: Add extra initialization here
m_listBox.AddString( "Foo" );
m_listBox.AddString( "Bar" );
m_listBox.AddString( "Baz" );

To determine the number of items currently in a list box, use the GetCount member function:

nItems = listBox.GetCount();


CAUTION: The GetCount function returns the total number of items in a list box, not the value of the last valid index. If a list box contains five items, GetCount returns five, but the last valid index is four.

Removing Items from a List Box

To remove items from a list box, specify the item position to be removed in a call to the DeleteString member function:

listBox.DeleteString(8);

This line removes the item in the ninth position of the list box. Remember, all list box position indexes start from zero. The return value from the DeleteString member function is the number of items remaining in the list box, or LB_ERR if any errors occur. The return value can be used like this:

int nItems = listBox.GetCount();
while(nItems > 3 && nItems != LB_ERR )
    nItems = listBox.DeleteString(nItems - 1);

This code removes the contents of a list box, except for the first three items. To clear a list box completely, use the ResetContent function:

listBox.ResetContent();

The ResetContent function returns void.

Receiving List Box Messages

Several messages are sent to the parent of a list box for notification purposes when certain events occur. All these messages are prefixed with LBN_, for List Box Notification. For these messages to be sent, the list box must have the Notify property enabled. The following messages are sent from the list box to its parent:

The LBN_DBLCLK message is the most frequently used notification message. Most users expect some sort of default action to take place when a list box item is double-clicked. For example, when a list of filenames is displayed, double-clicking a particular filename might be expected to open that file for editing.

The steps to add message-handling functions for any of the controls used in Windows are very similar. To create a message-handling function for the LBN_DBLCLK notification message, follow these steps:

1. Open ClassWizard and click the Message Maps tab.

2. Select the CListBoxDlg class and the IDC_LIST Object ID.

3. Select LBN_DBLCLK, and click the Add Function button.

4. Accept the suggested function name CListBoxDlg::OnDblclkList.

5. Click the button labeled Edit Code.

6. Add the source code from Listing 7.5 to the CListBoxDlg::OnDblclkList function.

TYPE: Listing 7.5. Handling a list box notification message.

void CListBoxDlg::OnDblclkList()
{
    int nSelection = m_listBox.GetCurSel();
    if( nSelection != LB_ERR )
    {
        CString szSelection;
        m_listBox.GetText( nSelection, szSelection );

        AfxMessageBox( szSelection );
    }
}

Compile and run the ListBox project and then double-click any of the list box items. The LBN_DBLCLK message is sent to the CListBoxDlg::OnDblclkList function, and a message box is displayed with information about the selected item.

You can determine the currently selected item in the list box by using the CListbox::GetCurSel member function, as shown in Listing 7.5. The GetCurSel member function returns the position of the currently selected item, with the first item position starting at zero. If no item is selected, or if the list box has the multiple-selection property, LB_ERR is returned.

What Are Combo Boxes?

A combo box control is a single control that combines an edit control with a list box. A combo box enables a user to enter data either by entering text like an edit control or by selecting an item from several choices like a list box.

Combo boxes are quite useful when a user is not limited to selecting only the items presented in a list box. The list box portion of the combo box can be used to display recent selections, giving the user the freedom to enter a new selection in the edit control.

There are three types of combo boxes:


Just a Minute: Combo boxes also are used when space in a dialog box is at a premium. A large number of choices in a combo box can be hidden until the combo box is opened, enabling more controls to be placed in a smaller area than that required for a list box.

MFC Support for Combo Boxes

Just like list boxes and other controls, you normally add combo boxes to dialog box resources using the Developer Studio dialog box editor. After you add the control, use ClassWizard to add message-handling functions and associate the control with a CComboBox object.

You use the MFC CComboBox class to manage and interact with the combo box control, and it contains many of the member functions that are available in the CListBox and CEdit classes. For example, you can use GetCurSel to get the currently selected item from the list box part of a combo box.

Combo Box Properties

A combo box has a large number of properties because it combines an edit control and a list box. Most edit-control and list-box styles have similar properties that can be applied to combo boxes. These combo box properties are identical to the list box properties discussed earlier:

The following combo box properties are identical to properties offered for edit controls (discussed in Hour 6, "Using Edit Controls"):

These two properties are unique to combo box controls:

Adding Items to a Combo Box

You add strings to combo boxes just as you add them to list boxes. Just like CListBox, the CComboBox class contains AddString and InsertString member functions:

comboBox.AddString( "Riley" );

or

comboBox.InsertString( 0, "Mitch" );

All positions in a combo box are numbered beginning with zero, just like list boxes. However, if an error occurs, CB_ERR is returned instead of LB_ERR. If an item cannot be added due to insufficient space, CB_ERRSPACE is returned.

To determine the number of items currently in a combo box, CComboBox includes the GetCount member function:

nItems = comboBox.GetCount();

Remember, CB_ERR is returned instead of LB_ERR when using a CComboBox object.

Collecting Input from a Combo Box

You can collect input from a combo box by using the GetWindowText member function, just like an edit control. For simple combo boxes and drop-down combo boxes, this is the easiest way to get the current selection. You can also use the GetCurSel member function to determine the current selection position from the list box.

A Combo Box Example

To create a sample project using a combo box and the CComboBox class, follow these steps:

1. Create a dialog box-based project named ComboList using AppWizard, as described in previous examples.

2. Add a drop-down combo list to the IDD_COMBOLIST_DIALOG resource, as you did for the list box earlier in this hour.

3. Give the combo box the resource ID IDC_COMBO. Use the default values for all other properties.

4. Add a static-text control to the dialog box, and give it the resource ID IDC_RESULT. This text control will be used to display information about messages received from the combo box.

5. Using ClassWizard, add a member variable to the CComboListDlg class named m_comboList. Set the Category to Control.

6. Using ClassWizard, add a message-handling function for the IDOK control BN_CLICKED message to the CComboListDlg class.

7. Using ClassWizard, add message-handling functions for IDC_COMBO control messages to the CComboListDlg class. Add functions to handle CBN_CLOSEUP and CBN_EDITUPDATE messages.

Adding Strings to a Combo Box

After completing these steps, add the source code in Listing 7.6 to the CComboListDlg::OnInitDialog member function. This code adds three entries to the combo box. There are already several lines of code in the function; don't remove them. Just add the code from Listing 7.6 after the //TODO comment provided by AppWizard.

TYPE: Listing 7.6. Source code added to the CComboListDlg::OnInitDialog function.

// In OnInitDialog...
// TODO: Add extra initialization here
    m_comboList.AddString( "Foo" );
    m_comboList.AddString( "Bar" );
    m_comboList.AddString( "Baz" );

Getting the Current Combo Box Selection

Edit the CComboListDlg::OnOK member function so it looks like the source code provided in Listing 7.7. This code uses member functions from the CComboBox class to display information about the current combo box selection.

TYPE: Listing 7.7. Source code added to the CComboListDlg::OnOK function.

void CComboListDlg::OnOK()
{
    CString szCombo;

    m_comboList.GetWindowText( szCombo );
    AfxMessageBox( szCombo );

    int nChoice = m_comboList.GetCurSel();
    szCombo.Format( "The current selection is %d", nChoice );
    AfxMessageBox( szCombo );

    CDialog::OnOK();
}

Detecting Combo Box Events

Add the source code provided in Listing 7.8 to the CComboListDlg::OnCloseupCombo function. When the CBN_CLOSEUP message is received, a message is displayed on the static-text control IDC_RESULT.

TYPE: Listing 7.8. Source code added to the CComboListDlg::OnCloseupCombo function.

void CComboListDlg::OnCloseupCombo()
{
    CString     szChoice;
    CString     szResult;
    int         nChoice;

    // Get current selections from edit and list-box controls
    m_comboList.GetWindowText( szChoice );
    nChoice = m_comboList.GetCurSel();

    if( nChoice != CB_ERR )
    {
        // If a valid choice was made from the list box, fetch
        // the item's text string.
        m_comboList.GetLBText( nChoice, szChoice );
        szResult = "Closing after selecting " + szChoice;
    }
    else if( szChoice.IsEmpty() == TRUE )
    {
        // No choice was made from the list box, and the edit
        // control was empty.
        szResult = "No choice selected";
    }
    else if( m_comboList.FindStringExact(-1, szChoice) != CB_ERR )
    {
        // The string from the edit control was found in the
        // list box.
        szResult = "Closing after selecting " + szChoice;
    }
    else
    {
        // The edit control contains a new string, not currently
        // in the list box. Add the string.
        m_comboList.AddString( szChoice );
        szResult = "Adding " + szChoice + " to list";
    }

    // Get a pointer to the static-text control, and display an
    // appropriate result message.
    CWnd* pWnd = GetDlgItem( IDC_RESULT );
    ASSERT( pWnd );
    if( pWnd )
        pWnd->SetWindowText( szResult );
}

The CComboListDlg::OnCloseupCombo function collects the contents from the edit control section of the combo box and the selected item from the list box section of the combo box. If a selection has been made in the list box, the item's string is retrieved and displayed. Otherwise, if a string was entered in the edit control, it is displayed. The string is not currently in the list box; it is added to it.

Add the source code provided in Listing 7.9 to the CComboListDlg::OnEditupdateCombo member function. CBN_EDITUPDATE is received when the user types inside the edit control. When the CBN_EDITUPDATE message is received, the contents of the edit control are displayed on the IDC_RESULT text control.

TYPE: Listing 7.9. Source code added to the CComboListDlg::OnEditupdateCombo function.

void CComboListDlg::OnEditupdateCombo()
{
    CString     szChoice;
    CString     szResult;

    m_comboList.GetWindowText( szChoice );
    szResult = "Choice changed to " + szChoice;

    CWnd* pWnd = GetDlgItem( IDC_RESULT );
    ASSERT( pWnd );
    if( pWnd )
        pWnd->SetWindowText( szResult );
}

Compile and run the ComboList project. Experiment by adding new entries to the combo box and by expanding and closing the combo box. Other messages sent to the combo box can be trapped and displayed just as CBN_EDITUPDATE was handled in Listing 7.9.

Summary

In this hour, you learned about list box and combo box controls and how they are used in Windows programs. You also learned how to associate these controls with CListBox and CComboBox objects.

Q&A

Q What is the easiest way to create a list box that has a bitmap image next to each item?

A The only way to display a bitmap in a list box is to create an owner-drawn list box, where you take responsibility for drawing each item in the list box. You can easily achieve a similar effect by using a list view control, which is discussed in Hour 18, "List View Controls."

Q When should I use a combo box drop list, and when is a list box more appropriate?

A A drop list is appropriate when space on your dialog box is at a premium. A list box is more appropriate when the user must see more than one item without clicking on the control.

Workshop

The Workshop is designed to help you anticipate possible questions, review what you've learned, and begin thinking ahead to putting your knowledge into practice. The answers to the quiz are in Appendix B, "Quiz Answers."

Quiz

1. Which MFC class is used to manage list box controls?

2. What message is sent to your dialog box when a user double-clicks a dialog box?

3. What functions are used to add items to a list box control?

4. What function is used to retrieve the number of items in a list box control?

5. What function is used to retrieve the currently selected index in a list box?

6. What are the three styles used for list box controls?

7. What are the three types of loops used in C++ programs?

8. Which MFC class is used to manage combo boxes?

9. What function is used to add an item to a combo box at a specific index?

10. What are the three styles used for combo boxes?

Exercise

1. Modify the ListBox project by adding a new button labeled Loop. When a user clicks the Loop button, display each item in the list box in a message box, one item at a time.


Previous chapterNext chapterContents


Macmillan Computer Publishing USA

© Copyright, Macmillan Computer Publishing. All rights reserved.