Contents
Introduction
In this Vista Goodies article, I'll demonstrate how to use Vista's new file dialogs - the built-in dialogs that you use for File-Open and File-Save operations.
This article is written for the RTM version of Vista, using Visual Studio 2005, WTL 7.5, and the Windows SDK. See the introduction in the first Vista Goodies article for more information on where you can download those components.
In Vista, Microsoft updated the common file dialogs again, this time to make them similar to regular Explorer windows. The file-open and file-save dialogs now have a built-in search field as well as the Favorite Links/tree view pane that are in all Explorer windows. Some applications that use the old common file dialog APIs will automatically get the new dialogs, but for backward compatibility reasons, Vista will still show the older dialogs in some situations.
If an app sets the lStructSize
member of the OPENFILENAME
struct to indicate that it was written for an OS prior to Windows 2000, it will continue to get the Windows 95-style dialog. The list view control that displays the filenames will have Vista features (such as larger thumbnail views), but the rest of the dialog will be unchanged. Here is a file-open dialog shown by VC6, which customizes the dialog by adding the Open as combo box:
If an app was written for the Windows 2000/XP file dialog, it will continue to get that version of the dialog. This will happen for many existing MFC and WTL apps, because those class libraries use a callback function that is not present in Vista. To maintain compatibility, Vista will display the Windows 2000-style dialog for those apps:
Finally, for some existing apps and all new apps that use the new file dialog APIs, Vista will show the brand-new common dialogs:
This article will cover the new APIs, and demonstrate how to use the new dialogs in your app, even if you are using a GUI class library like WTL.
Using the File Dialogs with Windows APIs
If an app calls the GetOpenFileName()
or GetSaveFileName()
API to show a file dialog, and does not customize the dialog at all, then Vista will show the new file dialog since there is no danger of breaking the app. The criteria that Vista uses to determine if the app customizes the dialog is whether there is a hook function address stored in the lpfnHook
member of the OPENFILENAME
struct, or a custom dialog template stored in the lpTemplateName
member.
Since MFC and WTL use a hook function to provide notifications to the app, if you use either library's CFileDialog
class, you will get the Windows 2000-style dialog. Therefore, to get the Vista-style dialog, you'll need to use the new COM interfaces that replace the GetOpenFileName()
and GetSaveFileName()
APIs.
File Dialog COM Interfaces
Base interfaces
There are two base interfaces for file dialogs: IModalWindow
and IFileDialog
. IModalWindow
just has one method: Show()
. Show()
shows the dialog using a given window as the dialog's parent, just like DoModal()
in the MFC and ATL dialog classes. Show()
returns S_OK
if the dialog was shown and the user selected a file, or a failure HRESULT
otherwise. If the user cancels the dialog, Show()
returns HRESULT_FROM_WIN32(ERROR_CANCELLED)
.
IFileDialog
contains the methods that are common to the file-open and file-save dialogs. IFileDialog
has a lot of methods that do the same tasks that were accomplished by setting members of an OPENFILENAME
struct in previous versions of Windows. IFileDialog
provides a COM-based API and does away with error-prone conventions such as double-null-terminated strings. Here is a quick overview of the IFileDialog
methods:
SetFileTypes()
, SetFileTypeIndex()
- Used for filling in the file types combo box.
GetFileTypeIndex()
- Used to determine which file type the user selected.
Advise()
, Unadvise()
- Used to start and stop listening to events from a file dialog, analogous to the corresponding
IConnectionPoint
methods.
GetOptions()
, SetOptions()
- Used to read or write a set of flags that control the dialog's behavior, analogous to the
Flags
and FlagsEx
members of OPENFILENAME
.
SetDefaultFolder()
- Sets the folder that the dialog will show when it first appears.
SetTitle()
- Sets the text in the dialog's caption bar.
SetOkButtonLabel()
, SetFileNameLabel()
- Sets the text for the Open/Save button and the static control next to the File name edit box, respectively.
GetFolder()
, SetFolder()
- Used to get or set the folder that the dialog is currently displaying.
GetFolder()
may also be called before the dialog is shown, in which case it does the same thing as SetDefaultFolder()
.
AddPlace()
- Adds an item to the Favorite Links section (the list of folders in the left pane of the dialog).
SetDefaultExtension()
- Sets a default extension to be added to the filename if the user does not enter an extension, analogous to the
lpstrDefExt
member of OPENFILENAME
.
SetClientGuid()
, GetClientGuid()
, ClearClientData()
- Vista normally saves some state information on a per-app basis, so that a file dialog's default folder will be the same folder that the previous dialog was displaying. An app can tell Vista to maintain several such states by creating one GUID per state and passing that GUID to
SetClientGuid()
. ClearClientData()
clears the state associated with that GUID.
SetFilter()
- If the app wants to filter out items from its file dialogs, it can create a COM object that implements
IShellItemFilter
, and pass that IShellItem
interface to SetFilter()
.
GetCurrentSelection()
- Gets the item that is currently selected in the dialog.
GetFileName()
, SetFileName()
- Used to get or set the text in the dialog's File name edit box.
GetResult()
- If the user selects a file and clicks OK,
GetResult()
returns the item that was selected.
Close()
- Closes the dialog and specifies what value
Show()
should return, analogous to what the EndDialog()
API does for regular dialogs.
Functions such as GetResult()
identify files with an IShellItem
interface, instead of a file path or PIDL
. You can think of IShellItem
as a COM version of a SHITEMID
or PIDL
, in that it can represent any item in the shell namespace, not just file system objects. I will cover IShellItem
later on.
File-Open Dialog Interfaces
IFileOpenDialog
inherits from IFileDialog
, and has two additional methods for use in multiple-select dialogs:
GetSelectedItems()
- Used in place of
GetCurrentSelection()
.
GetResults()
- Used in place of
GetResult()
.
Each method returns an IShellItemArray
interface that contains one IShellItem
for each selected item. Those two methods work in single-select dialogs as well, so you can use them in all of your file-open dialogs if you want.
File-Save Dialog Interfaces
IFileSaveDialog
inherits from IFileDialog
, and adds a few methods for setting attributes of the file being saved:
SetSaveAsItem()
- Sets the initial folder and file name to be shown when the dialog opens.
SetProperties()
, SetCollectedProperties()
, GetProperties()
, ApplyProperties()
- Used to read and write shell properties on the file being saved. Shell properties are a new feature of Vista and will not be covered in this article, but Ben Karas's blog has many articles on using properties if you want to learn more about them.
Other Interfaces
There are three other interfaces that will be covered later in this article:
IFileDialogEvents
- Used to receive events fired by the built-in controls in a file dialog.
IFileDialogCustomize
- Used to add controls to a file dialog.
IFileDialogControlEvents
- Used to receive events fired from controls that are added via
IFileDialogCustomize
.
Using the File Dialog Interfaces Directly
Basic File-Open Dialog Example
Here is a simple example of how to use the file-open dialog to select one file. This code does very little customization, just setting the dialog's caption and putting three items in the file type list.
The file type list uses a new system that is far easier than the double-null-terminated strings used in the OPENFILENAME
struct. Each item in the list is described by a COMDLG_FILTERSPEC
struct:
struct COMDLG_FILTERSPEC
{
LPCWSTR pszName;
LPCWSTR pszSpec;
};
pszName
contains the string to show in the combo box, for example "Text files". pszSpec
is the wildcard pattern to use with that item, for example "*.txt".
Here are the setup steps to create a file dialog COM object and initialize it:
void CMainDlg::OnFileOpen()
{
HRESULT hr;
CComPtr<IFileOpenDialog> pDlg;
COMDLG_FILTERSPEC aFileTypes[] = {
{ L"Text files", L"*.txt" },
{ L"Executable files", L"*.exe;*.dll" },
{ L"All files", L"*.*" }
};
hr = pDlg.CoCreateInstance ( __uuidof(FileOpenDialog) );
if ( FAILED(hr) )
return;
pDlg->SetFileTypes ( _countof(aFileTypes), aFileTypes );
pDlg->SetTitle ( L"A Single-Selection Dialog" );
After the initialization is done, we call Show()
to display the dialog:
hr = pDlg->Show ( m_hWnd );
If hr
contains a success HRESULT
, we call GetResult()
to get an IShellItem
interface on the file that the user selected. To get the path to that file, we call IShellItem::GetDisplayName()
and pass the SIGDN_FILESYSPATH
flag, which indicates that we want the file system path to the item. If the user chose something that isn't a file system object, GetDisplayName()
will fail. It's also our responsibility to free the string that GetDisplayName()
returns.
if ( SUCCEEDED(hr) )
{
CComPtr<IShellItem> pItem;
hr = pDlg->GetResult ( &pItem );
if ( SUCCEEDED(hr) )
{
LPOLESTR pwsz = NULL;
hr = pItem->GetDisplayName ( SIGDN_FILESYSPATH, &pwsz );
if ( SUCCEEDED(hr) )
{
MessageBox ( pwsz );
CoTaskMemFree ( pwsz );
}
}
}
}
Here's the dialog with the file type combo box expanded:
Note that Vista automatically appends the wildcard pattern to the file type description (the pszName
member of COMDLG_FILTERSPEC
). This is different from earlier OSes, where it was a convention for the description to include the pattern. If you use a description that contains a pattern, Vista will try to compensate for this by looking at the end of the description. If the pattern appears at the end of the description, Vista will not add the pattern again. For example, if your strings are:
Description: All files (*.*)
Pattern: *.*
Vista will not add another "(*.*)" to the description. The pattern in the description does not have to be in parentheses, however most apps do use parentheses, so that's why Vista checks for them.
Note that the two wildcard patterns must match exactly (not counting the parentheses), so if you use strings such as:
Description: Word files (*.doc, *.docx)
Pattern: *.doc;*.docx
Vista will still append "(*.doc;*.docx)" to the description, because the two wildcard patterns don't match exactly.
Even though the code given above doesn't call SetOptions()
, a few options are on by default. The file-open dialog automatically has FOS_FILEMUSTEXIST
set, so the user cannot enter a filename when that file doesn't exist.
Basic File-Save Dialog Example
The setup steps for a file-save dialog are similar. This example shows how to prompt the user to save some data, with the default format being a text file with a .txt
extension. This example also changes the Save button text using IFileDialog::SetOkButtonLabel()
.
void CMainDlg::OnFileSave()
{
HRESULT hr;
CComPtr<IFileSaveDialog> pDlg;
COMDLG_FILTERSPEC aFileTypes[] = {
{ L"Text files", L"*.txt" },
{ L"All files", L"*.*" }
};
hr = pDlg.CoCreateInstance ( __uuidof(FileSaveDialog) );
if ( FAILED(hr) )
return;
pDlg->SetFileTypes ( _countof(aFileTypes), aFileTypes );
pDlg->SetTitle ( L"A File-Save Dialog" );
pDlg->SetOkButtonLabel ( L"D&o It!" );
pDlg->SetFileName ( L"mystuff.txt" );
pDlg->SetDefaultExtension ( L"txt" );
hr = pDlg->Show ( m_hWnd );
if ( SUCCEEDED(hr) )
{
CComPtr<IShellItem> pItem;
hr = pDlg->GetResult ( &pItem );
if ( SUCCEEDED(hr) )
{
LPOLESTR pwsz = NULL;
hr = pItem->GetDisplayName ( SIGDN_FILESYSPATH, &pwsz );
if ( SUCCEEDED(hr) )
{
CoTaskMemFree ( pwsz );
}
}
}
}
Here's the dialog, showing the default file name:
As with the file-open dialog, the file-save dialog has some options on by default. Among these is FOS_PATHMUSTEXIST
, so the user must enter a directory name that already exists.
Multi-Select File Open Dialog Example
Finally, here's an example that uses a multi-select file-open dialog. The setup steps are mostly the same as in the previous file-open example. To allow multiple selection, we set the FOS_ALLOWMULTISELECT
flag using SetOptions()
. Notice that we have to call GetOptions()
first and then add FOS_ALLOWMULTISELECT
, because some options are on by default, as mentioned earlier.
CComPtr<IFileOpenDialog> pDlg;
DWORD dwFlags = 0;
pDlg->GetOptions ( &dwFlags );
pDlg->SetOptions ( dwFlags | FOS_ALLOWMULTISELECT );
hr = pDlg->Show ( m_hWnd );
If Show()
succeeds, we call GetResults()
to get an array containing all the selected files. We get the size of that array with IShellItemArray::GetCount()
, the loop through and get the path of each selected file.
if ( SUCCEEDED(hr) )
{
CComPtr<IShellItemArray> pItemArray;
hr = pDlg->GetResults ( &pItemArray );
if ( SUCCEEDED(hr) )
{
DWORD cSelItems;
hr = pItemArray->GetCount ( &cSelItems );
if ( SUCCEEDED(hr) )
{
for ( DWORD j = 0; j < cSelItems; j++ )
{
CComPtr<IShellItem> pItem;
hr = pItemArray->GetItemAt ( j, &pItem );
if ( SUCCEEDED(hr) )
{
LPOLESTR pwsz = NULL;
hr = pItem->GetDisplayName ( SIGDN_FILESYSPATH, &pwsz );
if ( SUCCEEDED(hr) )
{
MessageBox ( pwsz );
CoTaskMemFree ( pwsz );
}
}
}
}
}
}
Handling File Dialog Events
Prior to Vista, an app could be notified of file dialog events by specifying a callback function in the lpfnHook
member of the OPENFILENAME
struct. In Vista, the app creates a COM object that implements the IFileDialogEvents
interface, and passes that interface to the file dialog. When certain events occur, the file dialog calls IFileDialogEvents
methods to let the app act on the events.
The IFileDialogEvents Interface
IFileDialogEvents
has seven methods that are called when various events occur:
OnFolderChanging()
, OnFolderChange()
- Called before and after the dialog navigates to a different folder. If the app wants to prevent the change,
OnFolderChanging()
can do so.
OnSelectionChange()
- Called after the user selects a different file in the file list.
OnTypeChange()
- Called when the user changes the selection in the file types combo box.
OnFileOk()
- Called when the user selects a file and clicks the Open or Save button. The app can prevent the file dialog from closing by returning
S_FALSE
from this method.
OnOverwrite()
- Called by a file-save dialog if the user selects a file that already exists. The app can show its own UI in this method, or let the dialog show the default UI.
OnShareViolation()
- Called when a sharing error occurs during an open or save operation. The app can show its own UI in this method, or let the dialog show the default UI.
Handling Events
We can handle events by creating a new C++ class for a COM object that implements IFileDialogEvents
:
class CDlgEventHandler :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CDlgEventHandler>,
public IFileDialogEvents
{
public:
CDlgEventHandler();
~CDlgEventHandler();
BEGIN_COM_MAP(CDlgEventHandler)
COM_INTERFACE_ENTRY(IFileDialogEvents)
END_COM_MAP()
STDMETHODIMP OnFileOk(IFileDialog* pfd);
STDMETHODIMP OnFolderChanging(IFileDialog* pfd, IShellItem* psiFolder);
STDMETHODIMP OnFolderChange(IFileDialog* pfd);
STDMETHODIMP OnSelectionChange(IFileDialog* pfd);
STDMETHODIMP OnShareViolation(IFileDialog* pfd, IShellItem* psi,
FDE_SHAREVIOLATION_RESPONSE* pResponse);
STDMETHODIMP OnTypeChange(IFileDialog* pfd);
STDMETHODIMP OnOverwrite(IFileDialog* pfd, IShellItem* psi,
FDE_OVERWRITE_RESPONSE* pResponse);
};
I'll show one method here, OnFolderChanging()
, that prints a trace message showing the new folder path. You can look at the sample project to see the remaining methods, all of which print similar trace messages.
bool PathFromShellItem (
IShellItem* pItem, CString& sPath )
{
HRESULT hr;
LPOLESTR pwsz = NULL;
hr = pItem->GetDisplayName ( SIGDN_FILESYSPATH, &pwsz );
if ( FAILED(hr) )
return false;
sPath = pwsz;
CoTaskMemFree ( pwsz );
return true;
}
STDMETHODIMP CDlgEventHandler::OnFolderChanging (
IFileDialog* pfd, IShellItem* psiFolder )
{
CString sPath;
ATLTRACE("OnFolderChanging called\n");
if ( PathFromShellItem ( psiFolder, sPath ) )
ATLTRACE("Changing to folder: %ls\n", (LPCWSTR) sPath);
return S_OK;
}
Now that we have this class, we need to tell the file dialog that we want to get events. Let's go back to the first file-open dialog example and add an event handler. The new code is shown in bold:
void CMainDlg::OnFileOpen()
{
HRESULT hr;
CComPtr<IFileOpenDialog> pDlg;
hr = pDlg.CoCreateInstance ( __uuidof(FileOpenDialog) );
if ( FAILED(hr) )
return;
CComObjectStackEx<CDlgEventHandler> obj;
CComQIPtr<IFileDialogEvents> pEvents = obj.GetUnknown();
DWORD dwCookie;
bool bAdvised;
hr = pDlg->Advise ( pEvents, &dwCookie );
bAdvised = SUCCEEDED(hr);
hr = pDlg->Show ( m_hWnd );
if ( bAdvised )
pDlg->Unadvise ( dwCookie );
}
We first create a CDlgEventHandler
object, using the CComObjectStackEx
template class to manage the COM object's lifetime. (CComObjectStackEx
is like CComObjectStack
, except it supports QueryInterface()
.) We then call Advise()
and pass it an IFileDialogEvents
interface on our CDlgEventHandler
object. If Advise()
succeeds, we clean up by calling Unadvise()
after Show()
returns.
File Dialog Customization
Before Vista, customizing a file dialog was done via a tricky process that involved creating a custom dialog template. When an app used this method, it was tightly coupled to a particular version of the common dialog, which meant that the app could not take advantage of features introduced in later versions of Windows, such as the Places bar and resizeable dialogs. For example, this is how the file-open dialog looks in Paint Shop Pro 5:
Every element in the dialog, aside from the list view control, still looks like the original Windows 95 dialog. Windows can't automatically enable newer features, because doing so would change the layout of the dialog and break PSP's custom template.
Vista supports customization of the new file dialogs, but there are two main differences compared to the old method:
- Customization is done through a COM interface, which does not expose the app to any internal details of the file dialog. Presumably, customizations written today will work in future versions of Windows that have different file dialogs.
- An app can only add a set of pre-defined controls and text labels, instead of arbitrary UI elements.
The Control System
There are a few simple controls available to applications: static text controls, push buttons, check boxes, edit boxes, and separators. There are also three controls that are containers for other items: radio button groups, combo boxes, and buttons that show popup menus. There is also a feature called a visual group, which is a way to group controls together in the dialog. A visual group has a text label and can contain other controls (including controls that are themselves containers).
Once a control has been added, it can be modified only in limited ways. A control can be enabled or disabled, hidden or shown, and an item in a container can be selected. Containers have a little more flexibility, in that their contents can be changed at any time. Controls are identified by DWORD
identifiers that the app manages.
Adding Simple Controls
To add controls, we begin by creating the file dialog COM object as we saw earlier, then querying it for the IFileDialogCustomize
interface. We can add controls using these methods:
AddCheckButton()
- Adds a check box. The initial state of the check box can be checked or unchecked, as the app desires.
AddEditBox()
- Adds an edit box. The app can also pass a string, which will be used for the edit box's initial text.
AddPushButton()
- Adds a button.
AddSeparator()
- Adds a separator, an etched line similar to the separators used in menus.
AddText()
- Adds a static text control.
Here is an example of how to add a static text control and a button:
void CMainDlg::OnFileSave()
{
HRESULT hr;
CComPtr<IFileSaveDialog> pDlg;
hr = pDlg.CoCreateInstance ( __uuidof(FileSaveDialog) );
if ( FAILED(hr) )
return;
CComQIPtr<IFileDialogCustomize> pfdc = pDlg;
if ( !pfdc )
return;
pfdc->AddText(1000, L"A label");
pfdc->AddPushButton(1001, L"My Button");
}
Here's how the save dialog looks with these two additional controls:
Adding Container Controls
In order to add a container control, we first call AddComboBox()
, AddRadioButtonList()
, or AddMenu()
to create the container. Then we call AddControlItem()
once for each item to be shown in the container. Here is an example showing how to add a menu button:
CComPtr<IFileOpenDialog> pDlg;
CComQIPtr<IFileDialogCustomize> pfdc = pDlg;
const DWORD dwMenuID = 1100;
if ( !pfdc )
return;
pfdc->AddMenu(dwMenuID, L"A new menu button");
pfdc->AddControlItem(dwMenuID, 1101, L"Menu command 1");
pfdc->AddControlItem(dwMenuID, 1102, L"Menu command 2");
And here's how the button looks with the popup menu displayed:
Radio buttons and combo boxes are handled a bit differently. AddComboBox()
and AddRadioButtonList()
do not have a string parameter, since there is no label for those controls. Also, we can call SetSelectedControlItem()
to select one item in the container. Here is a snippet that adds a radio button group:
CComPtr<IFileOpenDialog> pDlg;
CComQIPtr<IFileDialogCustomize> pfdc = pDlg;
const DWORD dwRadioGroupID = 1200;
if ( !pfdc )
return;
pfdc->AddRadioButtonList(dwRadioGroupID);
pfdc->AddControlItem(dwRadioGroupID, 1201, L"Veronica");
pfdc->AddControlItem(dwRadioGroupID, 1202, L"Mars");
pfdc->SetSelectedControlItem(dwRadioGroupID, 1202);
And here's the dialog with the two additional radio buttons:
Using Visual Groups
The controls in a visual group are positioned together in the dialog. The group also has a label, which can be used to convey what the controls will do or what feature they control. To create a visual group, we start by calling StartVisualGroup()
, passing the group's ID and label. All controls that are added are automatically put into that group, until the call to EndVisualGroup()
that completes the group.
CComPtr<IFileOpenDialog> pDlg;
CComQIPtr<IFileDialogCustomize> pfdc = pDlg;
const DWORD dwRadioGroupID = 1200,
dwVisualGroupID = 1300;
if ( !pfdc )
return;
pfdc->StartVisualGroup(dwVisualGroupID, L"Favorite show?");
pfdc->AddRadioButtonList(dwRadioGroupID);
pfdc->AddControlItem(dwRadioGroupID, 1301, L"The Daily Show");
pfdc->AddControlItem(dwRadioGroupID, 1302, L"The Colbert Report");
pfdc->SetSelectedControlItem(dwRadioGroupID, 1302);
pfdc->EndVisualGroup();
Here's the dialog with the radio buttons in a group:
Making a Control Prominent
One additional option is to make a control prominent. Doing so moves the control down next to the Open or Save button. However, not all controls can be moved this way. Only check boxes, buttons, combo boxes, and menu buttons can be made prominent. A visual group that contains only one such control can also be made prominent. We can make the push button control prominent with this call:
pfdc->AddPushButton(1001, L"My Button");
pfdc->MakeProminent(1001);
The button is then moved down next to Save:
Note that if an app adds only one control to the dialog, and it is a type of control that can be made prominent, it will automatically be made prominent.
Handling Events From Additional Controls
IFileDialogControlEvents
has four methods that are called when various events occur that relate to the controls you add with IFileDialogCustomize
:
OnButtonClicked()
- Called when a push button is clicked.
OnCheckButtonToggled()
- Called when a check box is clicked.
OnControlActivating()
- Called when a combo box is opened or a menu button is clicked (to show the popup menu).
OnItemSelected()
- Called when a radio button, combo box item, or popup menu item is clicked.
The sample project implements IFileDialogControlEvents
and IFileDialogEvents
in the same C++ class. Even though we pass a IFileDialogEvents
interface to Advise()
, the file dialog will query for IFileDialogControlEvents
when necessary.
You can see these event handlers in action in the sample project. I kind of went overboard and added every possible type of control to the dialog. You wouldn't do this in real code, of course, but it does make for a good demo.
Other File Dialog Features
Setting the Initial Folder
Vista keeps some state information about the file dialogs that an app shows. One thing that is recorded is the folder that the most-recently-used file dialog was displaying. By default, the next file dialog the app shows will begin in that same folder. An app can override this default by calling IFileDialog::SetFolder()
before Show()
.
This code snippet shows how to make the dialog begin in the My Pictures directory. It uses two new Vista APIs, SHGetKnownFolderPath()
and SHCreateItemFromParsingName()
. SHGetKnownFolderPath()
is a replacement for SHGetSpecialFolderPath()
, and SHCreateItemFromParsingName()
takes the folder path and creates an IShellItem
that we can pass to SetFolder()
.
CComPtr<IFileOpenDialog> pDlg;
CComPtr<IShellItem> psiFolder;
LPWSTR wszPath = NULL;
hr = SHGetKnownFolderPath ( FOLDERID_Pictures, KF_FLAG_CREATE,
NULL, &wszPath );
if ( SUCCEEDED(hr) )
{
hr = SHCreateItemFromParsingName ( wszPath, NULL,
IID_PPV_ARGS(&psiFolder) );
if ( SUCCEEDED(hr) )
pDlg->SetFolder ( psiFolder );
CoTaskMemFree ( wszPath );
}
Keeping State Data
If your app has many load/save features, beyond simple documents, you might want to keep file dialog state data separately for each feature. You can do this by creating a GUID for each state you want to keep, and calling IFileDialog::SetClientGuid()
before Show()
.
CComPtr<IFileOpenDialog> pDlg;
static const GUID guidFileOpen = {
0x7D5FE367, 0xE148, 0x4A96, { 0xB3, 0x26, 0x42, 0xEF,
0x23, 0x7A, 0x36, 0x62 } };
hr = pDlg->SetClientGuid ( guidFileOpen );
Using the Sample Project
The sample project is a dialog-based app that lets you show a file-open dialog in three ways: the GetOpenFileName()
API, WTL's CFileDialog
, and the IFileOpenDialog
interface. The files you select in those dialogs are shown in the dialog's list control.
The fourth button shows a file-save dialog, and if you select a file, the app writes all the filenames in the list control to that file. This dialog shows a more real-world usage of customization: it has a combo box where you can select the encoding to be used for the text file.
References
Copyright and License
This article is copyrighted material, ©2006 by Michael Dunn. I realize this isn't going to stop people from copying it all around the 'net, but I have to say it anyway. If you are interested in doing a translation of this article, please email me to let me know. I don't foresee denying anyone permission to do a translation, I would just like to be aware of the translation so I can post a link to it here.
The demo code that accompanies this article is released to the public domain. I release it this way so that the code can benefit everyone. (I don't make the article itself public domain because having the article available only on CodeProject helps both my own visibility and the CodeProject site.) If you use the demo code in your own application, an email letting me know would be appreciated (just to satisfy my curiosity about whether folks are benefiting from my code) but is not required. Attribution in your own source code is also appreciated but not required.
Revision History
- December 7, 2006: Article first published.
- December 26, 2006: Code tested on the RTM Vista build, intro updated accordingly.
Series navigation: « Monitoring the Computer's Power Status | Showing Friendly Messages with Task Dialogs »