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.
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++:
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.
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.
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.
#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; }
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.
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.
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.
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.
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."
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.
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:
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:
Control ID | Variable Name | Category | Type |
IDC_LIST | m_listBox | Control | CListBox |
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.
// 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.
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.
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:
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.
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.
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.
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:
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.
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.
To create a sample project using a combo box and the CComboBox class, follow these steps:
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.
// In OnInitDialog... // TODO: Add extra initialization here m_comboList.AddString( "Foo" ); m_comboList.AddString( "Bar" ); m_comboList.AddString( "Baz" );
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.
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(); }
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.
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.
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.
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.
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."
© Copyright, Macmillan Computer Publishing. All rights reserved.