MFC Solution to Explanding the CFileDialog Common Dialog

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:

  1. Enlarge standard CFileDialog. This can be done by subblussing CFileDialog class
    and overiding OnInitDialog member function

  2. 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.

Downloads

Download demo project – 19 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read