Introduction
This article explains how to expand the CFileDialog common dialog box. Although there
are similar articles elsewhere on the Internet, none of them resolved the problem of
adding user controls in CFileDialog in an MFC manner (without hook procedures, which
are not encouridged by MFC any more).
In this article you’ll find a way how to add one static control and one combo
box control to CFileDialog standard dialog. The sample code is of no particular
use as the problem is general, but briefly explains concept, and what is more
important, I am using similar code in my applications.
TASKS
To add two controls to CFileDialog dialog box, we have to do following:
- Enlarge standard CFileDialog. This can be done by subblussing CFileDialog class
and overiding OnInitDialog member function - To retrieve information from additional controls, you must override
OnDestroy() member function.
SAMPLE
The sample included here shows CFileDialogEx class, derived from
CFileDialog using classwizard. Added member variable, m_nSubType,
is used for displaying possible document subtypes. Choosen value
is saved in the same variable and passed to document m_nDocSubType
variable. CComboBox and CStatic objects are added for purpose of our
sample.There are two message handlers that override standard
WM_INITDIALOG and WM_DESTROY messages for CFileDialogEx object:
/////////////////////////////////////////// // CFileDialogEx dialog class CFileDialogEx : public CFileDialog { DECLARE_DYNAMIC(CFileDialogEx) public: int m_nSubType; CStatic m_Static; CComboBox m_Combo; // our combo box control // TRUE for FileOpen, FALSE for FileSaveAs CFileDialogEx(BOOL bOpenFileDialog, LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL); protected: //{{AFX_MSG(CFileDialogEx) virtual BOOL OnInitDialog(); afx_msg void OnDestroy(); //}}AFX_MSG DECLARE_MESSAGE_MAP() };
Our first task is done by adding handler for WM_INITDIALOG:
BOOL CFileDialogEx::OnInitDialog() { CFileDialog::OnInitDialog(); // TODO: Add extra initialization here // We need to enlarge standard CFileDialog to make space // for our controls idea from Christian Skovdal Andersen // article - Customizing CFileDialog char szText[120]; // This variable should be changed acording to your wishes const UINT iExtraSize = 50; // Get a pointer to the original dialog box. CWnd *wndDlg = GetParent(); RECT Rect; wndDlg->GetWindowRect(&Rect); // Change the size of FileOpen dialog wndDlg->SetWindowPos(NULL, 0, 0, Rect.right - Rect.left, Rect.bottom - Rect.top + iExtraSize, SWP_NOMOVE); // Standard CFileDialog control ID's are defined in// Do not forget to include in implementation file // cmb1 - standard file name combo box control CWnd *wndComboCtrl = wndDlg->GetDlgItem(cmb1); wndComboCtrl->GetWindowRect(&Rect); wndDlg->ScreenToClient(&Rect); // Remember it is child controls // Put our control(s) somewhere below HIDDEN checkbox // Make space for 3 different subtypes Rect.top += 60; Rect.bottom += 120; Rect.left += 50; // Our control is CComboBox object // IMPORTANT: We must put wndDlg here as hWndParent, // NOTE: "this" as written in Microsoft documentation // example // NOTE: IDC_MYCOMBOBOX and IDC_MYSTATIC must be defined // (best in resource.h) m_Combo.Create(WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP, Rect, wndDlg, IDC_MYCOMBOBOX); // save new control font according to other controls font m_Combo.SetFont(wndComboCtrl->GetFont(), TRUE); m_Combo.SetFocus(); // We also need Static Control. Get coordinates from // stc3 control CWnd *wndStaticCtrl = wndDlg->GetDlgItem(stc2); wndStaticCtrl->GetWindowRect(&Rect); wndDlg->ScreenToClient(&Rect); Rect.top += 60; Rect.bottom += 80; Rect.right += 40; // our static control m_Static.Create("Proba", WS_CHILD | WS_VISIBLE, Rect, wndDlg, IDC_MYSTATIC); m_Static.SetFont(wndComboCtrl->GetFont(), TRUE); // Now, fill up static control and combo control // Our ComboBox control will offer three diffirent file // subtypes. Current file subtype is held in m_nSubType // member variable which will hold resulting (final) subtype, // also. for (int i=0; i < 3; i++) { sprintf(szText, "Subtype%d", i); wndDlg->SendDlgItemMessage(IDC_MYCOMBOBOX, CB_INSERTSTRING, (WPARAM) (-1), (LPARAM) (szText)); // Set displayed subtype to sybtype format of working file if (m_nSubType == i) wndDlg->SetDlgItemText(IDC_MYCOMBOBOX, szText); } // Add some general text for static box sprintf(szText, "File SubType Format"); wndDlg->SetDlgItemText(IDC_MYSTATIC, szText); return TRUE; // return TRUE unless you set the focus // to a control // EXCEPTION: OCX Property Pages should // return FALSE }
Second task is done by adding message handler for WM_DESTROY message:
void CFileDialogEx::OnDestroy() { CFileDialog::OnDestroy(); // TODO: Add your message handler code here char szText[40]; // get/save SaveAs file subtype if (GetParent()->GetDlgItemText(IDC_MYCOMBOBOX, szText, sizeof(szText)) > 0) { m_nSubType = szText[strlen(szText)-1] - '0'; } }
IMPLEMENTATION
Probably you’ll add message handler for ID_EDIT_SAVEAS menu item:
void CMyFileDialogDoc::OnFileSaveAs() { // TODO: Add your command handler code here // Call our Save As dialog box CFileDialogEx filedialog(FALSE, NULL, GetTitle(), OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, NULL); // pass document subtype to our SaveAs dialog box filedialog.m_nSubType = m_nDocSubType; filedialog.m_ofn.lpstrTitle = "User added controls - Save File As"; // display our dialog if (filedialog.DoModal() == IDOK) { // get/save document subtype m_nDocSubType = filedialog.m_nSubType; char szText[100]; sprintf(szText, "Document will be saved under " "subtype %d", m_nDocSubType); AfxMessageBox(szText); // do your own SaveAs procedure or call document default // CDocument::OnSaveDocument() } // error handling (if any) // ... }
Comparing this with hook procedures and user-added templates, that I had
to prepare in my earlier API samples, this seems to be much elegant. Only
problem was bad Microsoft documentation that used “this” pointer instead
of parent window (as explained earlier) while creating user controls. Also,
you cannot override OnOK() member function in CFileDialog dialog as you can
do that in other dialogs. You should check/save data from your controls in
OnDestroy() message handler.
I included demo project that shows trivial use of CFileDialogEx class, but
it should be used only as reference for your own purposes.