65.9K
CodeProject is changing. Read more.
Home

Opening multipe documents of several types at once

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.12/5 (9 votes)

May 16, 2004

CPOL

2 min read

viewsIcon

64932

downloadIcon

1335

This class provides an open file dialog that shows multiple file types at once, and allows selecting multiple files at once.

Open dialog showing multiple files types and two selected files

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

  • Add the four files included in the source code to your project.
  • Add a new string to your string table called IDS_ALLDOCSFILTER, this will be the string shown as the filter name.

    the string table of your app

  • Include the extended doc manager header in your CWinApp.cpp file:
    #include "DocManager2.h"
  • Create the doc manager in the first line of your InitInstance CWinApp. Notice that m_pDocManager is a member of the base class.
    BOOL CMyApp::InitInstance()
    {
        m_pDocManager= new CDocManager2();
        ... the rest of your code ...
    }
  • That is it! That was easy! Compile and try to open a file to test the new functionality.

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
    {
        // append all docs filter
        CString strFilters;
        POSITION pos = m_templateList.GetHeadPosition();
        // for each doc template
        while (pos != NULL)
        {
            CDocTemplate* pTemplate = 
                    (CDocTemplate*)m_templateList.GetNext(pos);
            CString strFilterExt;
            // append its extension to the filter list
            pTemplate->GetDocString(strFilterExt,CDocTemplate::filterExt);
            if(!strFilterExt.IsEmpty())
                strFilters+=_T('*')+strFilterExt+_T(';');
        }
        // delete the trailing ;
        strFilters.Delete(strFilters.GetLength()-1,1);

        CString allDocsFilter;
        VERIFY(allDocsFilter.LoadString(IDS_ALLDOCSFILTER));
        strFilter += allDocsFilter;
        strFilter += (TCHAR)'\0';   // next string please
        strFilter += strFilters;
        strFilter += (TCHAR)'\0';   // last string
        dlgFile.m_ofn.nMaxCustFilter++;
        
    ... original code ...
    // select the all documents filter as the default filter
    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()
{
    // prompt the user (with all document templates)
    CString newName;
    if (!DoPromptFileName(newName, AFX_IDS_OPENFILE,
      OFN_HIDEREADONLY | OFN_FILEMUSTEXIST 
      | OFN_ALLOWMULTISELECT, TRUE, NULL))
        return; // open cancelled
    LPSTR szName=newName.GetBuffer();
    // replace all the embebbed nulls, CString can not handle them well
    for(int i=0;i<newName.GetAllocLength();i++)
    {
        if(_T('\0')==szName[i])
            if(_T('\0')!=szName[i+1])
            szName[i]=_T('*');
    }
    newName.ReleaseBuffer();

    // get a vector of all names
    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!