When new common file dialogs were first introduced, I immediately liked the way they looked. But I also noticed that drop-down list of drives and directories seemed a bit awkward, especially if you wanted to flip between two directories on different drives. When we started hearing same thing from our customers, I knew we should try to make it easier to pick a folder.
My initial thought was, OK, CFileDialog is a common dialog just like
CFontDialog, all I have to do is copy the dialog template and insert my own controls. It turns out that customizing
CFileDialog is completely different than customizing
CFontDialog. (My article XFontDialog - Customizing CFontDialog Part I: Adding Font Filters explains how to customize
CFontDialog). I found a reasonably complete description of what was going on in the MSDN article Open and Save As Dialog Boxes. This article explains that, to add controls to the
CFileDialog dialog, what you do is create a dialog template that contains only the controls that you want to add.
IDD_XFILEDIALOG is defined as:
IDD_XFILEDIALOG DIALOG DISCARDABLE 0, 0, 360, 20
STYLE DS_3DLOOK | DS_CONTROL | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS
FONT 8, "MS Sans Serif"
LTEXT "Recent folders:",IDC_MRU_CAPTION,13,3,51,8,
SS_CENTERIMAGE | NOT WS_GROUP
COMBOBOX IDC_MRU_COMBO,85,1,260,102,CBS_DROPDOWN |
CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
Note the styles of this dialog - they are necessary for proper integration with
CFileDialog. Once you have this dialog defined, you can add it to
ctor of your derived class:
I set up dialog template and its rc file according to guidelines I described in my article XDialogImport - How to share dialogs between projects.
Once I had dialog template, I then started to unravel secrets of how to integrate with
CFileDialog. As I said before, it is completely different than
CFontDialog. Whereas with
CFontDialog all controls are on one dialog, with a customized
CFileDialog you have two levels of dialogs: the new
IDD_XFILEDIALOG actually is a child of original
CFileDialog. This is alluded to in MSDN article, but I did not fully appreciate what this meant, until I saw it using one of my favorite tools, HPS HwndSpy:
As you see in the screenshot,
IDD_XFILEDIALOG is highlighted in window tree, and it is child of "Open" dialog. What this means in terms of implementation of
CXFileDialog is that you can access all controls on
IDD_XFILEDIALOG just like you would on any other dialog - by using
GetDlgItem() or by using
DDX_Control. However, all controls of
CFileDialog are sibling to
IDD_XFILEDIALOG - to access them, you must use
GetParent()->GetDlgItem(xxx). Once I understood this, rest of the code was easy to implement - well, most of it was easy. There were still a few more things that bit me.
Before getting into more details, let me show you what I was attempting to do. I wanted to add a history combo box for most-recently used folders, so that every time user clicked Open, the current folder would be added to history list. Every time
CXFileDialog was opened, history list items would be restored to combo box (if they were still valid directories). I decided to use Paul Vickery's excellent History ComboBox to take care of loading and saving folder items, with a modification I made to allow checking folders for validity. Here is what
XFileDialog looks like:
Of course there were usual details to take care of - like positioning and sizing
IDD_XFILEDIALOG controls in the
OnSize() handler. But when I tested
CXFileDialog with pre-Win2K layout, I discovered that "File Name" control ID actually had two different values - one (1152) for when it was acting like an edit box (pre-Win2K), and another value (1148) for when it was acting like a combo box (Win2K and later). Furthermore, File Name control was also affected by a registry setting, which can be used to enable/disable the MRU property of the File Name control. How did I figure this out? Again, I used HPS HwndSpy:
The next thing that I tested was Read-Only checkbox. Naturally, when I enabled it, the checkbox positioned itself right on top of my Recent Folders control, since I had forgotten to account for checkbox in
Finally, I tested pre-Win2K layout. There was not enough room for the static label "Recent folders", so for pre-Win2K layout, I moved all combo boxes to the right. Here is the new pre-Win2K layout:
Here are features in new
- Recent folders MRU combo box - adds a combo box with list of most-recently used folders. Selecting a folder from the list will change the file display to that folder.
SetTitle() - Provides a convenient way to set title of dialog.
SetOsVersion() - Allows you to choose between old-style and Explorer-style dialog.
GetPath() - Gets path of selected file.
How to use
CXFileDialog into your app, you first need to add following files to your project:
You also need to add XFileDialog.rc to project rc file - go to View | Resource Includes... and in the bottom listbox, scroll down to the end. Insert
#include "XFileDialog.rc" right before the
Next, include header file XFileDialog.h in appropriate project files. Now you are ready to start using
The XFileDialogTest.exe demo shows how to use
- Version 1.1 - 2003 June 25
- Fixed some focus bugs;
- Added check for directory existence.
- Version 1.0 - 2003 June 24
This software is released into the public domain. You are free to use it in any way you like. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.