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
Using the different types of button controls provided by Windows
Using the MFC CButton class that is used to manage button controls
Using the MFC CWnd class to enable and disable controls
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.
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.
As Listing 5.5 shows, the switch statement has several different
parts. Here are the major features of a switch statement:
The switch() expression. The expression contained inside the switch
parentheses is evaluated, and its value is used as the basis for making
the selection.
One or more case labels. Each case label includes a value.
Every case label must be unique. If a case label's value
matches the switch expression, the statements after the case
label are executed.
One or more break statements. The break statement is
used to stop execution inside a switch statement. A break
statement is normally placed between every case. If a break
statement is removed, statements in the next case are executed
until a break is reached, or until no more statements remain inside
the switch.
A default label. The default label is selected when no case labels
match the switch expression.
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:
Pushbuttons have a raised, three-dimensional appearance and seem
to be depressed as they are clicked with the mouse. Pushbuttons normally
have a text label on the face of the control.
Radio buttons consist of a round button with a label adjacent to
it.
Check boxes are made up of a square box that contains a check mark
when selected and a label next to the control.
Owner-drawn buttons are painted by the button's owner instead of
by Windows.
Group boxes are simply rectangles that are used to surround other
controls that have a common purpose.
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:
Drag a button control from the palette to the dialog box by pressing the
left mouse button while over the control button, then dragging the mouse
cursor to the dialog box with the left mouse button still pressed. Release
the mouse button when the cursor is over the desired spot in the dialog
box.
Select a button control by clicking a control in the control palette. Click
the desired location for the control in the dialog box, and the dialog
box editor creates a control for you in that location.
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:
ID: Used for the button's resource ID. A default resource ID, such as IDC_BUTTON1,
is supplied by Developer Studio. Using IDC_ as a prefix for control
resource IDs is a Microsoft naming convention.
Caption: Indicates the text that appears as the button's label. Developer
Studio supplies a default name, such as Button. To make one of the letters
in the caption of a control the mnemonic key, precede it with an ampersand
(&).
Visible: Indicates that the button is initially visible. This check box
is normally checked.
Disabled: Indicates that the button should be initially disabled. This
check box is normally cleared.
Group: Marks the first control in a group. All controls following a control
with this attribute are considered part of the same group if this check
box is cleared. A user can move between controls in the same group using
the arrow keys.
Tab Stop: Indicates that this control can be reached by pressing Tab on
the keyboard. This check box is normally checked.
Default Button: Marks this control as the dialog box's default button.
There can be only one default button in a dialog box, and it is executed
if the user presses Enter without using any other controls in the dialog
box. This check box is normally cleared.
Owner Draw: Indicates that the button will be drawn by the button's owner;
in this case, the dialog box. In most cases, this check box is cleared.
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:
Auto: Automatically changes the state of the control when it is selected.
This check box is normally selected.
Left Text: Places the control's label on the left side of the check box
instead of the right. This check box is normally cleared.
Check boxes support the same properties as radio controls, except that
they are used with one additional attribute:
Tri-state: The check box can have three states instead of two. In addition
to true and false, the control can be disabled, meaning
that the value is neither true nor false.
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:
OK: Used to close and accept any information that is present in the dialog
box. Any user-supplied information in the dialog box is used by the program.
Note that the OK pushbutton is the only button spelled with all capital
letters.
Cancel: Used to close the dialog box and remove any changes that might
have been performed while the dialog box was open. If there are changes
that cannot be reversed, the label for this button should be changed to
read Close. Changing the label for a button is discussed later in this
hour.
Close: Used to close the dialog box. It does not necessarily imply that
any action is taken by the program. Close is most often used when a Cancel
button cannot be used to remove changes made while the dialog box is open.
Many programs change a Cancel button into a Close button.
Help: Used to request context-sensitive help for the open dialog box.
Apply: Used to perform changes based on data that has been entered in the
dialog box. Unlike the OK button, the dialog box should remain open after
the Apply button is pressed.
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.
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.
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.
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.
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.
Figure5.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.