Dialog windows typically have one or more controls and some text explaining what information the program needs from you. Dialog windows typically do not have a large blank work area, as you find in the main windows of a word processor or a programming editor. All the applications that you have built in the preceding days have been dialog windows, and your projects will continue to be dialog windows for the next few days.
All the dialogs that you have created up to now have been single window dialog applications. Today you are going to learn
The Windows operating system provides a number of pre-existing dialog windows. Simple dialog windows, also known as message boxes, present the user with a message and provide one to three buttons to click. More complex dialogs, such as the File Open, Save, or Print dialogs, are also provided with Windows. These system (or common) dialogs are created and used with a combination of a variable declaration of a C++ class and a series of interactions with the class instance.
As you learned in the previous days, using message boxes is as simple as making a single function call, passing the message text as the only argument. This results in a message box that displays the message to the user with an icon and gives the user one button to click to acknowledge the message. As you probably know from using other Windows software, you have a whole range of other message box possibilities with various button combinations and various icons that can be displayed.
As you have seen in previous days, the MessageBox function can be passed one or two arguments. The first argument is the message to be displayed to the user. The second argument, which is completely optional, is displayed in the title bar on the message box. You can use a third argument, which is also optional, to specify the buttons to be presented to the user and the icon to be displayed beside the message. In addition to this third argument, the MessageBox function returns a result value that indicates which button was clicked by the user. Through the combination of the third argument and the return value, the MessageBox function can provide a whole range of functionality in your Visual C++ applications.
NOTE: If you use the third argument to the MessageBox function to specify the buttons or the icon to be presented to the user, the second argument (the message box title) is no longer optional. You must provide a value for the title bar of the message box.
The button combinations that you can use in the MessageBox function are limited. You do not have the freedom to make up your own button combination. If you get to the point where you need to make up your own, you have to create a custom dialog window that looks like a message box. The button combinations that you can use are listed in Table 5.1.
ID | Buttons |
MB_ABORTRETRYIGNORE | Abort, Retry, Ignore |
MB_OK | OK |
MB_OKCANCEL | OK, Cancel |
MB_RETRYCANCEL | Retry, Cancel |
MB_YESNO | Yes, No |
MB_YESNOCANCEL | Yes, No, Cancel |
To specify the icon to be displayed, you can add the icon ID to the button combination ID. The icons that are available are listed in Table 5.2. If you want to specify either the icon or the button combination, and you want to use the default for the other, you can just specify the one ID that you want to use.
ID | Icon |
MB_ICONINFORMATION | Informational icon |
MB_ICONQUESTION | Question mark icon |
MB_ICONSTOP | Stop sign icon |
MB_ICONEXCLAMATION | Exclamation mark icon |
When you do specify a button combination, you want to capture the return value so that you can determine which button the user clicked. The return value is defined as an integer data type; the return value IDs are listed in Table 5.3.
ID | Button Clicked |
IDABORT | Abort |
IDRETRY | Retry |
IDIGNORE | Ignore |
IDYES | Yes |
IDNO | No |
IDOK | OK |
IDCANCEL |
Cancel |
To get a good understanding of how you can use the MessageBox function in your applications to get information from the user, you will build a simple application that uses the MessageBox function in a couple of different ways. Your application will have two separate buttons that call two different versions of the MessageBox function so that you can see the differences and similarities between the various options of the function. Later in the day, you will add a standard File Open dialog so that you can see how the standard dialogs can be used to allow the user to specify a filename or perform other standard functions. Finally, you will create a custom dialog that allows the user to enter a few different types of values, and you will see how you can read these values from the main application dialog after the user has closed the custom dialog.
To start this application, follow these steps:
Object | Property | Setting |
Command Button | ID | IDC_YESNOCANCEL |
|
Caption | &Yes, No, Cancel |
Command Button | ID | IDC_ABORTRETRYIGNORE |
|
Caption | &Abort, Retry, Ignore |
Command Button | ID | IDC_FILEOPEN |
|
Caption | &File Open |
Command Button | ID | IDC_BCUSTOMDIALOG |
|
Caption | &Custom Dialog |
Command Button | ID | IDC_BWHICHOPTION |
|
Caption | &Which Option? |
|
Disabled | Checked |
Command Button | ID | IDC_EXIT |
|
Caption | E&xit |
Static Text | ID | IDC_STATIC |
|
Caption | Dialog Results: |
Edit Box | ID | IDC_RESULTS |
|
Multiline | Checked |
|
Auto Vscroll | Checked |
FIGURE 5.1. The application main dialog layout.
Object | Name | Category | Type |
IDC_RESULTS | m_sResults | Value | CString |
IDC_BWHICHOPTION | m_cWhichOption | Control | CButton |
For the first command button (the Yes, No, Cancel button), create a function on the clicked event using the Class Wizard, just as you did on previous days. Edit the function on this button, adding the code in Listing 5.1.
1: void CDialogsDlg::OnYesnocancel() 2: { 3: // TODO: Add your control notification handler code here 4: 5: /////////////////////// 6: // MY CODE STARTS HERE 7: /////////////////////// 8: 9: int iResults; // This variable will capture the button selection 10: 11: // Ask the user 12: iResults = MessageBox("Press the Yes, No, or Cancel button", 13: "Yes, No, Cancel Dialog", 14: MB_YESNOCANCEL | MB_ICONINFORMATION); 15: 16: // Determine which button the user clicked 17: // Give the user a message showing which button was clicked 18: switch (iResults) 19: { 20: case IDYES: // The Yes button? 21: m_sResults = "Yes! Yes! Yes!"; 22: break; 23: case IDNO: // The No button? 24: m_sResults = "No, no, no, no, no."; 25: break; 26: case IDCANCEL: // The Cancel button? 27: m_sResults = "Sorry, canceled."; 28: break; 29: } 30: 31: // Update the dialog 32: UpdateData(FALSE); 33: 34: /////////////////////// 35: // MY CODE ENDS HERE 36: ///////////////////////
37: }
If you compile and run your application, you can see how selecting the different buttons on the message box can determine the next course of action in your application. If you add a function to the clicked event of the Abort, Retry, Ignore button using the Class Wizard and enter the same code as in Listing 5.1, substituting the MB_ABORTRETRYIGNORE and MB_ICONQUESTION values and changing the prompts and messages, you can see how this other button combination can be used in the same way.
Both of these control event functions are virtually the same. In each function, there is an integer variable declared to capture the return value from the MessageBox function. Next, the MessageBox function is called with a message to be displayed to the user, a title for the message box, and a combination of a button combination ID and an icon ID.
When the return value is captured from the MessageBox function, that value is passed through a switch statement to determine which value was returned. A message is displayed to the user to indicate which button was clicked on the message box. You can just as easily use one or two if statements to control the program execution based on the user's selection, but the return value being an integer lends itself to using a switch statement.
If you compile and run your application at this point, you can click either of the top two buttons and see a message box, as in Figure 5.2. When you click one of the message box buttons, you see a message in the edit box on the main dialog, indicating which button you selected, as in Figure 5.3.
FIGURE 5.2. The MessageBox with three choices.
FIGURE 5.3. A message is displayed based on which button was clicked.
Using common dialogs is not quite as simple and easy as using the MessageBox function, but it's still quite easy. The Microsoft Foundation Classes (MFC) provides several C++ classes for common Windows dialogs. These classes are listed in Table 5.6.
Class | Dialog Type |
CFileDialog | File selection |
CFontDialog | Font selection |
CColorDialog | Color selection |
CPageSetupDialog | Page setup for printing |
CPrintDialog | Printing |
CFindReplaceDialog | Find and Replace |
The common dialogs encapsulated in these classes are the standard dialogs that you use every day in most Windows applications to open and save files, configure printing options, print, perform find and replace on documents, and so on. In addition to these choices, a series of OLE common dialog classes provide several common functions to OLE or ActiveX components and applications.
All these dialogs are used in the same manner, although the individual properties and class functions vary according to the dialog functionality. To use one of these dialogs, you must follow these steps:
To better understand how this works, you'll add the CFileDialog class to your application. To do this, add a function to the clicked message on the File Open button using the Class Wizard. Edit this function, adding the code in Listing 5.2.
1: void CDialogsDlg::OnFileopen() 2: { 3: // TODO: Add your control notification handler code here 4: 5: /////////////////////// 6: // MY CODE STARTS HERE 7: /////////////////////// 8: 9: CFileDialog m_ldFile(TRUE); 10: 11: // Show the File open dialog and capture the result 12: if (m_ldFile.DoModal() == IDOK) 13: { 14: // Get the filename selected 15: m_sResults = m_ldFile.GetFileName(); 16: // Update the dialog 17: UpdateData(FALSE); 18: } 19: 20: /////////////////////// 21: // MY CODE ENDS HERE 22: ///////////////////////
23: }
In this code, the first thing that you do is declare an instance of the CFileDialog class. This instance is passed TRUE as an argument to the class constructor. This tells the class that it is a File Open dialog. If you pass it FALSE, it displays as a File Save dialog. There's no real functional difference between these two, only a visual difference. You can pass many more arguments to the constructor, specifying the file extensions to show, the default starting file and location, and filters to use when displaying the files. All the rest of these constructor arguments have default values, so you don't have to supply any of them.
After creating the instance of the File Open dialog, you call its DoModal function. This is a member function of the CDialog ancestor class, and it is available in all dialog windows. The DoModal function displays the File Open dialog to the user, as shown in Figure 5.4. The return value of the DoModal function is examined to determine which button the user clicked. If the user clicks the Open button, the IDOK value is returned, as with the MessageBox function. This is how you can determine whether your application needs to take any action on what the user selected with the dialog window.
FIGURE 5.4. The File Open dialog.
NOTE: There are two modes in which a dialog window can be displayed to the user. The first is as a modal window. A modal window halts all other user interaction while it is displayed. The user cannot do anything else in the application until the dialog is closed. A good example of a modal dialog window is a message box where the user cannot continue working with the application until she clicks one of the buttons on the message box.
The second mode in which a dialog window can be displayed to the user is as a modeless window. A modeless window can be open while the user is doing something else in the application, and it doesn't prevent the user from performing other tasks while the dialog is visible. Good examples of a modeless dialog window are the Find and Find and Replace dialogs in Microsoft Word. These dialog windows can be open and displayed on the screen while you are still editing the document that you are searching.
To display the name of the file selected, you set the m_sResults variable to the return value from the GetFileName method of the CFileDialog class. This method returns only the filename without the directory path or drive name, as shown in Figure 5.5. You can use other class methods for getting the directory path (GetPathName) or file extension (GetFileExt).
FIGURE 5.5. Displaying the selected filename.
Now you have an understanding of using standard dialogs. What if you need to create a custom dialog for your application? This task is fairly simple to do because it is mostly a combination of the process that you have already used to create and use the main dialog windows in all your applications and the methods you employed to use the common dialogs. You have to work through a few additional steps, but they are few and you should be comfortable with them soon.
For the custom dialog that you will add to your application, you will provide the user with a edit box in which to enter some text and a group of radio buttons from which the user can select one. When the user clicks the OK button, your application will display the text entered by the user in the display area of the main application dialog window. There is another button that the user can, can click to display which one of the radio buttons was selected. This exercise enables you to see how you can use custom dialog windows to gather information from the user and how you can read the user's selections after the dialog window is closed.
To create a custom dialog for your application, you need to
After doing these things, your custom dialog will be ready for your application. To accomplish these tasks, follow these steps:
FIGURE 5.6. The custom dialog window layout.
Object |
Property |
Setting |
Static Text | ID | IDC_STATIC |
|
Caption | Enter a &message: |
Edit Box | ID | IDC_MESSAGE |
|
Multiline | Checked |
|
Auto Vscroll | Checked |
Group Box | ID | STATIC |
|
Caption | Select an Option |
Radio Button | ID | IDC_OPTION1 |
|
Caption | &Option 1 |
|
Group | Checked |
Radio Button | ID | IDC_OPTION2 |
|
Caption | O&ption 2 |
Radio Button | ID | IDC_OPTION3 |
|
Caption | Op&tion 3 |
Radio Button | ID | IDC_OPTION4 |
|
Caption | Opt&ion 4 |
FIGURE 5.7. The Adding a Class dialog.
FIGURE 5.8. The New Class dialog.
Object | Name | Category | Type |
IDC_MESSAGE | m_sMessage | Value | CString |
IDC_OPTION1 | m_iOption | Value | int |
You should notice two things in the way that you configured the control properties and variables in the custom dialog. First, you should have selected the Group property on only the first of the radio buttons. This designates that all the radio buttons following that one belong to a single group, where only one of the radio buttons may be selected at a time. If you select the Group property on all the radio buttons, they are all independent of each other, allowing you to select all the buttons simultaneously. This property makes them behave somewhat like check boxes, but the primary difference is that the user would find it difficult to uncheck one of these controls due to the default behavior where one radio button in each group is always checked. The other difference is in their appearance; the radio buttons have round selection areas instead of the square areas of check boxes.
The other thing to notice is that you declared a single integer variable for the one radio button with the Group property checked. This variable value is controlled by which radio button is selected. The first radio button causes this variable to have a value of 0, the second sets this variable to 1, and so on. Likewise, if you want to automatically select a particular radio button, you can set this variable to one less than the sequence number of the radio button in the group of radio buttons.
NOTE: Because this is the C++ programming language, all numbering begins with 0, not 1. Therefore, the first position in an array or a set of controls is position 0. The second position is position 1. The third position is number 2, and so on.
You have now finished all that you need to do to the second dialog window to make it ready for use. You would expect to need an UpdateData or two in the code behind the dialog, but because you didn't remove the OK and Cancel buttons from the dialog, the UpdateData call is already performed when the user clicks the OK button. As a result, you don't have to touch any code in this second dialog, only in the first dialog.
Now that your custom dialog is ready for your application, using it is similar to the way that you use the common dialogs that are built into Windows. First, you have to declare an instance of the custom dialog class, which calls the class constructor and creates an instance of the class. Next, you call the dialog's DoModal method and capture the return value of that function. Finally, you read the values of the variables that you associated with the controls on the dialog.
Before you can use your custom dialog in your application, you have to make your main dialog window aware of the custom dialog, its variables, and methods and how your main dialog can interact with your custom dialog. You accomplish this by including the header file for your custom dialog in the main source file for your main application dialog. Follow these steps:
1: // DialogsDlg.cpp : implementation file 2: // 3: 4: #include "stdafx.h" 5: #include "Dialogs.h" 6: #include "MsgDlg.h" 7: #include "DialogsDlg.h" 8: 9: #ifdef _DEBUG 10: #define new DEBUG_NEW 11: #undef THIS_FILE 12: static char THIS_FILE[] = __FILE__; 13: #endif 14: 15:///////////////////////////////////////////////////////////////////////
16: // CAboutDlg dialog used for App About
It is important that you place the #include statement for the MsgDlg.h file before the #include statement for the DialogsDlg.h file. The reason is that you will be adding a variable declaration for your custom dialog to the main dialog class in the main dialog's header file. If the MsgDlg.h header file is included after the header file for the main dialog, the compiler will complain loudly and will refuse to compile your application until you move the #include of the MsgDlg.h file above the #include of the DialogsDlg.h file.
NOTE: The #include statement is what is known as a compiler directive in the C and C++ programming languages. What it tells the compiler to do is read the contents of the file named into the source code that is being compiled. It is used to separate class, structure, and function declarations into a file that can be included in any source code that needs to be aware of the information in the header file. For more information on how the #include statements work, and why you use them, see Appendix A, "C++ Review."
Now that you have made your main application dialog aware of the custom dialog that you created, you need to declare a variable of your custom dialog. Follow these steps:
If you expand the CDialogsDlg class in the tree view, you should see the instance of your custom dialog as a member of the main application dialog class. This means that you are ready to begin using the custom dialog in your application.
Now that you have added your custom dialog to the main application dialog as a variable that is always available, not just as a local variable available only within a single function (as with the CFileDialog variable), you can add code to use the dialog. To do this, follow these steps:
1: void CDialogsDlg::OnBcustomdialog() 2: { 3: // TODO: Add your control notification handler code here 4: 5: /////////////////////// 6: // MY CODE STARTS HERE 7: /////////////////////// 8: 9: // Show the message dialog and capture the result 10: if (m_dMsgDlg.DoModal () == IDOK) 11: { 12: // The user checked OK, display the message the 13: // user typed in on the message dialog 14: m_sResults = m_dMsgDlg.m_sMessage; 15: // Update the dialog 16: UpdateData(FALSE); 17: // Enable the Which Option button 18: m_cWhichOption.EnableWindow(TRUE); 19: } 20: 21: /////////////////////// 22: // MY CODE ENDS HERE 23: ///////////////////////
24: }
1: void CDialogsDlg::OnBwhichoption() 2: { 3: // TODO: Add your control notification handler code here 4: 5: /////////////////////// 6: // MY CODE STARTS HERE 7: /////////////////////// 8: 9: // Determine which radio button was selected, and display 10: // a message for the user to show which one was selected. 11: switch(m_dMsgDlg.m_iOption) 12: { 13: case 0: // Was it the first radio button? 14: m_sResults = "The first option was selected."; 15: break; 16: case 1: // Was it the second radio button? 17: m_sResults = "The second option was selected."; 18: break; 19: case 2: // Was it the third radio button? 20: m_sResults = "The third option was selected."; 21: break; 22: case 3: // Was it the fourth radio button? 23: m_sResults = "The fourth option was selected."; 24: break; 25: default: // Were none of the radio buttons selected? 26: m_sResults = "No option was selected."; 27: break; 28: } 29: 30: // Update the dialog 31: UpdateData(FALSE); 32: 33: /////////////////////// 34: // MY CODE ENDS HERE 35: ///////////////////////
36:
In the first listing, you called the DoModal method of the custom dialog, which displayed the dialog for the user, waiting for the user to click one of the two buttons on the dialog, as in Figure 5.9. If the user clicks the OK button, you copy the message the user typed in the custom dialog into the edit box variable to be displayed to the user. After updating the dialog display with the new variable values, you enable the Which Option button, as shown in Figure 5.10. If the user clicks the Cancel button, none of this is done. The dialog display is not changed.
FIGURE 5.9. The custom dialog allows the user to enter a message.
FIGURE 5.10. The message entered on the custom dialog is displayed for the user.
When the user clicks the Which Option button, you pass the radio button variable on the custom dialog to a switch statement, selecting a message that tells the user which radio button was selected, as shown in Figure 5.11. Notice that in both of these functions, you can access the control variables on the custom dialog directly from the main dialog. That is because the Class Wizard automatically declares the variables associated with controls as public, making them completely accessible outside the dialog class. You can change this by placing a private: access specifier where the public: access specifier is. You don't want to place anything after the //{{AFX_DATA line, where the variables are declared, because the variables are declared within an MFC Class Wizard macro, which enables the Developer Studio wizards to locate and manipulate the variables as needed without interfering with the Visual C++ compiler when you compile your application.
FIGURE 5.11. The option selected on the custom dialog is displayed for the user.
Today you learned how you can use additional dialog windows in your application to provide interactive experience for your users. You learned about the options available to you with the simple MessageBox function, how you can provide your users a variety of button combinations, and how you can determine which button the user selects. You saw how you can use this information to determine which path to take in your application logic.
You also learned about some of the common dialogs that are built into the Windows operating systems and how they have been encapsulated into C++ classes in the MFC class library. You learned how you can use the File Open dialog to present the user with the standard file selection dialog and how you can determine which file the user selected.
Finally, you learned how you can design your own additional dialogs that you can add to your applications to get information from the user and how you can capture that information and use it in your application.
1: typedef struct tagOFN { // ofn 2: DWORD lStructSize; 3: HWND hwndOwner; 4: HINSTANCE hInstance; 5: LPCTSTR lpstrFilter; 6: LPTSTR lpstrCustomFilter; 7: DWORD nMaxCustFilter; 8: DWORD nFilterIndex; 9: LPTSTR lpstrFile; 10: DWORD nMaxFile; 11: LPTSTR lpstrFileTitle; 12: DWORD nMaxFileTitle; 13: LPCTSTR lpstrInitialDir; 14: LPCTSTR lpstrTitle; 15: DWORD Flags; 16: WORD nFileOffset; 17: WORD nFileExtension; 18: LPCTSTR lpstrDefExt; 19: DWORD lCustData; 20: LPOFNHOOKPROC lpfnHook; 21: LPCTSTR lpTemplateName;
22: } OPENFILENAME;
1: void CDialogsDlg::OnFileopen() 2: { 3: // TODO: Add your control notification handler code here 4: 5: /////////////////////// 6: // MY CODE STARTS HERE 7: /////////////////////// 8: 9: CFileDialog m_ldFile(TRUE); 10: 11: // Initialize the starting directory 12: m_ldFile.m_ofn.lpstrInitialDir = "C:\\Temp\\"; 13: 14: // Show the File open dialog and capture the result 15: if (m_ldFile.DoModal() == IDOK) 16: { 17: // Get the filename selected 18: m_sResults = m_ldFile.GetFileName(); 19: // Update the dialog 20: UpdateData(FALSE); 21: } 22: 23: /////////////////////// 24: // MY CODE ENDS HERE 25: ///////////////////////
26: }
The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. The answers to the quiz questions and exercises are provided in Appendix B, "Answers."
© Copyright, Macmillan Computer Publishing. All rights reserved.