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.
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.
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.
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.
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 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.
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;
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.
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.
#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++; }
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.
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.
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.
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.
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:
#include "testdlg.h"
void CMainFrame::OnViewTest() { CTestDlg dlg; dlg.DoModal(); }
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.
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:
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:
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.
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.
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.
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.
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.
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.
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.
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:
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.
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" );
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.
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.
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.