The Ex07a Example: The Dialog Box That Ate Cincinnati
We'll dive in headfirst here and build a dialog box that contains almost every kind of control. The job will be easy because Visual Studio's dialog editor will help us. The finished product is shown in Figure 7-1.

Figure 7-1: The finished dialog box in action.
As you can see, the dialog box supports a human resources application. The program is brightened a little by the use of Loyalty and Reliability scroll bar controls. Here is a classic example of direct action and visual representation of data! These are standard Windows controls we're looking at—we'll cover ActiveX controls in Chapter 9.
Building the Dialog Resource
Here are the steps for building the dialog resource:
Run the MFC Application Wizard to generate a project named Ex07a. Choose New Project from Visual Studio's File menu. In the New Project dialog box, select the MFC Application, type the name Ex07a, and click OK. In the MFC Application Wizard, accept all the defaults but two: On the Application Type page, select Single Document, and on the Advanced Features page, deselect Printing And Print Preview.
Create a new dialog resource with ID IDD_DIALOG1. Choose Add Resource from Visual Studio's Project menu. In the Add Resource dialog box, click Dialog and then click New. Visual Studio will create a new dialog resource and display it in the dialog editor, as shown here:

The dialog editor will assign the resource ID IDD_DIALOG1 to the new dialog box. Notice that the dialog editor inserts OK and Cancel buttons for the new dialog box.
Size the dialog box and set its properties. Enlarge the dialog box so that it is about 6 inches wide and 5 inches tall.Right-click on the new dialog box and choose Properties from the shortcut menu. The Properties window will appear (usually on the right side of the screen, depending upon your profile settings):

Add the dialog box's controls. Use the Toolbox to add each control. (If the Toolbox is not visible, choose Toolbox from the View menu.) Drag controls from the Toolbox to the new dialog box, and then position and size the controls, as shown in Figure 7-1. Here are the Toolbox controls:

Note | The dialog editor displays the position and size of each control in the lower right corner of the status bar. The position units are special "dialog units," or DLUs, not device units. A horizontal DLU is the average width of the dialog font divided by 4. A vertical DLU is the average height of the font divided by 8. The dialog font is normally 8-point MS Sans Serif. |
Here's a brief description of the dialog box's controls:
The static text control for the Name field. A static text control simply paints characters on the screen. No user interaction occurs at run time. You can type the text after you position the bounding rectangle (which sets the Caption property in the Properties window), and you can resize the rectangle as needed. Add a static text control for the Name field and set the Caption property to &Name. Follow the same procedure for the other static text controls in the dialog box. All static text controls have the same ID, but that's okay because the program doesn't need to access any of them.
Note | If you include an ampersand (&) in the Caption property for a static text control, at run time an underline will appear below the character that follows when the Alt key is pressed. This enables the user to jump to selected controls by holding down the Alt key and pressing the key corresponding to the underlined character. The related control must immediately follow the static text in the tabbing order. (I'll discuss tabbing order later in the chapter.) Thus, Alt+N jumps to the Name edit control and Alt+K jumps to the Skill combo box. (See Figure 7-1, shown earlier.) Needless to say, designated jump characters should be unique within the dialog box. The Skill control uses Alt+K because the SS Nbr control uses Alt+S. |
The Name edit control. An edit control is the primary means of entering text in a dialog box. Add a Name edit control and in the Properties window change this control's ID from IDC_EDIT1 to IDC_NAME. Leave the defaults for the rest of the properties. Notice that the default sets Auto HScroll to True, which means that the text scrolls horizontally when the box is filled.
The SS Nbr (social security number) edit control. The SS Nbr control is similar to the Name edit control. Simply change its ID to IDC_SSN. Later, you'll use the Add Member Variable Wizard to make this a numeric field.
The Bio (biography) edit control. This is a multi-line edit control. To make an edit control multi-line, set the Multiline property to True. Set Auto HScroll to False and change its ID to IDC_BIO.
The Category group box. This control serves only to group two radio buttons visually. Set the Caption property to &Category. The default ID is sufficient.
The Hourly and Salary radio buttons. Position these radio buttons inside the Category group box. For the Hourly radio button, set Caption to Hourly, Group to True, ID to IDC_CAT, and Tabstop to True. For the Salary radio button, set Caption to Salary and Tabstop to True.Be sure that both buttons have the Auto property set to True (the default) and that only the Hourly button has the Group property set to True. Setting the Group property to True indicates that the Hourly radio button is the first control in the Category group. When these properties are set correctly, Windows will ensure that only one of the two buttons can be selected at a time. The Category group box has no effect on the buttons' operation.
The Insurance group box. This control holds three check boxes. Set the Caption property to &Insurance and set the Group property to True.
Note | Later, when you set the dialog box's tab order, you can ensure that the Insurance group box follows the last radio button of the Category group. Setting the Group property to True will "terminate" the previous group. If you fail to do this, it isn't a serious problem, but you'll get several warning messages when you run the program through the debugger. |
The Life, Disability, and Medical check boxes. Place these controls inside the Insurance group box. Set the Caption properties to Life, Disability, and Medical and set the IDs to IDC_LIFE, IDC_DIS, and IDC_MED. Unlike radio buttons, check boxes are independent; the user can set any combination.
The Skill combo box. This is the first of three types of combo boxes. Change the ID to IDC_SKILL, and then set the Type property to Simple. Increase the height of the control to accommodate multiple lines. In the Data property, add the three skills Manager, Programmer, and Writer (separating each line with a semicolon).This is a combo box of type Simple. The user can type anything in the top edit control, use the mouse to select an item from the attached list box, or use the Up or Down direction key to select an item from the attached list box.
The Educ (education) combo box. Change the ID to IDC_EDUC and set the Sort property to False. In the Data property, add the three education levels High School, College, and Graduate (separating each line with a semicolon. In this drop-down combo box, the user can type anything in the edit box, click on the arrow, and then select an item from the drop-down list or use the Up or Down direction key to select an item from the attached list box.
Note | To set the size for the drop-down portion of a combo box, click on the box's arrow and drag down from the center of the bottom of the rectangle. |
The Dept (department) list box. Change the ID to IDC_DEPT; otherwise, leave the defaults. In this list box, the user can select only a single item by using the mouse, by using the Up or Down direction key, or by typing the first character of a selection. Note that the list box doesn't have a Data property, so you can't enter the initial choices. You'll see how to programmatically set these choices later in the chapter.
The Lang (language) combo box. Change the ID to IDC_LANG, and then set the Type property to Drop List. In the Data property, add the languages English, French, and Spanish (separating each line with a semicolon). With this drop-down combo box, the user can select only from the attached list box. To select, the user can click on the arrow and then select an entry from the drop-down list, or the user can type in the first letter of the selection and then refine the selection using the Up or Down direction key.
The Loyalty and Reliability horizontal scroll bars. Do not confuse scroll bar controls with a window's built-in scroll bars (as seen in scrolling views). A scroll bar control behaves in the same manner as other controls do and can be resized at design time. Position and size the horizontal scroll bar controls as shown earlier in Figure 7-1, and then assign the IDs IDC_LOYAL and IDC_RELY.
The OK, Cancel, and Special buttons. Add a button control below the existing OK and Cancel buttons. Set the Caption property to S&pecial and then set the ID IDC_SPECIAL. Later, you'll learn about special meanings that are associated with the default IDs IDOK and IDCANCEL.
Any icon. (The MFC icon is shown as an example.) You can use the Picture control to display any icon or bitmap in a dialog box, as long as the icon or bitmap is defined in the resource script. We'll use the program's MFC icon, identified as IDR_MAINFRAME. Set the Type option to Icon, and set the Image property to IDR_MAINFRAME. Leave the ID as IDC_STATIC.
Check the dialog box's tabbing order. Choose Tab Order from the Format menu. Use the mouse to set the tabbing order shown below. Click on each control in the order shown, and then press Enter.

Tip | If you mess up the tab sequence partway through, you can recover with a Ctrl+left mouse click on the last correctly sequenced control. Subsequent mouse clicks will start with the next sequence number. |
Save the resource file on disk. For safety, choose Save from the File menu or click the Save button on the toolbar to save

Creating the Dialog Class
You've now built a dialog resource, but you can't use it without a corresponding dialog class. (The section titled "Understanding the Ex07a Application" later in this chapter explains the relationship between the dialog box and the underlying classes.) Class View works in conjunction with the dialog editor to create that class. Here are the steps to create a dialog class:
Start the MFC Class Wizard. In Class View, select the Ex07a project, as shown here.

On the Project menu, choose Add Class (or right-click on the project name in Class View and choose Add, Add Class). In the Add Class dialog box, select the MFC Class template. Click Open in the Add Class dialog box. The MFC Class Wizard will appear.
Add the CEx07aDialog class. Create a CDialog-based class by filling in the fields of the MFC Class Wizard, as shown below. Be sure the Dialog ID drop-down list is set to IDD_DIALOG1 so the dialog resource you created earlier is used.

When you click Finish, the CEx07aDialog class will be added to Class View and its CPP file will be opened in the editor.
Add the CEx07aDialog member variables.After the CEx07aDialog class is added, you can add member variables using the Add Member Variable Wizard (shown below). To start the wizard, right-click on the CEx07aDialog class in Class View and choose Add, Add Variable.You must associate data members with each of the dialog box's controls. To do this, select the Control Variable check box, select a control from the Control ID drop-down list, and select Value from the Category drop-down list. Type a name in the Variable Name box and enter any other parameters. Here you can see the settings for adding the CString member variable named m_strBio for the Bio edit control:

When you're finished, click Finish and repeat this process for each of the controls listed in the table on the following page.
As you select controls in the Add Member Variables Wizard, you can set such things as the length of the string to enter or the range of numbers to enter. If you select a CString variable, you can set its maximum number of characters; if you select a numeric variable, you can set its minimum and maximum values. Set the minimum value for IDC_SSN to 0 and the maximum value to 999999999.Most relationships between control types and variable types are obvious. The way in which radio buttons correspond to variables is not so intuitive, however. You should associate an integer variable with each radio button group, with the first radio button corresponding to value 0, the second to 1, and so forth.
Control ID | Data Member | Type | Parameters |
---|---|---|---|
IDC_BIO | m_strBio | CString | Max chars = 1000 |
IDC_CAT | m_bCat | int | |
IDC_DEPT | m_strDept | CString | |
IDC_DIS | m_bInsDis | BOOL | |
IDC_EDUC | m_strEduc | CString | |
IDC_LANG | m_strLang | CString | |
IDC_LIFE | m_bInsLife | BOOL | |
IDC_LOYAL | m_nLoyal | int | |
IDC_MED | m_bInsMed | BOOL | |
IDC_NAME | m_strName | CString | |
IDC_RELY | m_nRely | int | |
IDC_SKILL | m_strSkill | CString | |
IDC_SSN | m_nSsn | int | Min value = 0 Max value = 999999999 |
Add the message-handling function for the Special button. CEx07aDialog doesn't need many message-handling functions because the CDialog base class, with the help of Windows, does most of the dialog management. When you specify the ID IDOK for the OK button, for example, the virtual CDialog function OnOK gets called when the user clicks the button. For other buttons, however, you need message handlers.With the CEx07aDialog class selected in Class View, click the Events button at the top of the Properties window to add event handlers. The Properties window should contain an entry for IDC_SPECIAL. Expand the IDC_SPECIAL tree and click the BN_CLICKED message. Click the down arrow for the BN_CLICKED message, as shown here:


void CEx07aDialog:: OnBnClickedSpecial ()
{
TRACE("CEx07aDialog::OnBnClickedSpecial\n");
}
Add an OnInitDialog message-handling function. As you'll see in a moment, Visual Studio generates code that initializes a dialog box's controls. This DDX (Dialog Data Exchange) code won't initialize the list-box choices, however, so you must override the CDialog::OnInitDialog function. Although OnInitDialog is a virtual member function, Visual Studio generates the prototype and skeleton if you map the WM_INITDIALOG message in the derived dialog class. With the CEx07aDialog class selected in Class View, click the Overrides button at the top of the Properties window. In the list of overrides, select the OnInitDialog function and click the down arrow to select the method.


BOOL CEx07aDialog::OnInitDialog()
{
// Be careful to call CDialog::OnInitDialog
// only once in this function
CListBox* pLB = (CListBox*) GetDlgItem(IDC_DEPT);
pLB->InsertString(-1, "Documentation");
pLB->InsertString(-1, "Accounting");
pLB->InsertString(-1, "Human Relations");
pLB->InsertString(-1, "Security");
// Call after initialization
return CDialog::OnInitDialog();
}
This code initializes the Dept list box with four values. You can also use the same initialization technique for the combo boxes in place of setting the Data property in the resource.
Connecting the Dialog Box to the View
Now we've got the resource and the code for a dialog box, but it's not connected to the view. In most applications, you would probably use a menu command to display a dialog box, but we haven't studied menus yet. Here we'll use the familiar mouse-click message WM_LBUTTONDOWN to display the dialog box. The steps are as follows:
Add the OnLButtonDown member function.You've done this in the examples in earlier chapters. Simply select the CEx07aView class in Class View, and at the top of the Properties window, click the Messages button to display the list of messages for CEx07aView. Select the WM_LBUTTONDOWN entry, click the down arrow, and select <Add> OnLButtonDown to add the OnLButtonDown member function to

Write the code for OnLButtonDown in file

void CEx07aView::OnLButtonDown(UINT nFlags, CPoint point)
{
CEx07aDialog dlg;
dlg.m_strName = "Shakespeare, Will";
dlg.m_nSsn = 307806636;
dlg.m_nCat = 1; // 0 = hourly, 1 = salary
dlg.m_strBio = "This person is not a well-motivated tech writer";
dlg.m_bInsLife = TRUE;
dlg.m_bInsDis = FALSE;
dlg.m_bInsMed = TRUE;
dlg.m_strDept = "Documentation";
dlg.m_strSkill = "Writer";
dlg.m_nLang = 0;
dlg.m_strEduc = "College";
dlg.m_nLoyal = dlg.m_nRely = 50;
int ret = dlg.DoModal();
TRACE("DoModal return = %d\n", ret);
TRACE("name = %s, ssn = %d, hourly = %d salary = %d\n",
dlg.m_strName, dlg.m_nSsn, dlg.m_nCat);
TRACE("dept = %s, skill = %s, lang = %d, educ = %s\n",
dlg.m_strDept, dlg.m_strSkill, dlg.m_nLang, dlg.m_strEduc);
TRACE("life = %d, dis = %d, med = %d, bio = %s\n",
dlg.m_bInsLife, dlg.m_bInsDis, dlg.m_bInsMed, dlg.m_strBio);
TRACE("loyalty = %d, reliability = %d\n",
dlg.m_nLoyal, dlg.m_nRely);
}
Add code to the virtual OnDraw function in the file

void CEx07aView::OnDraw(CDC* pDC)
{
CEx07aDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pDC->TextOut(0, 0, "Press the left mouse button here.");
}
Add the dialog class include statement to

#include "Ex07aDialog.h"
at the top of the CEx07aView class source code file (

#include "Ex07aView.h"
Build and test the application.If you've done everything correctly, you should be able to build and run the Ex07a application through Visual C++. Try entering data in each control, and then click OK and observe the TRACE results in the Output window. Notice that the scroll bar controls don't do much yet; we'll attend to them later. Notice what happens when you press Enter while typing in text data in a control: The dialog box closes immediately. Here's an example of the TRACE results in the Output window:

Understanding the Ex07a Application
When your program calls DoModal, control is returned to your program only when the user closes the dialog box. If you understand that, you understand modal dialog boxes. When you start creating modeless dialog boxes, you'll begin to appreciate the programming simplicity of modal dialog boxes. A lot happens "out of sight" as a result of that DoModal call, however. Here's a "what calls what" summary:
CDialog::DoModal
CEx07aDialog::OnInitDialog
…additional initialization… CDialog::OnInitDialog
CWnd::UpdateData(FALSE)
CEx07aDialog::DoDataExchange
user enters data…user clicks the OK buttonCEx07aDialog::OnOK
…additional validation…CDialog::OnOK
CWnd::UpdateData(TRUE)
CEx07aDialog::DoDataExchange
CDialog::EndDialog(IDOK)
OnInitDialog and DoDataExchange are virtual functions overridden in the CEx07aDialog class. Windows calls OnInitDialog as part of the dialog box initialization process, and that results in a call to DoDataExchange, a CWnd virtual function that was overridden by Visual Studio. Here is a listing of that function:
void CEx07aDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_BIO, m_strBio);
DDV_MaxChars(pDX, m_strBio, 1000);
DDX_Radio(pDX, IDC_CAT, m_nCat);
DDX_LBString(pDX, IDC_DEPT, m_strDept);
DDX_Check(pDX, IDC_DIS, m_bInsDis);
DDX_CBString(pDX, IDC_EDUC, m_strEduc);
DDX_CBIndex(pDX, IDC_LANG, m_nLang);
DDX_Check(pDX, IDC_LIFE, m_bInsLife);
DDX_Scroll(pDX, IDC_LOYAL, m_nLoyal);
DDX_Check(pDX, IDC_MED, m_bInsMed);
DDX_Text(pDX, IDC_NAME, m_strName);
DDX_Scroll(pDX, IDC_RELY, m_nRely);
DDX_CBString(pDX, IDC_SKILL, m_strSkill);
DDX_Text(pDX, IDC_SSN, m_nSsn);
DDV_MinMaxInt(pDX, m_nSsn, 0, 999999999);
}
The DoDataExchange function and the DDX_ (exchange) and DDV_ (validation) functions are "bidirectional." If UpdateData is called with a FALSE parameter, the functions transfer data from the data members to the dialog box controls. If the parameter is TRUE, the functions transfer data from the dialog box controls to the data members. DDX_Text is overloaded to accommodate a variety of data types.The EndDialog function is critical to the dialog box exit procedure. DoModal returns the parameter passed to EndDialog. IDOK accepts the dialog box's data, and IDCANCEL cancels the dialog box.
Tip | You can write your own "custom" DDX function and wire it into Visual C++. This feature is useful if you're using a unique data type throughout your application. For details, see the "TN026: DDX and DDV Routines" topic in the Visual Studio documentation. |