Teach Yourself Visual C++® 5 in 24 Hours

Previous chapterNext chapterContents


- Hour 6 -
Using Edit Controls

In Windows programs, user input is often collected using edit controls. In this hour you will also learn about

You will also create an SDI project and use it to show how data is transferred in and out of edit controls used in dialog boxes.

Identifier Scope and Lifetime

New Term: The scope of an identifier refers to its visibility in a C++ program.

Every identifier used to name a variable or function has a specific scope when it is created, and this scope determines how and where that name can be used. If a variable is "in scope" at a certain point in a program, it is visible and can be used in most circumstances. If it is "out of scope," it is not visible, and your program will not be capable of using that variable.

One simple type of scope is shown in the following code sample. The following code is not legal because the variable myAge is used before it is declared:

myAge = 12;

int myAge;

Because the identifier myAge is not in the current scope, it cannot be assigned a value.


Just a Minute: The preceding example illustrates one simple property about visibility: it almost always runs "downward," beginning at the point where the variable is declared. There are also several different types of scope, ranging from very small to very large.


Time Saver: In general, your program should use variables that have as small a scope as possible. The smaller the scope, the less chance that an identifier will be accidentally misused or subjected to side effects. For example, passing objects as parameters to a function is always better than relying on global variables. Using variables that are local to the function helps make the function more reusable.

Using Different Types of Scope

The scope of an identifier comes into play whenever an identifier is declared. The most commonly used types of scope available in a C++ program are

Each of these types of scope is discussed in the following sections.

Local Scope

The simplest example of local scope is a variable or other object that is declared outside any functions, like this:

int foo;

int main()

{

    return 0;

}

In this example, the variable foo is in scope from the point of its declaration to the end of the source file. For this reason, this type of local scope is sometimes called file scope. All declarations that occur outside class or function definitions have this type of scope.

Variables declared inside a function body have local scope and are visible only within the function.

Another type of local scope is block scope, where a variable within a compound statement or other block is visible until the end of the block, as shown in Listing 6.1.

TYPE: Listing 6.1. An example of local block scope.

if( bPrinting == true)

{

    int nMyAge = 42;

    cout << "My age is " << nMyAge << endl;

}

// nMyAge is not in scope here.

The variable nMyAge has block scope and can be used only between the curly braces.

Function Scope

Function scope is rarely an issue. Function scope applies to labels declared inside a function definition. The only time you would use a label is with the widely discouraged goto statement.

None of the labels declared in a function are visible outside the function. This means that the C++ language does not directly support jumping to a label outside the current function. It also means that you can reuse labels in different functions.

Class Scope

All identifiers used in a class, union, or structure are tightly associated with the class and have class scope. An identifier with class scope can be used anywhere within the class, union, or structure.

If a class or variable name is used to qualify access to the identifier, it also is visible outside the class. For example, if a class is defined as follows, the variables m_myVar and m_myStaticVar are in scope for all the CFoo member functions:

// class CFoo

class CFoo

{

public:

    CFoo();



    int     GetMyVar();

    int     GetStaticVar();



    int        m_myVar;

    static int m_myStaticVar;

};

int CFoo::m_myStaticVar;

Outside the CFoo class, the variables can be accessed only through a CFoo object, like this:

aFoo.m_myVar = 42;

There is one exception to the rule that requires a member to be accessed with a variable name: A class member declared as static is shared by all objects of that class. Static members of a class exist even when no objects of a class have been created. To access a static class member without using a class object, prefix the class name to the member name, like this:

CFoo::m_myStaticVar = 1;

Understanding Identifier Lifetime

In a C++ program, every variable or object has a specific lifetime, which is separate from its visibility. It is possible for you to determine when a variable is created and when it is destroyed.


Just a Minute: Lifetime can be an important issue when you design your program. Large objects can be costly to create and destroy. By understanding the lifetime of objects created in your programs, you can take advantage of features in the C++ language that help your programs run more efficiently.

Static Lifetime

A variable declared as static in a function is created when the program starts and is not destroyed until the program ends. This is useful when you want the variable or object to remember its value between function calls. Listing 6.2 is an example of a static object in a function.

TYPE: Listing 6.2. A static object in a function, destroyed when the program ends.

#include <iostream>

using namespace std;



void PrintMessage();

int main()

{

    for( int nMessage = 0; nMessage < 10; nMessage++ )

        PrintMessage();

    return 0;

}



void PrintMessage()

{

    static int nLines = 1;

    cout << "I have printed " << nLines << " lines." << endl;

    nLines++;

}

Understanding Edit Controls

New Term: An edit control is a window used to store free-form text input by a user.

New Term: A single-line edit control is an edit control that enables a single line of text to be entered.

New Term: A multiple-line edit control, sometimes called an MLE, is an edit control that enables multiple lines of text to be entered.

Edit controls are usually found in dialog boxes. Almost anywhere user input is required, you can usually find an edit control.

Why Use an Edit Control?

Single-line edit controls are used when text must be collected. For example, when a name or address must be entered in a dialog box, an edit control is used to collect that information. Multiline edit controls often use scrollbars that enable more text to be entered than can be displayed.

A prompt in the form of default text can be provided for an edit control. In some situations, this can reduce the amount of typing required by a user. All edit controls also support a limited amount of editing, without any need for extra programming on your part. For example, the standard cut-and-paste commands work as expected in an edit control. Table 6.1 lists the editing commands available in an edit control.

Table 6.1. Editing commands available in an edit control.

Command Keystroke
Cut Control+X
Paste Control+V
Copy Control+C
Undo Control+Z


Just a Minute: Because of the built-in editing capabilities of the edit control, it's possible to create a simple text editor using a multiple-line edit control. Although an MLE cannot replace a real text editor, it does provide a simple way to collect multiple lines of text from a user.

One difference between edit controls and the pushbutton controls you saw in Hour 5, "Button Controls," is that a button control is normally used to generate events. An edit control can generate events also, but it most often is used to actually store data.

MFC Support for Edit Controls

You normally add edit controls to a dialog box just as you added buttons in Hour 5. After you add the control to a dialog box, use ClassWizard to configure the control for use in the program.

The MFC class CEdit is often used to interact with edit controls. As you will see in the next section, you can use ClassWizard to associate an edit control with a specific CEdit object. An edit control can also be associated with a CString object, which can simplify the use of edit controls in dialog boxes. You will learn about using edit controls associated with CString objects in detail beginning with the section "Passing Parameters to Dialog Boxes Using DDV and DDX Routines," later in this hour.


Just a Minute: Of course, edit controls can be used in dialog box-based programs, which were discussed in Hour 5. However, in this hour an SDI program is used to show off some of the data exchange and validation features often used with edit controls.

Building an SDI Test Project

Some of the sample programs in this book require you to build an SDI project and add a test dialog box. You can use the following steps to build a test project that includes a test dialog box:

1. Create an SDI project named EditTest using MFC AppWizard, as discussed in Hour 1, "Introducing Visual C++ 5." Feel free to add or remove any of the optional features suggested by AppWizard, because they aren't used in this hour.

2. As discussed in Hour 4, "Dialog Boxes and C++ Classes," add a dialog box resource to the program. Name the dialog box IDD_TEST, and set the caption to Test Dialog. Using ClassWizard, create a dialog box class called CTestDlg for the new dialog box.

3. Add a menu choice named ID_VIEW_TEST, with a caption of Test... that brings up the Edit dialog box by adding a new menu choice on the View menu. Add a message-handling function for the new menu item using ClassWizard. The steps required to add a message-handling function that uses a CDialog-based object were discussed in Hour 4. Use the source code provided in Listing 6.3 for the CMainFrame message-handling function.

4. Include the class declaration for CTestDlg in the MainFrm.cpp file by adding the following line after all the #include directives in MainFrm.cpp:
#include "testdlg.h"
5. Add a pushbutton control, IDC_TEST, labeled Test, to the dialog box, as was done in Hour 5. Using ClassWizard, add a function that handles the BN_CLICKED message, which will be used in later examples.

6. After following these steps, make sure that the project compiles properly by pressing the Build icon on the toolbar or by selecting Build|Build EditTest.exe from the main menu. Try the menu item to make sure the IDC_TEST dialog box is displayed when View|Test... is selected.

TYPE: Listing 6.3. Handling a menu-item selection for EditTest.

void CMainFrame::OnViewTest()

{

    CTestDlg    dlg;



    dlg.DoModal();

}

Adding an Edit Control to a Dialog Box

You add an edit control to a dialog box just as you added a button control in Hour 5, using either of these two basic methods:

Arrange the edit control so that the dialog box resembles the one in Figure 6.1.

Figure 6.1.
The dialog box used in the edit control examples.

In Figure 6.1, a static text control is located immediately to the left of the edit control. Edit controls are usually labeled with static text so a user can determine the type of input needed. Static text controls were discussed in Hour 4. The ID for the new edit control is set by default to IDC_EDIT1 or a similar name. Change the ID to IDC_EDIT_TEST, leaving the other properties set to their default values.

Edit Control Properties

You can examine the properties for an edit control, just as with other resources, by right-clicking over the control and selecting Properties from the pop-up menu. These general properties are available for an edit control:

There also is a group of properties that apply specifically to edit controls. The following properties are displayed by clicking the Styles tab in the Properties dialog box:

Binding a CEdit Object to an Edit Control

As discussed earlier, one way to interact with an edit control is through a CEdit object that is attached to the control. To attach a CEdit object to an edit control, you use ClassWizard much as you did for button controls in the previous hour:

1. Open ClassWizard.

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

3. Select the tab labeled Member Variables.

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

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 6.2.

Table 6.2. Values used to add a CEdit member variable for CTestDlg.

Control ID Variable Name Category Type
IDC_EDIT_TEST m_editTest Control CEdit

The default value displayed in the Category control is Value. The Value category is used for some member variables later this hour, when you learn about DDV and DDX routines.

Collecting Entered Text from an Edit Control

The primary reason for using an edit control, of course, is to collect information from a user. To do that, you must get the information from the edit control. Using the CEdit class simplifies this process.

Using CEdit Member Functions to Retrieve Text

Several CEdit member functions are useful when collecting information from an edit control, such as the GetWindowText and LineLength member functions. As an example, add the source code in Listing 6.4 to the CTestDlg::OnTest member function, created earlier.

TYPE: Listing 6.4. Collecting input from an edit control using CEdit.

void CTestDlg::OnTest()

{

    CString szEdit;

    CString szResult;



    int nLength = m_editTest.LineLength();

    m_editTest.GetWindowText( szEdit );

    szResult.Format( "%s has %d chars", szEdit, nLength );



    AfxMessageBox( szResult );

}

When the Test button is clicked, the text entered in the edit control is retrieved by using the m_editTest object. Normally, you are interested only in data contained in an edit control if OK is clicked. If the Cancel button is clicked, the dialog box should be closed and, usually, any entered information is simply discarded.

Passing Parameters to Dialog Boxes Using DDV and DDX Routines

The DDV and DDX routines are helper functions that help manage data for dialog boxes. DDV, or Dialog Data Validation, routines are used for data validation. DDX, or Dialog Data Exchange, routines are used to exchange data to and from the controls in a dialog box.


Time Saver: Although you can use the DDV and DDX routines in your dialog boxes directly, ClassWizard adds the code for you at the click of a button. Normally, you add the DDV and DDX routines with ClassWizard instead of trying to hand-code the necessary function calls.

Why Are DDV and DDX Routines Used?

The DDV routines are useful when collecting data from an edit control. In general, you have little control over how a user enters data in an edit control. A DDV enables you to perform some simple validation based on range or string length.


Just a Minute: For example, if an edit control is used to collect an abbreviated state name, you want to limit the entered text to two characters. Using a DDV routine, it's easy to make sure that two characters have been entered.

DDX functions link member variables from the dialog box class to controls that are contained in the dialog box. DDX routines enable data to be transferred to and from the controls much easier than is otherwise possible. As discussed in Hour 4, a dialog box is normally used something like this:

CMyDialog    dlgMine;

dlgMine.DoModal();

In this example, the dialog box is created when DoModal is called, and the function does not return until the user closes the dialog box. This presents a problem if data must be passed to or from the dialog box. Because none of the controls exist until the dialog box is created, using SetWindowText, GetWindowText, or other functions to interact directly with controls contained in the dialog box is not possible. After the dialog box has been dismissed, it is too late to use those functions to collect user input.

When DDX routines are used to exchange information with a dialog box, the dialog box can be used like this:

CMyDialog    dlgMine;

dlgMine.m_szTest = "Hello World";

dlgMine.DoModal();

The DDX routines enable you to have access to the dialog box's controls before and after the dialog box has been created. This simplifies dialog box programming because it is a much more flexible method than adding code in the InitDialog member function.

How Are DDV and DDX Routines Used?

The easiest and most useful way to add DDV and DDX routines to your dialog box class is by using ClassWizard. Member variables associated with dialog box controls by value automatically use the DDV and DDX routines provided by MFC. For example, CString member variables are often associated with edit controls. ClassWizard adds source code to handle the exchange and validation of data in two places:

DoDataExchange is a virtual function that is called to move data between the control and the dialog box's member data. DoDataExchange takes a single parameter, either TRUE or FALSE, with TRUE as the default parameter. When DoDataExchange( FALSE ) is called, data is moved from the member variable to the control. When DoDataExchange( TRUE ) is called, data is copied from the control to the member variable.

When the dialog box is initially displayed during CDialog::OnInitDialog, UpdateData(FALSE) is called to transfer data from the member variables to the dialog box's controls. Later, during CDialog::OnOk, UpdateData() is called to transfer data from the dialog box's controls to member variables.

As shown in Figure 6.2, DoDataExchange has a single parameter that controls the direction that data, in this case m_szTest, is copied.

Figure 6.2.
DDV and DDX routines used to handle dialog box data.

Associating a Control's Value with a Member Variable

You add member variables that are associated with a control's value almost exactly the way you added control-type variables earlier in this hour. For example, to create a member variable associated with the IDD_EDIT_TEST edit control, follow these steps:

1. Open ClassWizard.

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

3. Select the Member Variables tab.

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

5. Click the Add Variable button. 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 6.3.

Table 6.3. Values used to associate a CString member variable with an edit control.

Control ID Variable Name Category Type
IDC_EDIT_TEST m_szTest Value CString

The preceding steps are exactly like the steps used to add a control-type variable earlier in this hour, except that the control type is set to Value. A member variable associated by value with an edit control can also be an int, UINT, long, DWORD, float, double, or BYTE, although it is most commonly a CString.

After closing the Add Member Variable dialog box, ClassWizard displays an edit control that you can use to specify the type of validation to be performed on the member variable. If a CString object is associated with an edit control, the maximum string length can be specified. If a numeric variable is used, the allowed range can be defined.

Exchanging Edit-Control Information Using DDX Functions

The member variables associated with dialog box controls by ClassWizard are added to the dialog box class as public variables. This allows the member variables to be easily accessed and used. For example, to use the m_szTest variable that was added in the previous section, edit the CMainFrame::OnViewTest member function so it looks like the function in Listing 6.5. Before compiling the project, remove the following line, which was added to CTestDlg::OnInitDialog earlier:

m_editTest.SetWindowText( "Default" );

TYPE: Listing 6.5. Using member variables to exchange information with an edit control.

void CMainFrame::OnViewTest()

{

    CTestDlg    dlg;



    dlg.m_szTest = "DDX Test";



    if( dlg.DoModal() == IDOK )

    {

        AfxMessageBox( dlg.m_szTest );

    }

    else

    {

        AfxMessageBox( "Dialog cancelled" );

    }

}

Listing 6.5 sets the value of m_szTest before the dialog box is displayed to the user. CDialog::OnInitDialog calls the CWnd::UpdateData function, which calls UpdateData. Because UpdateData is a virtual function, the proper version of the function is called--the version that is part of the CDialog-derived class that handles the dialog box.

After the dialog box closes, the CMainFrame::OnViewTest function checks the return value of DoModal. If IDOK was returned, the dialog box was closed using the OK button, and the value of m_szTest is displayed.

Summary

In this hour, you learned about identifier scope and lifetime. You also learned about the Windows edit control and how it is usually used in a dialog box. You saw how to associate an edit control with a CEdit object using ClassWizard and used data exchange and validation to pass parameters to and from dialog boxes.

Q&A

Q I would like to use the DDV routines for all my dialog box data, but what if I have a complex data type, such as a credit card number? How can I use the DDV DDX mechanism?

A You have two options. You can perform the validation yourself when accepting user input by testing for valid data before the user is allowed to close the dialog box. You can also write your own custom DDV routine. Technical Note 26 describes how to write such a routine. You can find this technical note by searching for TN026 in the Developer Studio online help.

Q Can I call UpdateData at any time? I would like to implement an Undo feature in my dialog box.

A Normally, UpdateData is called when the dialog box is initialized and when the user closes the dialog box by clicking OK. However, if you must call UpdateData at other times, it's perfectly okay.

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 MFC class is used to manage edit controls?

2. What is the difference between an MLE and an SLE?

3. How are DDV and DDX routines used?

4. What member function do you call to transfer data to and from your dialog box controls?

5. What function is used to retrieve text from an edit control?

6. What function is used to set text in an edit control?

7. What property is used to hide user input in an edit control by replacing it with asterisks?

8. What keystroke is used to paste text into an edit control?

9. What keystroke is used to copy text from an edit control?

10. What keystroke is used to cut text from an edit control?

Exercises

1. Set the maximum length for the text entered in the edit control in the Test project to five characters.

2. Change the Test project so that the edit control is used to store an integer value instead of a string.


Previous chapterNext chapterContents


Macmillan Computer Publishing USA

© Copyright, Macmillan Computer Publishing. All rights reserved.