Click here to Skip to main content
Click here to Skip to main content

XFileDialog - Customizing CFileDialog

By , 23 Jun 2003
 

Introduction

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.

Implementation notes

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"
BEGIN
    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
END

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 CFileDialog in ctor of your derived class:

SetTemplate(0, _T("IDD_XFILEDIALOG"));

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:

screenshot

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:

screenshot

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:

screenshot

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 OnSize().

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:

screenshot

CXFileDialog Features

Here are features in new CXFileDialog:

  • 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

To integrate CXFileDialog into your app, you first need to add following files to your project:

  • XFileDialog.cpp
  • XFileDialog.h
  • XFileDialog.rc
  • XFileDialogRes.h
  • XHistoryCombo.cpp
  • XHistoryCombo.h

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 #endif:

screenshot

Next, include header file XFileDialog.h in appropriate project files. Now you are ready to start using CXFileDialog.

Demo App

The XFileDialogTest.exe demo shows how to use CXFileDialog:

screenshot

Acknowledgments

Revision history

  • Version 1.1 - 2003 June 25
    • Fixed some focus bugs;
    • Added check for directory existence.
  • Version 1.0 - 2003 June 24
    • Initial public release.

Usage

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Hans Dietrich
Software Developer (Senior) Hans Dietrich Software
United States United States
Member
I attended St. Michael's College of the University of Toronto, with the intention of becoming a priest. A friend in the University's Computer Science Department got me interested in programming, and I have been hooked ever since.
 
Recently, I have moved to Los Angeles where I am doing consulting and development work.
 
For consulting and custom software development, please see www.hdsoft.org.






Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberwa4 Feb '11 - 2:46 
Very usefull explanation of customizing CFileDialg.
QuestionExplorer Style Missing?memberKyudos29 Oct '09 - 16:52 
Hi Hans,
 
I've been reading your great articles trying to solve a particular problem, but I think I don't even know exactly what the problem is...
 
I have an MFC VC6 app and I was trying to enable the 'new style' file dialogs (with the shortcut buttons on the left..ie., Recent Docs / Desktop / My Computer / Network). I thought it would just be a case of setting a flag...but I can't figure it out...
 
If I call:
 
OPENFILENAME ofn;
char szFileName[MAX_PATH] = "";
 
ZeroMemory(&ofn, sizeof(ofn));
 
ofn.lStructSize = sizeof(ofn); // SEE NOTE BELOW
ofn.hwndOwner = this->GetSafeHwnd();
ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrDefExt = "txt";
 
if (GetOpenFileName(&ofn))
{
}
 
I get the dialog with the shortcuts. But if I use CFileDialog:
 
CFileDialog fdlg(TRUE, DES_EXT, NULL, OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, DESIGN_FILTER);
 
if (fdlg.DoModal() == IDOK)
{
}
 
I don't get the shortcuts. Try as I might, I can't seem to find a straightforward answer as to why this is the case.
 
You seem to have a better grasp on this stuff - any pointers?
 
Cheers!
AnswerRe: Explorer Style Missing?mvpHans Dietrich1 Nov '09 - 16:21 
I think it would help you to read Paul DiLascia's article that I reference above; this is what I based my article on.
 
Best wishes,
Hans
 

[Hans Dietrich Software]

GeneralRe: Explorer Style Missing?memberKyudos3 Nov '09 - 15:57 
Thanks Hans, I'll have a look ... (the link in the article is dead BTW...new link http://msdn.microsoft.com/en-us/magazine/cc301412.aspx[^])
QuestionXFileOpen - any way to remember last View and Size of common dialog?membermatt200014 Aug '09 - 19:02 
1. Most interested in View type (Detail)
2. Sort order
3. Size of dialog
 
I have the hook in place and working, just need to restore their last state.
 
Is it even possible? I've been looking around and have yet to find a solution.
 
Lastly, is there a library that will hijack the FileOpen dialog and allow restoring?
 
Thanks
QuestionCFileDialog - store datamembervijayashree30 Apr '09 - 23:23 
I have a calculated result (data) and the data must be saved using save as dialog CFileDialog. how to do it. i am able to create the files and store data. but the format is not as i wanted.
iwanted the data to be like
 
abc value \tabspace ang value \newline
 
i am giving below my code.
 
float abc;
int ang;
 

this->UpdateData();
 
CString str;
CFile f;
char strFilter[] = { "EXCEL Files (*.txt)|*.txt|All Files (*.*)|*.*||" };
CFileDialog FileDlg(FALSE, ".txt", NULL, 0, strFilter);
CFileDialog::GetPathName;
 

if( FileDlg.DoModal() == IDOK )
{
f.Open(FileDlg.GetFileName(), CFile::modeCreate | CFile::modeWrite);
CArchive ar(&f, CArchive::store);
 
for(ang=0;ang<=180;ang++)
{
abc = ang + 2;
str.Format("%f\t%d",abc,ang);
ar << str;
 
}

ar.Close();
f.Close();
 
}
else
return;
GeneralFails on VS2008 and Vista - How To Resolve.memberNeville Franks9 Mar '09 - 14:09 
XFileDialog fails on Vista when using VS2008. This is because by default the latest MFC code uses the new Vista (Explorer) style File|Open dialogs which is incompatible with SetTemplate() and will raise an "Attempted an unsupported operation" Exception.
 
To continue to use the older style File|Open dialogs and XFileDialog you need to set the new CFileDialog( .., bVistaStyle ) parameter to FALSE.
 
If instead you want to use the new Vista style dialogs see Michael Dunn's article Vista Goodies in C++: Using the New Vista File Dialogs" http://www.codeproject.com/KB/vista/VGFileDialogs.aspx[^]
 
Neville Franks, Author of Surfulater www.surfulater.com "Save what you Surf" and ED for Windows www.getsoft.com
 

GeneralRe: Fails on VS2008 and Vista - How To Resolve.mvpHans Dietrich9 Mar '09 - 22:43 
Thanks, Neville!
 

GeneralI'm implementing something similar but OnSize and other events not firing [modified]memberdpackage10 Feb '09 - 4:49 
I'm adding the control dynamically, so I'm not using templates.
 
I'm also not overriding DoModal the same way. I'm just calling CFileDialog::DoModal from inside my override.
 
Now when I run, none of the extended CFileDialog events fire after the initial load of the dialog, including OnSize which I really need.
 
Do I need to manually implement DoModal the way you do?
 
modified on Tuesday, February 10, 2009 11:58 AM

Generaldemo project does not work under visual studio 2005 [modified]membereffem7 Jan '07 - 6:56 
This really seems a nice thing. However, when I downloaded it I had to do some work to get it to compile under visual studio 2005 pro.
 
It converts the project. Then when trying to compile I get:
1>c:\projects\cxfiledialog\xhistorycombo.cpp(260) : error C2065: 'n' : undeclared identifier
 
which to me is a little bit odd. This line says:
for (n = nMax; n < 1000/* prevent runaway*/; n++)
 
line 250 says:
for (int n = 0; n < nMax; n++)
 
Perhaps the scope of int n has been changed to the inner loop, so I removed the int from line 250 and explicity declare int n; just before it.
 
However this gives me two linker errors:
1>CVTRES : fatal error CVT1100: duplicate resource. type:MANIFEST, name:1, language:0x0409
1>LINK : fatal error LNK1123: failure during conversion to COFF: file invalid or corrupt
 
This can be fixed by turning off incremental linking.
No idea if there is a better solution.
 

On a slightly related topic:
does anyone know some sample code that will allow me to show a counter when doing multi file selection showing the # of files selected??
 
Thanks, Frans.
 
One more thing:
When trying this in my project I didn't have to edit the resource includes any more.
(I would get duplicate linker errors if I did).
 
Also make sure that when you are using this that you properly call the SetOsVersion
after creating your dialog. Without that I did not get a dialog. On XP I have to set the value to 1. No idea what it should really be.
 
And finally when I included the files in my own project I did not get the manifest error even when doing incremental linking
 

 

-- modified at 13:58 Sunday 7th January, 2007

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 24 Jun 2003
Article Copyright 2003 by Hans Dietrich
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid