Changing default file open/save dialogs

Before I tell you how to change the default behaviour, lets understand how MFC displays the file open and file save dialogs.
When you select the menu File Open, the command message is routed to CWinApp::OnFileOpen, which calls CDocManager::OnFileOpen through its member m_pDocManager (a pointer to a CDocManager object). This last function calls the CDocManager virtual member DoPromptFileName, and on success calls CWinApp::OpenDocumentFile, with the selected document path. The file open dialog is displayed in the DoPromptFileName virtual function.
When saving, the File Save (or Save As) command message is routed to the CDocument::OnFileSave (or CDocument::OnFileSaveAs). In boths cases the CDocument::DoSave function gets called, either with a file name (we are saving an opened document) or NULL parameter (new document or saving as). At last, if the file name is NULL, CDocument::DoSave calls CWinApp::DoPromptFileName, that validates the m_pDocManager member and calls CDocManager::DoPromptFileName to display the Save As dialog.
As we see, the CDocManager::DoPromptFileName function (which is virtual!) is responsible for displaying the standard open and save dialogs (a boolean parameter decides which one).


It seems trivial now, that to change default behaviour, you have to override the DoPromptFileName function in the CDocManager class, and somehow tell the application class to use your own modified CDocManager, instead of the original class. Below is the code for a custom CDocManager that displays a dialog descending from CFileDialog:


// CDocManager class declaration
//

class CDocManagerEx : public CDocManager
{
DECLARE_DYNAMIC(CDocManagerEx)

// Construction
public:
CDocManagerEx();

// Attributes
public:

// Operations
public:

// Overrides
// helper for standard commdlg dialogs
virtual BOOL DoPromptFileName(CString& fileName, UINT nIDSTitle,
DWORD lFlags, BOOL bOpenFileDialog, CDocTemplate* pTemplate);

// Implementation
public:
virtual ~CDocManagerEx();
};

// DocManager.cpp : implementation file
//

#include “stdafx.h”
#include “PreviewFileDlg.h”
#include “DocManager.h” // the header with the class declaration

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

static void AppendFilterSuffix(CString& filter, OPENFILENAME& ofn,
CDocTemplate* pTemplate, CString* pstrDefaultExt)
{
ASSERT_VALID(pTemplate);
ASSERT_KINDOF(CDocTemplate, pTemplate);

CString strFilterExt, strFilterName;
if (pTemplate->GetDocString(strFilterExt, CDocTemplate::filterExt) &&
!strFilterExt.IsEmpty() &&
pTemplate->GetDocString(strFilterName, CDocTemplate::filterName) &&
!strFilterName.IsEmpty())
{
// a file based document template – add to filter list
#ifndef _MAC
ASSERT(strFilterExt[0] == ‘.’);
#endif
if (pstrDefaultExt != NULL)
{
// set the default extension
#ifndef _MAC
*pstrDefaultExt = ((LPCTSTR)strFilterExt) + 1; // skip the ‘.’
#else
*pstrDefaultExt = strFilterExt;
#endif
ofn.lpstrDefExt = (LPTSTR)(LPCTSTR)(*pstrDefaultExt);
ofn.nFilterIndex = ofn.nMaxCustFilter + 1; // 1 based number
}

// add to filter
filter += strFilterName;
ASSERT(!filter.IsEmpty()); // must have a file type name
filter += (TCHAR)’’; // next string please
#ifndef _MAC
filter += (TCHAR)’*’;
#endif
filter += strFilterExt;
filter += (TCHAR)’’; // next string please
ofn.nMaxCustFilter++;
}
}

/////////////////////////////////////////////////////////////////////////////
// CDocManagerEx

IMPLEMENT_DYNAMIC(CDocManagerEx, CDocManager)

CDocManagerEx::CDocManagerEx()
{
}

CDocManagerEx::~CDocManagerEx()
{
}

BOOL CDocManagerEx::DoPromptFileName(CString& fileName, UINT nIDSTitle, DWORD lFlags, BOOL bOpenFileDialog, CDocTemplate* pTemplate)
{
CPreviewFileDlg dlgFile(bOpenFileDialog); // this is the only modified line!

CString title;
VERIFY(title.LoadString(nIDSTitle));

dlgFile.m_ofn.Flags |= lFlags;

CString strFilter;
CString strDefault;
if (pTemplate != NULL)
{
ASSERT_VALID(pTemplate);
AppendFilterSuffix(strFilter, dlgFile.m_ofn, pTemplate, &strDefault);
}
else
{
// do for all doc template
POSITION pos = m_templateList.GetHeadPosition();
BOOL bFirst = TRUE;
while (pos != NULL)
{
CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetNext(pos);
AppendFilterSuffix(strFilter, dlgFile.m_ofn, pTemplate,
bFirst ? &strDefault : NULL);
bFirst = FALSE;
}
}

// append the “*.*” all files filter
CString allFilter;
VERIFY(allFilter.LoadString(AFX_IDS_ALLFILTER));
strFilter += allFilter;
strFilter += (TCHAR)’’; // next string please
#ifndef _MAC
strFilter += _T(“*.*”);
#else
strFilter += _T(“****”);
#endif
strFilter += (TCHAR)’’; // last string
dlgFile.m_ofn.nMaxCustFilter++;

dlgFile.m_ofn.lpstrFilter = strFilter;
#ifndef _MAC
dlgFile.m_ofn.lpstrTitle = title;
#else
dlgFile.m_ofn.lpstrPrompt = title;
#endif
dlgFile.m_ofn.lpstrFile = fileName.GetBuffer(_MAX_PATH);

BOOL bResult = dlgFile.DoModal() == IDOK ? TRUE : FALSE;
fileName.ReleaseBuffer();
return bResult;
}


The code was borrowed completely from original MFC sources, only one line had to be modified: the dialog declaration (of course, this is because the example dialog is a CFileDialog descendant, otherwise you would have to make some more modifications). For a standard dialog with preview look in the dialogs section.
The AppendFilterSuffix function was called from our DoPromptFileName function and was copied verbatim from the MFC sources.
The only thing that remains to be said about this class is that if you want to use different dialogs for opening and saving, then you can do so using the bOpenFileDialog parameter, which is TRUE when opening and FALSE otherwise.


We still have to make our application class to use the new CDocManagerEx, instead of the default CDocManager. The CWinApp uses the document manager through its m_pDocManager member, so all we have to do is initializate this member correctly. A closer look to the MFC code shows that CWinApp creates the object to which this member points only in the CWinApp::AddDocTemplate, a function that normally is called only in our override of the CWinApp::InitInstance function. The CWinApp::AddDocTemplate function only creates the CDocManager object if the m_pDocManager member is NULL, thus once the m_pDocManager pointer is correctly initialized, the CWinApp::AddDocTemplate function can be safely called.
So, the final step is to initialize the m_pDocManager member in our InitInstance override before we call CWinApp::AddDocTemplate (you can choose not to call CWinApp::AddDocTemplate at all and call directly m_pDocManager->AddDocTemplate).
Here is the code to do that:


BOOL COurApp::InitInstance()
{
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.

#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif

// Change the registry key under which our settings are stored.
// You should modify this string to be something appropriate
// such as the name of your company or organization.
SetRegistryKey(_T(“Local AppWizard-Generated Applications”));

LoadStdProfileSettings(); // Load standard INI file options (including MRU)

// Register the application’s document templates. Document templates
// serve as the connection between documents, frame windows and views.

CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_DIBTYPE,
RUNTIME_CLASS(COurDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(COurView));
ASSERT(m_pDocManager == NULL);
m_pDocManager = new CDocManagerEx;
m_pDocManager->AddDocTemplate(pDocTemplate); // or just AddDocTemplate(pDocTemplate);

// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;

// Enable drag/drop open
m_pMainWnd->DragAcceptFiles();

// Enable DDE Execute open
EnableShellOpen();
RegisterShellFileTypes(TRUE);

// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);

// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;

// The main window has been initialized, so show and update it.
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();

return TRUE;
}


It was all we needed to change the default file open and file save dialogs. In the samples section you can find an enhanced DIBLOOK sample that uses this technique to show standard file dialogs with preview.

Last updated: 25 June 1998

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read