Introduction
If you have a MDI application, it is not uncommon to also have several document classes. The MFC's default implementation of the open file dialog has two shortcomings that your users may find annoying: it only shows one file type at a time, and does not allow to open multiple files at once. The class explained in this article solves these issues.
Using the code
Inside the class
If you take a peek under the CWinApp
hood, you will find that it delegates file related functions to an instance of the undocumented class CDocManager
(Roger Allen has a nice explanation about CDocManager
here). This class is actually responsible for showing the open file dialog and for opening the document, so it is the ideal candidate for our task at hand. So let's get into work.
Showing all file types at once
The first step is to override the CDocManager::DoPromptFileName
function. This function was not really made to be overridden, so in order to make a slight change, you have to copy the base class code and tweak it a little. The following code adds a new filter to the filter list:
BOOL CDocManager2::DoPromptFileName(CString& fileName,
UINT nIDSTitle, DWORD lFlags,
BOOL bOpenFileDialog, CDocTemplate* pTemplate)
{
... original code ...
if (pTemplate != NULL)
{
ASSERT_VALID(pTemplate);
_AfxAppendFilterSuffix(strFilter,
dlgFile.m_ofn, pTemplate, &strDefault);
}
else
{
CString strFilters;
POSITION pos = m_templateList.GetHeadPosition();
while (pos != NULL)
{
CDocTemplate* pTemplate =
(CDocTemplate*)m_templateList.GetNext(pos);
CString strFilterExt;
pTemplate->GetDocString(strFilterExt,CDocTemplate::filterExt);
if(!strFilterExt.IsEmpty())
strFilters+=_T('*')+strFilterExt+_T(';');
}
strFilters.Delete(strFilters.GetLength()-1,1);
CString allDocsFilter;
VERIFY(allDocsFilter.LoadString(IDS_ALLDOCSFILTER));
strFilter += allDocsFilter;
strFilter += (TCHAR)'\0';
strFilter += strFilters;
strFilter += (TCHAR)'\0';
dlgFile.m_ofn.nMaxCustFilter++;
... original code ...
dlgFile.m_ofn.nFilterIndex=0;
INT_PTR nResult = dlgFile.DoModal();
fileName.ReleaseBuffer();
return nResult == IDOK;
}
Unfortunately, this code uses the internal MFC function _AfxAppendFilterSuffix
. To make the code compile, the only solution I found was to copy the code verbatim from MFC source files. Let's hope it does not change a lot in future versions of MFC!
Opening multiple files at once
To open multiple files at once, we must override CDocManager::OnFileOpen
. Apart from adding the obvious OFN_ALLOWMULTISELECT
flag, we must add code to iterate the selected files list and open the documents one by one:
void CDocManager2::OnFileOpen()
{
CString newName;
if (!DoPromptFileName(newName, AFX_IDS_OPENFILE,
OFN_HIDEREADONLY | OFN_FILEMUSTEXIST
| OFN_ALLOWMULTISELECT, TRUE, NULL))
return;
LPSTR szName=newName.GetBuffer();
for(int i=0;i<newName.GetAllocLength();i++)
{
if(_T('\0')==szName[i])
if(_T('\0')!=szName[i+1])
szName[i]=_T('*');
}
newName.ReleaseBuffer();
std::vector<CString> vectorFiles=split(newName,_T("*"));
CString strPath=vectorFiles[0];
if(1==vectorFiles.size())
AfxGetApp()->OpenDocumentFile(strPath);
else
{
for(std::vector<CString>::iterator i=vectorFiles.begin()+1;
i!=vectorFiles.end();i++)
{
AfxGetApp()->OpenDocumentFile(strPath+_T('\\')+*i);
}
}
}
Now, we have a class that behaves as we want. Happy coding!