Teach Yourself Visual C++® 5 in 24 Hours

Previous chapterNext chapterContents


- Hour 5 -
Button Controls

Button controls are probably the most flexible controls available in Windows. Before learning about buttons, though, it's important to begin with a short lesson about conditional expressions in C++ programs. In this hour you will also learn about

Later this hour, you will add each type of button to a dialog box-based project. You will also use ClassWizard to add button events and member variables for the dialog box's button controls.

What Are Conditional Expressions?

New Term: A conditional expression is an expression that results in a true or false value.

Most programs exercise some type of control over their execution flow using conditional expressions. They perform different actions based on varying conditions as the execution progresses. Then, they repeat these actions until all their tasks are complete. For example, a Windows program might need to search for a certain record from a database, or might take different actions depending on the messages that are sent to it.

New Term: A selection statement uses a conditional expression to pick a particular path of execution in your program. This is similar to choosing a fork in the road.

New Term: A sequence statement uses a conditional expression to determine how often to execute a part of your program.

Selecting an Execution Path with Selection Statements

The first set of control statements to look at are the selection statements. If your program must take a particular action only if a certain condition is true, or if a user must make a choice from a list of possible items, these statements are for you.


Just a Minute: All selection statements work by evaluating an expression, then taking an action based on the value of that expression.

Using the if Statement

The if statement enables one or more statements to be executed only if an expression inside the parentheses is true. If necessary, values inside the parentheses are converted into Boolean values, with zero being converted to false and all non-zero values converted to true.

Listing 5.1 provides a function that shows how the if statement is used. If the parameter passed to the function is greater than zero, the function returns a value of true.

TYPE: Listing 5.1. A function that returns true if a positive number is passed to it.

bool IsPositive( int nCheckValue )
{
    bool bReturn = false;

    if( nCheckValue > 0 )
        bReturn = true;

    return bReturn;

}

Using Compound Statements

The statement controlled by an if statement is executed only when the test condition is true. If more than one statement must be executed, group the statements together to form a compound statement. Compound statements are often called blocks because they group statements into blocks of code.

A compound statement begins and ends with curly braces, just like a function body. All the statements within a compound statement that follows an if statement are executed when the test condition is true, as shown in Listing 5.2.

TYPE: Listing 5.2. Using a compound statement to group several statements together.

void PrintTest(bool bShouldPrint)
{
    if( bShouldPrint == true )
    {
        cout << "A short demonstration of" << endl;
        cout << "a compound statement - also" << endl;
        cout << "known as a block." << endl;
    }

} In Listing 5.2, the test for equality is made using ==, the equality operator.


CAUTION: A common mistake is to use =, which is the assignment operator.

A standard code-formatting convention is to visually nest each conditional "level" of your source code by indenting statements, as in Listings 5.1 and 5.2. Indentation helps make your code more readable because it helps make the flow of control in your source code easy to see.

Using else with if Statements

You can couple an else statement with an if statement to create an either/or selection. When the expression tested by the if statement is true, the first statement (or block statement) is executed. When the expression is false, the statements grouped with the else statement are executed instead.

Listing 5.3 provides an example of a function that uses the if and else statements. This function always returns the larger of two parameters passed to it.

TYPE: Listing 5.3. A function that uses the if and else statements.

int GetMax( int nFirst, int nLast )
{
    int nReturn;

    if( nFirst > nLast )
        nReturn = nFirst;
    else
        nReturn = nLast;

    return nReturn;

}

Using the switch Statement

Sometimes you must choose between more than just one or two alternatives. Suppose you are implementing a simple menu function with three choices. If you use the if statement, you might wind up with a function like the one shown in Listing 5.4.

TYPE: Listing 5.4. A menu-selection function.

//
// Processes a selection from a character-based menu. If a
// valid selection is made, the proper functions are called,
// and true is returned. If an invalid selection is made,
// false is returned.
bool HandleMenuSelection( char chSelection )
{
    bool bValidSelection = true;

    if( chSelection == `F' )
        OpenNewFile();
    else if( chSelection == `P' )
        PrintDocument();
    else if( chSelection == `S' )
        SaveFile();
    else
        bValidSelection = false;

    return bValidSelection;
} 

This is already starting to look a little cluttered, but how bad would it look if you had a few more selections? What if you had 20 or 30? The solution is to use the switch statement. A switch statement evaluates an expression and then chooses from a list of choices, as shown in Listing 5.5.

TYPE: Listing 5.5. Using the switch statement.

bool HandleMenuSelection( char chSelection )
{
    bool bValidSelection = true;

    switch( chSelection )
    {
        case `F':
            OpenNewFile();
            break;
        case `P':
            PrintDocument();
            break;
        case `S':
            SaveFile();
            break;

        default:
            bValidSelection = false;
    }
    return bValidSelection;
} 

As Listing 5.5 shows, the switch statement has several different parts. Here are the major features of a switch statement:

What Is a Button?

New Term: A button is a special type of window that contains a text or bitmap label, usually found in a dialog box, toolbar, or other window containing controls.

Five different types of buttons are provided by Windows:

In general, buttons are used to indicate a user selection. Buttons are used in Windows programs because they are convenient and easy for users to operate. Users have come to expect buttons to be presented in a large number of cases, especially when dialog boxes are present in a program.

What Are Pushbuttons?

Almost every dialog box has at least one pushbutton control to indicate actions that a user can invoke. Some common uses for pushbuttons include closing a dialog box, beginning a search, or asking for help.

What Are Radio Buttons?

Radio buttons are used when a selection must be made from several mutually exclusive options, such as a user's gender. Only one of the radio buttons, which usually are grouped together, is checked at any particular time.

What Are Check Boxes?

Check boxes are used as Boolean flags that indicate whether a particular condition is true or false. Unlike radio buttons, several check boxes in a group can be checked. Optionally, a check box can support a third state--disabled--meaning that the control is neither true nor false.

What Are Group Boxes?

A group box logically groups controls that are used for similar purposes. This helps the user understand the relationships between controls and makes a dialog box easier to use. Radio buttons are almost always enclosed in a group box so that it's obvious which controls are associated with each other.

MFC Support for Buttons

Button controls normally are created as part of a dialog box. After you add a button to a dialog box, you can use ClassWizard to add functions that can be used to handle events created when the button is pressed, checked, or selected. You also use ClassWizard to create CButton objects that are associated with individual button controls.

You can use the MFC class CButton to interact with button controls--both buttons that have been added to a dialog box resource and buttons that have been created dynamically. Use ClassWizard to associate a button control with a specific CButton object.

A Sample Project Using Buttons

In order to see how button controls can be used with dialog boxes, create a dialog box-based project named Button using AppWizard, following the steps provided in Hour 4, "Dialog Boxes and C++ Classes." You will use this project for the rest of this hour as an example of how to use buttons in a dialog box.

Click the ResourceView tab in the project workspace. Open the dialog box editor by double-clicking the IDD_BUTTON_DIALOG icon in the Dialog resource folder.

The IDD_BUTTON_DIALOG dialog box is displayed in the dialog box editor, along with a dockable control toolbar or palette. The floating control palette contains all the controls available for a dialog box, as shown in Figure 5.1.

Figure 5.1.
The floating control palette, showing the buttons and boxes needed to create basic dialog boxes.

There are four different icons on the control palette for buttons, each used for a particular button type. Use one of the following steps to add a button control to a dialog box:

These steps apply for all controls in the control palette. After you've added a control to the dialog box, you can use the mouse to reposition and resize it.

As a demonstration, add several buttons to the main dialog box used in the Button project. You will use these controls later this hour to demonstrate button events. Refer to Figure 5.2 for the location of the added buttons.

Figure 5.2.
The main dialog box used by the Button project.

A total of five buttons are added to IDD_BUTTON_DIALOG. Use the values from Table 5.1 to set the properties for each control. Except for the ID and caption, all controls use the default set of properties.

Table 5.1. Values used for controls in IDD_BUTTON_DIALOG.

Control ID Button Type Caption
IDC_BTN_TEST Pushbutton &Test
IDC_RADIO_HIGH Radio button &High
IDC_RADIO_LOW Radio button &Low
IDC_GROUP_VOLUME Group control &Volume
IDC_CHECK_AMP Check box &Amplified

Button Control Properties

Like all controls, buttons have a set of properties that define the behavior of each control. Although there are four different types of button controls, they share a common set of properties. You can display the properties for a particular control by selecting Properties from the menu displayed when you right-click the control. These properties are shared by all button controls:

Group boxes support the fewest properties of any button control. All button properties are supported except Default Button and Owner Draw.

Radio buttons do not use the default button property because they aren't used as default buttons. However, radio buttons do support two properties not used by pushbutton controls:

Check boxes support the same properties as radio controls, except that they are used with one additional attribute:

In addition, all controls have a property page that is labeled Extended Styles. These styles are rarely used, and aren't discussed in this book.

Using Standard Pushbutton Layouts in Your Dialog Boxes

Several pushbuttons are commonly used in dialog boxes that contain controls. Because each of these pushbuttons carries a specific meaning, you should try to use the standard terminology whenever possible because it minimizes the amount of work required for users of your programs. Here are the standard meanings for these buttons:

Binding a Button Control to a CButton Object

The easiest way to set or retrieve the value of a control is to associate it with a class-member variable using ClassWizard. When associating a member variable with a control, you can associate the member variable either with the control or with the control's value. Member variables representing buttons are rarely associated by value; instead, the CButton class is used to represent most button controls. You will learn about associating member variables by value with dialog box controls in Hour 6, "Using Edit Controls."

To add a member variable to a CDialog-derived class, follow these steps:

1. Open ClassWizard.

2. Select the tab labeled Member Variables.

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

4. Select the control ID representing the control associated with the new member variable.

5. Press the button labeled Add Variable. An Add Member Variable dialog box appears.
Enter the control's name, category, and variable type, then press OK.

6. Close ClassWizard.

Follow these steps for all controls added to the IDD_BUTTON_DIALOG earlier. Use the values from Table 5.2 for each new member variable added to CButtonDlg.

Table 5.2. Values used to add member variables for CButtonDlg.

Control ID Variable Name Category Type
IDC_BTN_TEST m_btnTest Control CButton
IDC_GROUP_VOLUME m_btnVolume Control CButton
IDC_CHECK_AMP m_btnAmp Control CButton

ClassWizard automatically adds the member variables to the CButtonDlg class declaration for you.

Adding Button Events to a Dialog Box Class

Although the buttons are part of the dialog box resource and appear whenever the dialog box is displayed, nothing happens when the buttons are used because no button events are handled by the dialog box class.

Pushbuttons are normally associated with button events in a dialog box class. To add a button event for IDC_BTN_TEST, follow these steps:

1. Open ClassWizard.

2. Select the tab labeled Message Maps.

3. Select CButtonDlg as the class name.

4. Select IDC_BTN_TEST as the object ID.

5. Select BN_CLICKED from the Messages list box.

6. Press the button labeled Add Function and accept the default name for the member function.

7. Close ClassWizard.

Check boxes and radio buttons sometimes use BN_CLICKED messages, but not as often as pushbuttons. Add the source code from Listing 5.6 to the CButtonDlg::OnBtnTest function, then compile and run the project.

TYPE: Listing 5.6. The CButtonDlg::OnBtnTest member function.

void CButtonDlg::OnBtnTest()
{
    AfxMessageBox( "Test button pressed" );

}

Changing a Button's Label

Like all controls, a button is a just a special type of window. For that reason, the MFC class library uses the CWnd class as a base class for all control classes. To change the label for a button, you can use the SetWindowText function.

This function commonly is used to change the label for buttons after the dialog box has been created. You can use the SetWindowText function to change the Amplify button from the earlier example into a Record button. To do so, replace the CButtonDlg::OnBtnTest function with the function provided in Listing 5.7.

TYPE: Listing 5.7. Changing the label for several buttons.

void CButtonDlg::OnBtnTest()
{
    static BOOL bSetWaterLevel = TRUE;
    if( bSetWaterLevel == TRUE )
    {
        m_btnVolume.SetWindowText( "&Water Level" );
        m_btnAmp.SetWindowText( "&Record" );
        bSetWaterLevel = FALSE;
    }
    else
    {
        m_btnVolume.SetWindowText( "&Volume" );
        m_btnAmp.SetWindowText( "&Amplify" );
        bSetWaterLevel = TRUE;
    }
} 

After you build the Button example using the code from Listing 5.7, the radio button group will alternate between Volume and Water Level.

Enabling and Disabling Buttons

Most controls are enabled by default, although a control can be initially disabled by setting that attribute in its property list. A control can be selected only if it is enabled. The CWnd class includes the EnableWindow member function that allows a CWnd object to be enabled or disabled. Because CButton and all other control classes are derived from CWnd, they include all the member data and member functions from the CWnd class, and you can disable a button like this:

pButton->EnableWindow( FALSE );  // Disables control

The parameter for EnableWindow is TRUE if the window or control should be enabled, and FALSE if it should be disabled. The default parameter for EnableWindow sets the parameter to TRUE because no parameter is needed to enable the control:

pButton->EnableWindow();  // Enables control

It is common practice for buttons and other controls to be enabled or disabled based on events that are received by the dialog box. As an example, pressing one button can cause another button to be disabled or enabled. To disable a dialog box control, replace the CButtonDlg::OnBtnTest function with the source code provided in Listing 5.8.

TYPE: Listing 5.8. Using CWnd::EnableWindow to disable a dialog box control.

void CButtonDlg::OnBtnTest()
{
    static BOOL bEnableControl = FALSE;

     m_btnAmp.EnableWindow( bEnableControl );

    if( bEnableControl == TRUE )
        bEnableControl = FALSE;
    else
        bEnableControl = TRUE;
} 

Now when you click the Test button, the Amplify check box is disabled. When you click the Test button again, the check box is enabled.

Hiding a Button

It's not unusual to need to hide a button that is located in a dialog box. Often, a button has its properties set to be hidden by default. Once again, the CWnd class has a member function that can be used to hide or display a window as needed. Use the CWnd::ShowWindow member function like this:

pButton->ShowWindow( SW_HIDE );  // Hide control

This code hides the pButton window, which is a button control in this case. To display a hidden window, the ShowWindow function is used with the SW_SHOW parameter:

pButton->ShowWindow( SW_SHOW );  // Display control

Listing 5.9 provides a function that uses CWnd::ShowWindow to alternately hide and display some of the other buttons in the main dialog box.

TYPE: Listing 5.9. Using CWnd::ShowWindow to hide a dialog box control.

void CButtonDlg::OnBtnTest()
{
    static int nShowControl = SW_HIDE;

    m_btnAmp.ShowWindow( nShowControl );

    if( nShowControl == SW_SHOW )
        nShowControl = SW_HIDE;
    else
        nShowControl = SW_SHOW;
}

Defining and Setting Tab Order

New Term: When a dialog box is presented to the user, one control will have the keyboard focus, sometimes just called the focus. The control that has the focus receives all input from the keyboard. When a control has the focus, it has a dotted focus rectangle drawn around it.

A user can change the focus to a new control by pressing the Tab key on the keyboard. Each time the Tab key is pressed, a new control receives the focus. If you aren't familiar with how this works, you might want to experiment with a few dialog boxes from Developer Studio.

New Term: The controls are always selected in a fixed order, known as the tab order. Tab order lets users select controls without using the mouse. Although almost all Windows users have access to a mouse, using the keyboard sometimes is more convenient. Also, because tabbing between controls is a standard feature in Windows dialog boxes, you should use it correctly.


Time Saver: The tab order should follow a logical pattern through the dialog box. If the tab order follows a predictable pattern, users of the dialog box will find it much easier to navigate using the Tab key. Usually, the first editable control receives the focus when the dialog box is opened. After that, the focus should be passed to the next logical control in the dialog box. The buttons that control the dialog box--OK, Cancel, and Apply--should receive the focus last.

In a dialog box, the tab order follows the sequence in which controls were defined in the resource script. As new controls are added, they are placed at the end of the tab order. You can use the resource tools included in the Developer Studio to change this sequence, thereby altering the tab order.


Time Saver: To prevent a user from selecting a control using the Tab key, clear the Tab Stop property for the control.

With the dialog box displayed in the Developer Studio, select Tab Order from the Layout menu, or press Ctrl+D. Each control in the dialog box that has the tabstop attribute is tagged with a number, as shown in Figure 5.3.

Figure 5.3.
Displaying the tab order for dialog box controls.

To change the tab order, just click the control that should be in tab position 1; the tag associated with that control changes to reflect its new tab order. Repeat the process of clicking controls until the displayed tab order is correct.

Summary

In this hour you learned about the different types of button controls provided by Windows and used the controls in a variety of ways. You also built a dialog box-based project. Finally, you learned about control tab order and conditional expressions.

Q&A

Q What is the difference between BOOL and bool?

A The bool type is defined by the C++ standard, whereas the BOOL type is defined deep inside the Windows header files. In practice, they work very much alike, and you can interchange them without any problem. The reason for the BOOL type is historical; bool was only recently added to the C++ standard, and the BOOL type has been used for Windows programming for many years. In fact, BOOL was used for Windows programming before C++ was invented.

Q When is it more appropriate to hide a control instead of disabling it?

A If a control is unavailable or doesn't make sense for a temporary period, it should be disabled. If the control is unavailable for a long period of time, it should be hidden. In general, the user should be presented with as few options as possible, especially if those options cannot be selected.

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. What is the difference between the Cancel and Close buttons?

2. What is the difference between the OK and Apply buttons?

3. What MFC class is used to manage button controls?

4. What are the five types of button controls?

5. How do you prevent the Tab key from being used to select a control?

6. What function is used to disable a control at runtime?

7. What function is used to hide a control at runtime?

8. What is the default label used for in a switch statement?

9. What is the difference between the = and == operators?

10. What function is used to change the label on a button?

Exercises

1. Modify the Button project so that the Amplify check box is removed from the tab order.

2. Modify the Button project so that the source code from Listing 5.7 is used, except that the Amplify check box is hidden when the group box caption is set to Water Level.


Previous chapterNext chapterContents


Macmillan Computer Publishing USA

© Copyright, Macmillan Computer Publishing. All rights reserved.