Created: March 25, 1992
Abstract
This technical note describes the use of the standard dialog classes provided with MFC.
The Microsoft Foundation Class (MFC) library provides a full-featured set of C++ object classes for the MicrosoftÒ WindowsÔ graphical environment. It includes classes that directly support application development for Windows as well as general-purpose classes for collections, files, persistent storage, exceptions, diagnostics, memory management, strings, and time. Each MFC technical note describes a feature of MFC using code fragments and examples.
This technical note describes the use of the standard dialog classes provided with the MicrosoftÒ Foundation Class (MFC) library. The classes are CFileDialog, CFontDialog, CColorDialog, CPrintDialog, and CFindReplaceDialog. They provide standard user-interface objects for your application. The class declarations for these classes can be found in the source file AFXDLGS.H in the Sample Code, MFC Samples, INCLUDE area on this disc.
You may use these classes for any MFC application. Although these classes use the Microsoft WindowsÔ version 3.1 COMMDLG functions, they do not require Windows version 3.1 to run. You must redistribute COMMDLG.DLL with your application and install the dynamic-link library (DLL) in the user's Windows version 3.0 system directory.
When building an application that uses these classes, you must link with the COMMDLG.LIB library, which is part of the Microsoft Windows Software Development Kit (SDK) installation.
These dialog classes can be used in two ways. You can use them "as is" and supply the necessary arguments to the constructor to customize the dialogs to suit your needs. You might also consider deriving from these classes and providing a specific constructor tailored to your needs. In either case, these dialogs act like standard MFC dialogs because they derive from CDialog (either directly or indirectly.) You can use message maps and customize these dialogs even more.
COMMDLG dialog APIs interface with the application code through a parameter block structure. The MFC dialog classes provide public access to this structure, which may be accessed at any time to permit maximum customization. On the other hand, MFC provides sensible defaults for most of the fields in the parameter block so that you do not need to worry about them. These structures are described in the Windows version 3.1 SDK reference manual.
The MULTIPAD sample application uses CFileDialog, CFontDialog, and CPrintDialog. The CHART application uses CFileDialog and CPrintDialog. The MDI application uses CColorDialog. In addition, the OLE classes use the standard CFileDialog for implementing standard OLE user interface objects.
The CFileDialog class encapsulates the OPENFILENAME structure and the two Windows APIs GetOpenFileName and GetSaveFileName. This dialog box is used to obtain from a user either a new filename or the name of an existing file to open. CFileDialog is a modal dialog and derives from the class CModalDialog.
To use a CFileDialog class object, create an object using the CFileDialog constructor. The arguments to the constructor are listed below; note that several have default values:
bOpenFileDialog | is set to TRUE for a file open dialog, FALSE for a file save dialog. |
lpszDefExt | is the default extension; if a user does not include a file extension, this is automatically appended. |
lpszFileName | is the initial filename (defaults to NULL). |
dwFlags | is a set of flags that you can supply that allow you to customize the dialog box. See the Windows SDK reference manual (OPENFILESTRUCT) for more information (defaults to OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT). |
lpszFilter | is a series of pairs of strings that specify filters the user may apply to the files. See below (defaults to NULL). |
pParentWnd | is a pointer to a parent window. This is used only when routing customized help messages (defaults to NULL). |
The lpszFilter is used to display a possible list of suffixes. For example, Microsoft Excel permits users to open *.XLC and *.XLS files (and many others). The filter for Excel would look something like the following:
char BASED_CODE szFilter[] =
"Chart Files (*.xlc)|*.xlc|Worksheet Files (*.xlm)|*.xlm|All Files (*.*)|*.*||"
Note the use of the | symbol to separate the components of the filter. Also note that the entire filter ends with two | symbols. This is the standard MFC syntax for specifying filters. Also, note that the entire string is a standard NULL-terminated C string, although you can always use an MFC CString object.
Once the dialog has been constructed, you are free to modify the public member variable m_ofn, which is an OPENFILENAME structure. You may manually set any flags or fields that you need. When you are ready to prompt the user, call the DoModal function (this is a virtual function derived from CModalDialog). The user is then prompted for a filename (either an existing file or new file, depending on the bOpenFileDialog constructor parameter). When the user clicks OK or CANCEL, or selects the Close command from the dialog's system menu, control is returned to your application. The return value from DoModal indicates success (IDOK) or failure (IDCANCEL.) If the return value is IDCANCEL, either the user closed the dialog or clicked CANCEL, or there was an error in the parameter block. If you suspect an error, you can call the CommDlgExtendedError API to learn more about the problem.
The CommDlgExtendedError function returns 0 if there was no error. This function differs from the standard OnOK and OnCancel functions in that it is possible to force the user to change actions, whereas OnOK and OnCancel are notifications that you must accept.
If the user chose a filename, you can continue processing. The CFileDialog class provides a number of helper functions to retrieve information regarding the user's choice:
GetPathName | retrieves the fully qualified path of the selected file (suitable to use with CFile). |
GetFileName | retrieves just the filename without an extension. |
GetFileExt | retrieves only the file extension (without a '.'). |
GetFileTitle | retrieves the preferred title of the file, which should be used as the title text of the frame window. |
GetReadOnlyPref returns TRUE if the user checked the read-only option. If this is TRUE, you should open the file with read-only permissions.
There are also several "callback" functions that you may respond to in your derived CFileDialog class. You do not need message map entries for these, because they are standard virtual functions:
OnShareViolation | is called when a network sharing violation occurs while processing the dialog. See OPENFILENAME in the Windows SDK reference manual for more information. |
OnLBSelChangeNotify | is called whenever the user changes the selection(s) in the file list. You can use this message if you need to do extra work when a file is a potential selection, such as calculating disk space required or displaying file access rights. |
OnFileNameOK | is called when the user selects OK. You can validate the filename and determine if the user can continue. If you return TRUE, be sure to indicate to the user the problem with the selected file(s). |
One other technique available to customize CFileDialog is to introduce a message map in a derived class and to handle messages just as you would for any other dialog. For example, you can modify the dialog background color or handle the command messages for a new control (the .RC file for the dialog is included).
The CFontDialog class provides support for a standard dialog that permits a user to choose a font. The dialog makes use of the COMMDLG structure CHOOSEFONT and the API ChooseFont. CFontDialog is a modal dialog derived from the CModalDialog class.
To use a CFontDialog class object, create an object using the CFontDialog constructor. The arguments to the constructor are below; note that several have default values:
lplfInitial | is the initial settings of the dialog box (defaults to NULL). |
dwFlags | is a set of flags that you can use to customize the function and appearance of the dialog. See the CHOOSEFONT structure reference for more information on possible values (defaults to CF_EFFECTS | CF_SCREENFONTS). |
hdcPrinter | if supplied is a CDC for the printer for which fonts are to be selected (defaults to NULL). |
pParentWnd | is a pointer to a parent window. This is used only when routing customized help messages (defaults to NULL). |
As with the CFileDialog class, once the object has been constructed you are free to modify the CHOOSEFONT structure, m_cf, to customize the dialog box fully. After any further customization, you call the DoModal member function, which will return either IDOK or IDCANCEL. If the return value is IDCANCEL, the user clicked on CANCEL or there was an initialization error in the dialog. You may use the CommDlgExtendedError function to determine if there was an initialization error. If the user selected OK, the IDOK value is returned. The CFontDialog class provides a number of helper functions for extracting information out of the dialog:
GetFaceName | returns the face name of the font. |
GetStyleName | returns the style name of the font. |
GetSize | returns the point size in 1/10ths of a point. |
GetColor | returns the color of the selected font. |
GetWeight | returns the weight of the font. |
IsStrikeout | returns TRUE if the strikeout effect was selected. |
IsUnderline | returns TRUE if the underline effect was selected. |
IsBold | returns TRUE if the weight is equal to FW_BOLD. |
IsItalic | returns TRUE if the font is italic. |
In addition, the member variable m_lf is the LOGFONT descriptor of the font, which can be used in a CreateFontIndirect call or accessed directly.
You may customize the dialog by deriving your own class from CFontDialog and using a message map to handle any messages. You can also use the CFontDialog::GetCurrentFont member function to determine the currently selected font while in a message handler.
The CColorDialog class provides support for a standard dialog that permits a user to choose/create a color. The dialog makes use of the COMMDLG structure CHOOSECOLOR and the API ChooseColor. CColorDialog is a modal dialog derived from the CModalDialog class.
CColorDialog is used just like CFontDialog. The COMMDLG structure that is used for customizing the dialog is CHOOSECOLOR, and it can be modified after construction of the object and before DoModal is called. The arguments to the constructor are as follows:
clrInit | is a COLORREF that is the initial color selection (defaults to RGB(0,0,0)). |
dwFlags | is a set of flags that you can use to customize the function and appearance of the dialog. See the CHOOSECOLOR structure reference for more information on possible values (defaults to 0). |
pParentWnd | is a pointer to a parent window. This is used only when routing customized help messages (defaults to NULL). |
In addition, CColorDialog permits the user to define up to 16 custom colors. CColorDialog saves these custom colors between invocations of the dialog in the static member variable clrSavedCustom. If you wish to save these colors between executions of the application, you must do this yourself. After the dialog has been constructed, you call DoModal. If the return value is IDOK, you can use the GetColor member function to retrieve the color the user selected.
As with all of the standard dialog classes, you are permitted to define a message map in your derived CColorDialog class to customize the dialog to suit your needs. The member function SetCurrentColor is provided for forcing the currently selected color to a certain value. As with CFileDialog, a validation callback is provided that gives you a chance to override the OK selection. This validation is handled in the OnColorOK virtual function.
The CPrintDialog class provides support for a standard dialog that permits a user to print a document or set up the printer. The dialog makes use of the COMMDLG structure PRINTDLG and the API PrintDlg. CPrintDialog is a modal dialog derived from the CModalDialog class.
As with CFileDialog, CPrintDialog is really two different dialogs, distinguished by a constructor argument. The CPrintDialog class can be used to respond to the Print command and/or the Print Setup command (both usually in the File menu.) The arguments to the constructor for CPrintDialog include:
bPrintSetupOnly | is TRUE if you want only a Print Setup dialog, FALSE for the Print dialog. |
dwFlags | is a set of flags that you can use to customize the function and appearance of the dialog. See the PRINTDLG structure reference for more information on possible values (defaults to PD_ALLPAGES | PD_USEDEVMODECOPIES | PD_NOPAGENUMS | PD_HIDEPRINTTOFILE | PD_NOSELECTION). |
pParentWnd | is a pointer to a parent window. This is used only when routing customized help messages (defaults to NULL). |
You may use the GetDefaults member function to retrieve the current printer defaults. All of the helper functions described below will be valid after a successful return. Note that this member function requires no user interaction.
As with other standard dialogs, you can customize the appearance and functionality by directly modifying the PRINTDLG structure, which is the m_pd member variable. After any customization you call DoModal, which will return IDOK or IDCANCEL as expected.
Remember to check for extended errors if IDCANCEL is the return value, because there might have been a setup error. If IDOK is returned, there are a number of functions that can be used to retrieve information about the selected printer. Some of these functions might not be valid, depending on the settings of dwFlags and m_pd.Flags:
GetCopies | returns the number of copies the user chose. |
GetFromPage | returns the starting page of the print job. |
GetToPage | returns the ending page of the print job. |
GetDevMode | returns a pointer to the device mode. |
GetDriverName | returns a string that is the driver name. |
GetDeviceName | returns a string that is the device name. |
GetPortName | returns a string that is the port name. |
PrintCollate | returns TRUE if the job is to be collated. |
PrintAll | returns TRUE if all pages are selected. |
PrintRange | returns TRUE if the user entered a print range (GetFromPage and GetToPage are then valid). |
PrintSelection | returns TRUE if the user wishes to print the current selection. |
GetPrinterDC | returns an HDC for the selected printer. |
The GetPrinterDC function returns an HDC only if the bPrintSetupOnly constructor parameter was FALSE (that is, the Print dialog is displayed). You are responsible for calling DeleteDC. If you wish to use this DC as an MFC CDC object, then you can attach it to an object as follows:
{
CDC printerDC;
CPrintDialog pd(FALSE);
if (pd.DoModal() == IDCANCEL)
return FALSE;
printerDC.Attach(pd.GetPrinterDC());
// print using printerDC
} // falling out of scope will implicitly call CDC::DeleteDC
While the Print dialog is active, it is possible for the user to obtain a Print Setup dialog and further customize the print job. MFC handles this automatically for you.
You may customize the CPrintDialog object by deriving your own class from it and using a message map to handle messages and commands of your choosing. If you wish to handle the same message differently depending on if the Print or the Print Setup dialog is active, you will need to derive an additional class, one for the Print dialog and one for your custom Print Setup dialog. In your print dialog class, you will also need to override the AttachOnSetup member function. This function handles the creation of a new dialog for when the Print Setup button is clicked. The source code in WINDLGS.CPP in the Sample Code, MFC Samples, SRC area on this disc provides more documentation on how to implement this feature.
The CFindReplaceDialog class is a standard modeless dialog for implementing a dialog that queries the user for a find/replace string pair. The dialog is dual purpose in that it can display either a Find dialog or a Find Replace dialog. This is a modeless dialog and is derived from the CDialog class. You are responsible for implementing code that does the actual search and replace, because this dialog only receives input from the user. The relevant parameter block structure is FINDREPLACE and the APIs are FindText and ReplaceText.
Because this dialog is modeless, it should be dynamically allocated using operator new. If you wish to allocate a CFindReplace dialog on the stack frame, you will need to derive your own CFindReplaceDialog and override the default behavior of the PostNcDestroy function.
The constructor for this class takes no arguments. When your application needs to query the user for find/replace information, the Create member function is called. This will create and show the modeless dialog. The parameters to Create are as follows:
bFindDialogOnly | is TRUE for Find, FALSE for Find and Replace. |
lpszFindWhat | is the default search string, such as the current selection. |
lpszReplaceWith | is the default replacement string (defaults to NULL). |
dwFlags | is a set of flags that you can use to customize the function and appearance of the dialog. See the FINDREPLACE structure reference for more information on possible values (defaults to FR_DOWN). |
pParentWnd | is the parent of the dialog. This is the dialog that receives the special message indicating that a find/replace action is requested (defaults to the current frame window, CWinApp::m_pMainWnd). |
For pParentWnd to be notified of find/replace requests, you must use the Windows API ::RegisterMessage(FINDMSGSTRING). The return value of this function is a message number that is unique to this application instance. Your frame window should have a message map entry that handles this registered message. The following code fragments show how to do this for a frame window class CMyFrameWnd:
class CMyFrameWnd : public CFrameWnd
{
// normal members
protected:
// CFindReplaceDialog helpers
static CFindReplace* pFindReplace;
static UINT nMsgFind;
afx_msg LONG CmdFindHelper(UINT wParam, LONG
lParam);
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
// normal message map entries
ON_REGISTERED_MESSAGE(nMsgFind, CmdFindHelper)
END_MESSAGE_MAP
UINT CMyFrameWnd::nMsgFind =
::RegisterMessage(FINDMSGSTRING);
CFindReplace* CMyFrameWnd::pFindReplace = NULL;
Note the use of a static member that is the current CFindReplaceDialog object. When the user first executes the Find command, a dialog will be created and that object will be used until the Close command on the system menu is executed or the user clicks CANCEL.
Within CmdFindHelper you will interpret the intentions of the user and do the work for find/replace. To assist you, there are a number of helper functions available:
GetFindString | returns the current find string. |
SearchDown | is TRUE if the user wishes to search down the document. |
FindNext | is TRUE if the user clicked the Find Next button. |
MatchCase | is TRUE if the user wishes to match case of text exactly. |
MatchWholeWord | is TRUE if the user wishes to match entire words only. |
GetReplaceString | returns the current replace string for a replace dialog. |
ReplaceCurrent | is TRUE is the user requested that the current word be replaced in a replace dialog. |
ReplaceAll | is TRUE if the user wishes all occurrences to be replaced. |
IsTerminating | is TRUE if the dialog is terminating (the current m_hWnd is no longer valid when this function returns TRUE). |
GetNotifier | returns the CFindReplaceDialog structure (valid only in the CmdFindHandler callback function). |
The CmdFindHandler function, upon being called, will first use the static member function of CFindReplaceDialog, GetNotifier, to convert the lParam into a CFindReplaceDialog object pointer, which will then be used to call member functions and extract dialog information. If you derived your own CFindReplaceDialog, you will need to provide a typesafe castdown static member function to safely convert the lParam to your own derived CFileDialog.
Normally CmdFindHandler will then do a check to see if the dialog is being terminated (using IsTerminating) and, if this is TRUE, you will clean up (delete the current pFindReplace dialog and set the member variable to NULL) and possibly store away the final find/replace text to be used in the initialization of the next dialog.
Although this dialog is a modeless dialog, as with other CDialog derived modeless dialogs, MFC automatically handles the translation dialog messages and routes them to your dialog's message map. Thus you are free to customize your CFindReplaceDialog by deriving and supplying a message map with the necessary handlers.
Many of these dialogs permit the use of strings to customize the appearance or functionality of the dialog. Whenever a string is used, it is best to use a STRINGTABLE resource rather than embedding the string in your code. The CString::LoadString API can assist you in loading a string from a resource file. Similarly, if the string is read-only, you could place it in a code segment (use the MFC helper macro BASED_CODE).
The standard COMMDLG functions all permit the user to add special "hook" functions to customize the dialog box appearance and functionality. With MFC you should not use these special hook procedures, but you should use a derived class and a message just as you would for any other standard Windows-based dialog. If you require the use of the hook function, be sure to save the MFC hook function and call it if you do not handle a message, just as when dynamically subclassing a window in Windows.
As with other short-lived objects, dialogs that derive from the CModalDialog class are best allocated on the stack frame. The CFindReplaceDialog class is designed to be heap allocated (through operator new) because it is a long-lived object.
All of these dialog classes have a parent window pointer as a constructor parameter. The classes will all use the current frame window (CWinApp::m_pMainWnd) as the parent if you specify NULL as the pParentWnd parameter (the default.) If you are spawning a standard dialog from another dialog, you must pass the current dialog as the parent pointer (the this pointer in the command handler that spawns the dialog). If you do not, the two dialogs will have a sibling relationship rather than a parent/child relationship.
COMMDLG functions require significant stack space, so be sure to have at least 16K of stack space available (use the .DEF file to change the default.)