- Table of Contents
- Copyright
- About the Authors
- About the Contributors
- Acknowledgments
- Tell Us What You Think!
- Introduction
- How to Use This Book
- What You Need to Use This Book
- What's New in Visual C++ 6.0
- Contacting the Main Author
- Part I: Introduction
- Chapter 1. The Visual C++ 6.0 Environment
- Part II: MFC Programming
- Chapter 2. MFC Class Library Overview
- Chapter 3. MFC Message Handling Mechanism
- Chapter 4. The Document View Architecture
- Chapter 5. Creating and Using Dialog Boxes
- Handling Dialog Boxes in MFC
- Dialog Box Data Exchange and Validation
- Derived Control Classes in Dialog Boxes
- Modeless Dialog Boxes
- Summary
- Chapter 6. Working with Device Contexts and GDI Objects
- Chapter 7. Creating and Using Property Sheets
- Chapter 8. Working with the File System
- Chapter 9. Using Serialization with File and Archive Objects
- Part III: Internet Programming with MFC
- Chapter 10. MFC and the Internet Server API (ISAPI)
- Chapter 11. The WinInet API
- Chapter 12. MFC HTML Support
- Part IV: Advanced Programming Topics
- Chapter 13. Using the Standard C++ Library
- Chapter 14. Error Detection and Exception Handling Techniques
- Chapter 15. Debugging and Profiling Strategies
- Chapter 16. Multithreading
- Chapter 17. Using Scripting and Other Tools to Automate the Visual C++ IDE
- Part V: Database Programming
- Chapter 18. Creating Custom AppWizards
- Chapter 19. Database Overview
- Chapter 20. ODBC Programming
- Chapter 21. MFC Database Classes
- Chapter 22. Using OLE DB
- Chapter 23. Programming with ADO
- Part VI: MFC Support for COM and ActiveX
- Chapter 24. Overview of COM and Active Technologies
- Chapter 25. Active Documents
- Chapter 26. Active Containers
- Chapter 27. Active Servers
- Chapter 28. ActiveX Controls
- Part VII: Using the Active Template Library
- Chapter 29. ATL Architecture
- Chapter 30. Creating COM Objects Using ATL
- Chapter 31. Creating ActiveX Controls Using ATL
- Chapter 32. Using ATL to Create MTS and COM+ Components
- Part VIII: Finishing Touches
- Chapter 33. Adding Windows Help
- Part IX: Appendix
Handling Dialog Boxes in MFC
Dialog boxes in MFC usually are based on dialog box templates. These dialog box templates are binary resources held in the structure defined by the Win32 DLGTEMPLATE, DLGITEMTEMPLATE, DLGTEMPLATEEX, and DLGITEMTEMPLATEEX. You can fill these structures with positioning information, size, ID, and style flags required to describe every control on a dialog box.
If these structures were created tediously by hand for every dialog box, user interface software would progress at a snail's pace. Fortunately, the Visual Studio dialog box Template Editor is a powerful tool dedicated to this task.
After you create a dialog box template resource, you can create a CDialog-derived class that uses the template to create the dialog box and then manages the exchange between window controls and your dialog box's member variables.
Creating Dialog Box Template Resources
You can create dialog boxes and add controls by using the Resource Editor, as Figure 5.1 shows.
Figure 5.1 Editing a dialog box with Visual Studio's Dialog Box Editor.
After you create the dialog box, Visual Studio saves the details in a text format in your project's .rc file. These details provide the source code required for the Resource Compiler to generate the required DLGTEMPLATE and DLGITEMTEMPLATE binary structures.
You'll rarely need to edit the .rc file directly, because the Resource Editor should solely maintain it. It is interesting to note the code produced after editing a dialog box, however, such as this entry produced for the dialog box shown in Figure 5.1:
IDD_CUSTOM_DIALOG DIALOG DISCARDABLE 0, 0, 232, 135
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Custom Dialog"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "OK",IDOK,175,7,50,14
PUSHBUTTON "Cancel",IDCANCEL,175,24,50,14
LTEXT "Static Text",IDC_STATIC,7,23,44,8
EDITTEXT IDC_EDIT1,53,19,97,14,ES_AUTOHSCROLL
LISTBOX IDC_LIST1,7,64,147,64,LBS_SORT | LBS_NOINTEGRALHEIGHT |
WS_VSCROLL | WS_TABSTOP
CONTROL "DateTimePicker1",IDC_DATETIMEPICKER1,"SysDateTimePick32",
DTS_RIGHTALIGN | WS_TABSTOP,7,42,146,15
CONTROL "Tree1",IDC_TREE1,"SysTreeView32",WS_BORDER | WS_TABSTOP,
161,42,64,86
END
When you compile the resources, the compiler produces a file with a .res extension that holds the binary format of these dialog box templates (and the other resources). This .res file then is linked with the program code (.obj files) to produce your target .exe, .dll, or .lib file.
Creating a CDialog-Derived Class
You can use ClassWizard to generate a CDialog-derived class by invoking the wizard while the new dialog box is displayed in the Resource Editor. You could create the derived class by hand, but the wizard makes this process much easier.
ClassWizard detects that there is no class currently held in the .clw file that uses the ID of the new dialog box template resource. It then displays a dialog box to let you select an existing handler class or create a new one (see Figure 5.2).
Figure 5.2 ClassWizard prompts you to create or select a dialog box handler class.
Normally, you would create a new dialog box handler class, but you sometimes may want to select an existing implementation.
If you choose to create a new dialog box handler class, the wizard displays the New Class dialog box, as Figure 5.3 shows.
Figure 5.3 The New Class dialog box.
The New Class dialog box lets you change the name of the class. It then generates filenames based on the name you have specified. You can change these autogenerated filenames if they aren't appropriate.
The CDialog base class and your dialog box template ID are selected automatically. If your application is object linking and embedding (OLE)-enabled, you also can specify any OLE automation specific details for the new dialog box.
After you click OK in the New Class dialog box, you see the new dialog box handler class in the ClassView pane. If you examine the new header, you see a minefield of //{{AFX_... comments created by ClassWizard. When you subsequently add member variables and functions with ClassWizard, it adds the new code inside the appropriate comment sections.
You'll also notice that the dialog box ID is enumerated as the IDD member with a value based on your dialog box template's resource ID, such as this:
enum {IDD = IDD_CUSTOM_DIALOG };
If you examine the dialog constructor function, you'll see that it passes this ID down to the CDialog base class, along with an optional parent-window pointer:
CCustomDlg::CCustomDlg(CWnd* pParent /*=NULL*/)
: CDialog(CCustomDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CCustomDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}
The //{{AFX_DATA_INIT section in the constructor implementation is used by ClassWizard to initialize any member variables that you insert using the wizard.
The dialog box class definition also includes a declaration for the DoDataExchange() virtual function (covered in more detail later in the section, "The Data Exchange and Validation Mechanism"), and a message map declaration:
DECLARE_MESSAGE_MAP()
All classes that implement the DECLARE_MESSAGE_MAP macro must be derived from the CCmdTarget class. The dialog box class is derived from CCmdTarget through the CWnd class. This is because the dialog box is a window, and your CDialog-derived class must process messages from its own window and the various dialog box controls (which are child windows).
Displaying the Dialog Box
At this point, you can create an instance of the dialog box handler class and display the dialog box. Dialog boxes normally are displayed in a modal sense. (Modeless dialog boxes are discussed later in this chapter in "Modeless Dialog Boxes.") When a modal dialog box is displayed, users are unable to interact with any other part of the application until they click OK or Cancel to exit the dialog box.
The CDialog class's DoModal() function initiates the modal process and ends when the EndDialog() member function is called. The default implementation of CDialog calls EndDialog() in response to the user clicking the OK or Cancel button. You can use the integer value returned from DoModal() to find out which button was pressed. This integer actually is returned from the parameter passed to EndDialog(), which by default is IDOK or IDCANCEL in response to the OK or Cancel button.
The following lines show how an object of the CCustomDlg class is constructed and displayed, as well as how the return code determines which button was clicked to close the dialog box:
CCustomDlg dlgCustom;
if (dlgCustom.DoModal()==IDOK)
AfxMessageBox("User Hit OK");
else
AfxMessageBox("User Hit Cancel");
If you do want to use a dialog box template constructed in memory, you should construct a CDialog-derived class calling the constructor with no parameters. Then you can call the InitModalIndirect() function passing a pointer to a DLGTEMPLATE structure in memory, or an HGLOBAL handle to a global memory segment. If the initialization suc ceeds, InitModalIndirect() returns a TRUE value. You then can call DoModal() as normal to display the modal dialog box.
Dialog Box Coordinates
When you edit a dialog box in the Resource Editor, you'll notice that the coordinates don't correspond to the current screen resolution. Dialog boxes use their own coordinate system based on the size of the font being used in the dialog box. This method helps with the problem of matching the sizes of controls and group boxes to the various font sizes.
When the dialog box is displayed, the control positions and sizes are converted from dialog box units to real-screen units, depending on the size of the font used. Most dialog boxes use the default system font, but you can set different fonts when editing the dialog box, from within your code by calling the CWnd base class's SetFont() function, or by overriding the CDialog class's OnSetFont() function and supplying a pointer to your required dialog box font.
These real-screen units are calculated from the average height and width of the current dialog box font. The real-screen units are one-eighth of the height and one-fourth of the width of the dialog box units.
You can convert between these dialog box units and the real-screen coordinates by passing a RECT structure holding the dialog box coordinates to the MapDialogRect()function. MapDialogRect() then translates these dialog box coordinates into screen coordinates.
Changing the Input Focus and Default Buttons
When you edit a dialog box template in the Resource Editor, you can set the tab order by pressing Ctrl+D. This action displays sequence numbers above the controls and lets you change the order by clicking on various controls in sequence.
Whenever the user presses the Tab button while the dialog box is displayed, the input focus moves to the next control in this tab order.
A set of member CDialog functions lets you change the current input focus from within your code based on this tab order.
You can set the focus to a specific control after the dialog box is displayed by calling GotoDlgCtrl() and passing a pointer to the control's window. You can find this pointer by ID by calling GetDlgItem(). To set the input focus to the OK button, for example, you could call the following from within your dialog box class (after activation):
GotoDlgCtrl(GetDlgItem(IDOK));
The NextDlgCtrl() and PrevDlgCtrl() functions let you move forward and backward through the tab order, setting the input focus to the next or preceding control from the current control with input focus.
Normally the OK button is the default button, so when a user presses the Enter key, the OK button is clicked. You can change this behavior by passing a different button ID to SetDefID(). The alternative button then becomes the default clicked when the user presses Enter. The corresponding GetDefID() returns the ID of the current default button.
If you are using context-sensitive help, you can set the context help ID for a dialog box by calling SetHelpID() and passing the ID that corresponds to the documentation for that dialog box.
Dialog Box Data Exchange and Validation | Next Section