The Problem
Basically, I got sick of having to navigate from "My Documents" every time I opened an app. One of my projects requires several instances to be open simultaneously and the extra folder-hopping just got to me.
The Solution
I created a class to replace the existing CDocManager
class in my MFC Doc/View projects. The new class, CRegDocMgr
(based on CDocManager
), stores the most recently used directory (MRUD) in the Registry—in HKEY_CURRENT_USER
, along with any other information normally stored there by the MFC framework. It adds the string "MRUD"
to the "Settings"
key. So the whole path would be HKEY_CURRENT_USER/MyCompany/MyApp/Settings/MRUD
.
How It Works
The new doc manager replaces the original in the app's InitInstance
function— after specifying standard Registry settings, but before the app registers its document templates. The constructor of CRegDocMgr
pulls the MRUD from the Registry and keeps it in the CString m_strInitialMRUD
. Each time a file is saved or opened (including MRU/drag-and-drop files), m_strMRUD
(which was initialized to m_strInitialMRUD
) is updated. When the destructor for CRegDocMgr
is called just before the program closes, it writes m_strMRUD
to the registry if it differs from m_strInitialMRUD
(avoiding unnecessary registry access).
How To Use It
Only two lines beyond the normal inclusion of a class are needed for basic usage. First, as is usual, add CRegDocMgr.cpp and CRegDocMgr.h to your project. In the main app implementation file (e.g. MyApp.cpp), be sure to #include "RegDocMgr.h"
. Then, in the InitInstance
function for the app, add two lines after the registry init code, but before any document templates are registered.
MyApp.cpp : Defines the class behavioyrs for the application.
...
#include "RegDocMgr.h"
...
... standard InitInstance stuff ...
SetRegistryKey(_T("Your Company Name"));
LoadStdProfileSettings();
...
ASSERT(m_pDocManager == NULL);
m_pDocManager = new CRegDocMgr;
... and the rest continues ...
CSingleDocTemplate* pDocTemplate;
"That's all there is to it." The Open and SaveAs file dialogs will behave exactly the same as before (including IDR_MAINFRAME
filters), except that they will start in the MRUD.
Extended Functions
The class also allows you to access the MRUD from elsewhere in your program. This is useful if you have import or export functions in your app that could benefit from MRUD access. You have access to the MRUD initially loaded from the registry (CString CRegDocMgr::GetInitialMRUD()
) and to the current MRUD (CString CRegDocMgr::GetCurrentMRUD()
). You may also set the MRUD by passing it a complete filename (CString CRegDocMgr::SetMRUD(CString csFileName)
). The class will extract the path and set it as the MRUD.
To use these functions in an import/export, add CRegDocMgr
to your app as noted above; then, in the import/export sections of your code, add a few more lines to access the MRUD before showing your import/export file dialog. Afterward, you may also set the MRUD to reflect the import/export directory used, if you so choose.
...
#include "RegDocMgr.h"
...
void MyAppView::OnExport()
{
MyAppDoc* pDoc = GetDocument();
CString csTitle = pDoc->GetTitle();
if (csTitle != "Untitled")
csTitle = csTitle.Left(csTitle.GetLength()-4);
static char szFilter[] = "ABC Files
(*.abc)|*.abc; *.abc|All Files (*.*)|*.*||";
CFileDialog dlgExport(false, "abc",
(LPCTSTR) csTitle, OFN_HIDEREADONLY |
OFN_OVERWRITEPROMPT, szFilter,
AfxGetMainWnd());
dlgExport.m_ofn.lpstrTitle = "Export to file";
(*.abc)|*.abc; *.abc||";
// CFileDialog dlgImport(true, "abc", NULL,
// OFN_HIDEREADONLY | OFN_PATHMUSTEXIST |
// OFN_FILEMUSTEXIST, szFilter, AfxGetMainWnd());
///////////////////////////////////////////////
// set a starting directory if one exists
CRegDocMgr* pDocMgr = (CRegDocMgr*)
AfxGetApp()->m_pDocManager;
dlgExport.m_ofn.lpstrInitialDir =
pDocMgr->GetCurrentMRUD();
///////////////////////////////////////////////
// show the file dialog
if (dlgExport.DoModal() == IDOK)
{
/////////////////////////////////////////////
// omit this if import/export should not
// affect document MRUD
pDocMgr->SetMRUD(dlgExport.m_ofn.lpstrFile);
/////////////////////////////////////////////
//... proceed with the import/export ... etc
Testing
The project includes a small text file named "TestFile.xyz". Copy/Move it to wherever you'd like to test the app—open the app, open the file, close the app, re-open the app, and go to File/Open. The dialog should start in the directory from which you last loaded "TestFile.xyz". There is also an "Export" option on the File menu. It will create a meaningless text file, but you should be able to see that it opens to the MRUD and updates the MRUD, should you save the exported file.
Why It Works
CRegDocMgr
is 99% CDocManager
. Only two virtual functions, "DoPromptFileName
" and "OpenDocumentFile
", have been modified from the original MFC code. DoPromptFileName
is called whenever the file dialog is opened for Open/SaveAs and OpenDocumentFile
is used to catch MRU files opened from the menu bar and when files are dropped on the app.
Miscellaneous
You can remove the "All Files (*.*)" filter from your file dialogs if you define _HIDEALLFILTER
. The class then skips that extra section of code which adds *.* to the filters.
The registry accesses have all been made unicode-safe using the _T macro
, to match the rest of the MFC unicode-enabled code.
I included options for _MAC
, but have no way to test them.
The AppendFilterSuffix
function refused to behave unless I included it in RegDocMgr.cpp too.
Thanks
Thanks to Jorge Lodos, whose CodeGuru article "Changing the default file open/save dialogs in an MFC doc/view application" pointed me in the right direction.
Revision History
26 Sep 2002 - Initial Revision