多文档,多种文件类型、文件扩展名下打开合适的文档模板

一、问题的提出
我建立了一个名为MDI的多文档工程,想打开两种格式的文件于是在string table更改如下:
IDR_MDITYPE:/n图像/n新建图像/n图像文件(*.aaa;*.bbb)/n.aaa;.bbb/nMDI.Document/nMDI.Document
IDR_OtherTYPE:/n文本/n新建文本/n文本文件(*.ccc;*.ddd)/n.ccc;.ddd/nMDI.Document/nMDI.Document
并且有三个不一样的菜单IDR_MAINFRAME,IDR_MDITYPE,IDR_OtherTYPE。
同时加了两个新类COtherDoc,COtherView。在CMDIApp::InitInstance()中进行了关联。
现在是问题是:新建时选择"新建图像"则菜单显示的是IDR_MDITYPE的菜单,选择"新建文本"则菜单显示的是IDR_OtherTYPE的菜单,这是正确的。但是当打开一个文件时,无论选择图像文件还是文本文件菜单都是IDR_MDITYPE的菜单。

二、新建与打开的不同
新建的顺序
CWinApp::OnFileNew()//m_pDocManager

{

       CDocManager::OnFileNew()//m_pDocTemplateList

       {

              if(没有文档模板)

                            return;

              if(有多个文档模板)

              {

                            弹出对话框CNewTypeDlg让用户选择;

                            取得模板指针;

              }

              CMultiDocTemplate::OpenDocumentFile()

              {

                            new一个文档;

                            创建子框架;

                            构建frame,doc,view之间的关系;

                            CDocument::OnNewDocument()

                            {

                                   DeleteContents();

                            }

                            InitialUpdateFrame();

                            return pDoc;

              }

       }

}

打开的顺序
CWinApp::OnFileOpen调用CDocManager::OnFileOpen。

CDocManager::OnFileOpen首先显示文件打开对话框(AFX_IDS_OPENFILE),然后调用CWinApp::OpenDocumentFile(FileName)。

CWinApp::OpenDocumentFile(FileName)调用CDocManager::OpenDocumentFile。

CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)遍历文档模板,对每个模板用MatchDocType(szPath,pOpenDocument)匹配文档类型。匹配时主要根据文件扩展名判断。若文件已经在某个文档中打开,则激活文档的第一个视图,否则用匹配的文档模板pBestTemplate->OpenDocumentFile(szPath)。

CDocTemplate::OpenDocumentFile调用CDocument::OnOpenDocument打开文件。

CDocument::OnOpenDocument打开文件,用DeleteContents删除现有文档内容,创建文件对应的CArchive对象loadArchive,Serialize(loadArchive)读文件内容,SetModifiedFlage(FALSE)。

CWinApp::OnFileOpen()

{

       CDocManager::OnFileOpen()

       {

              CDocManager::DoPromptFileName()//弹出打开文件对话框获得文件名(路径)

              {

                            CFileDialog::DoModal();

              }

              CWinApp::OpenDocumentFile()

              {

                     CDocManager::OpenDocumentFile()

                     {对每个模板调用

 

                            CDocTemplate::MatchDocType(szPath,pOpenDocument)匹配文档类型匹配时主要根据文件扩展名判断

                            ...调整视图和框架;

                            CMultiDocTemplate::OpenDocumentFile();

                            {

                                   判断有无现存文档,是否需要保存

                                   新建框架

                                   判断文件是否存在,调用CDocument::OnOpenDocunment/OnNewDocument

                            }

              }

       }

}

通过对比发现,打开文件比新建文件多了CDocManager::OpenDocumentFile()这个过程

CDocument* CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)

{

       // find the highest confidence

       POSITION pos = m_templateList.GetHeadPosition();

       CDocTemplate::Confidence bestMatch = CDocTemplate::noAttempt;

       CDocTemplate* pBestTemplate = NULL;

       CDocument* pOpenDocument = NULL;

 

       TCHAR szPath[_MAX_PATH];

       ASSERT(lstrlen(lpszFileName) < _countof(szPath));

       TCHAR szTemp[_MAX_PATH];

       if (lpszFileName[0] == '/"')

              ++lpszFileName;

       lstrcpyn(szTemp, lpszFileName, _MAX_PATH);

       LPTSTR lpszLast = _tcsrchr(szTemp, '/"');

       if (lpszLast != NULL)

              *lpszLast = 0;

       AfxFullPath(szPath, szTemp);

       TCHAR szLinkName[_MAX_PATH];

       if (AfxResolveShortcut(AfxGetMainWnd(), szPath, szLinkName, _MAX_PATH))

              lstrcpy(szPath, szLinkName);

 

       while (pos != NULL)

       {

              CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetNext(pos);

              ASSERT_KINDOF(CDocTemplate, pTemplate);

 

              CDocTemplate::Confidence match;

              ASSERT(pOpenDocument == NULL);

              match = pTemplate->MatchDocType(szPath, pOpenDocument);

              if (match > bestMatch)

              {

                     bestMatch = match;

                     pBestTemplate = pTemplate;

              }

              if (match == CDocTemplate::yesAlreadyOpen)

                     break;      // stop here

       }

 

       if (pOpenDocument != NULL)

       {

              POSITION pos = pOpenDocument->GetFirstViewPosition();

              if (pos != NULL)

              {

                     CView* pView = pOpenDocument->GetNextView(pos); // get first one

                     ASSERT_VALID(pView);

                     CFrameWnd* pFrame = pView->GetParentFrame();

                     if (pFrame != NULL)

                            pFrame->ActivateFrame();

                     else

                            TRACE0("Error: Can not find a frame for document to activate./n");

                     CFrameWnd* pAppFrame;

                     if (pFrame != (pAppFrame = (CFrameWnd*)AfxGetApp()->m_pMainWnd))

                     {

                            ASSERT_KINDOF(CFrameWnd, pAppFrame);

                            pAppFrame->ActivateFrame();

                     }

              }

              else

              {

                     TRACE0("Error: Can not find a view for document to activate./n");

              }

              return pOpenDocument;

       }

 

       if (pBestTemplate == NULL)

       {

              AfxMessageBox(AFX_IDP_FAILED_TO_OPEN_DOC);

              return NULL;

       }

 

       return pBestTemplate->OpenDocumentFile(szPath);

}

通过以上代码可以看出该函数的作用主要是对CDocManager::DoPromptFileName()函数中弹出的打开文件对话框返回的文件名(lpszFileName)进行一些处理,并通过遍历模板找出最适合该文件的模板,这里有个关键的函数CDocTemplate::MatchDocType()。通过对每个模板调用该函数找到最好的模板。

CDocTemplate::Confidence CDocTemplate::MatchDocType(LPCTSTR lpszPathName,

       CDocument*& rpDocMatch)

{

       ASSERT(lpszPathName != NULL);

       rpDocMatch = NULL;

 

       // go through all documents

       POSITION pos = GetFirstDocPosition();

       while (pos != NULL)

       {

              CDocument* pDoc = GetNextDoc(pos);

              if (AfxComparePath(pDoc->GetPathName(), lpszPathName))

              {

                     // already open

                     rpDocMatch = pDoc;

                     return yesAlreadyOpen;

              }

       }

 

       // see if it matches our default suffix

       CString strFilterExt;

       if (GetDocString(strFilterExt, CDocTemplate::filterExt) &&

         !strFilterExt.IsEmpty())

       {

              // see if extension matches

              ASSERT(strFilterExt[0] == '.');

              LPCTSTR lpszDot = _tcsrchr(lpszPathName, '.');

              if (lpszDot != NULL && lstrcmpi(lpszDot, strFilterExt) == 0)

                     return yesAttemptNative; // extension matches, looks like ours

       }

 

       // otherwise we will guess it may work

       return yesAttemptForeign;

}

该函数的返回类型为枚举类型定义在CdocTemplate中

       enum Confidence

       {

              noAttempt,

              maybeAttemptForeign,

              maybeAttemptNative,

              yesAttemptForeign,

              yesAttemptNative,

              yesAlreadyOpen

       };

通过代码发现该函数首先对所有文档进行文件名的对比,如果相等则返回yesAlreadyOpen,并将当前文档指针传递给CDocManager::OpenDocumentFile()函数中的pOpenDocument(rpDocMatch = pDoc)。若找不到已打开的文档,则通过对比扩展名返回yesAttemptNative或yesAttemptForeign。此处要到了lstrcmpi()函数,由于我的扩展名是”.aaa;.bbb”(对于图像文件),”.ccc;.ddd”(对于文本文件),因此函数总是返回yesAttemptForeign。因此在CDocManager::OpenDocumentFile()中pBestTemplate就只能是图像类型了。

              if (match > bestMatch)

              {

                     bestMatch = match;

                     pBestTemplate = pTemplate;

              }

第一次返回后,match = yesAttemptForeign > bestMatch = noAttempt,之后则相等。

综上,这是由于CDocTemplate::MatchDocType()函数使用了lstrcmpi(),不能对多于一个扩展名的文件进行匹配的缘故。

三、解决方法
为此,自定义CMyMultiDocTemplate类继承自CMultiDocTemplate,只对MatchDocType()函数进行重载。

CMyMultiDocTemplate::Confidence CMyMultiDocTemplate::MatchDocType(LPCTSTR lpszPathName,

       CDocument*& rpDocMatch)

{

       ASSERT(lpszPathName != NULL);

       rpDocMatch = NULL;

 

       // go through all documents

       POSITION pos = GetFirstDocPosition();

       while (pos != NULL)

       {

              CDocument* pDoc = GetNextDoc(pos);

              if (lstrcmpi(pDoc->GetPathName(), lpszPathName) == 0)//AfxComparePath(pDoc->GetPathName(), lpszPathName))此处由于找不到AfxComparePath所在的头文件,存疑。。。

              {

                     // already open

                     rpDocMatch = pDoc;

                     return yesAlreadyOpen;

              }

       }

 

       // see if it matches our default suffix

       CString strFilterExt;

       if (GetDocString(strFilterExt, CDocTemplate::filterExt) &&

         !strFilterExt.IsEmpty())

       {

              // see if extension matches

              ASSERT(strFilterExt[0] == '.');

              LPCTSTR lpszDot = _tcsrchr(lpszPathName, '.');

              int iStart = 0;

              if(lpszDot != NULL)

              {

                     do

                     {

                            CString strExtention = strFilterExt.Tokenize(_T(";"),iStart);//将扩展名分割

                            if(iStart != -1)

                            {

                                   if(lstrcmpi(lpszDot, strExtention) == 0)

                                          return yesAttemptNative;

                            }

                     }while(iStart != -1);

              }

              //if (lpszDot != NULL && lstrcmpi(lpszDot, strFilterExt) == 0)

              //     return yesAttemptNative; // extension matches, looks like ours

       }

 

       // otherwise we will guess it may work

       return yesAttemptForeign;

}

最后在CMDIApp::InitInstance()中的文档模板改为CMyMultiDocTemplate类。

你可能感兴趣的:(多文档,多种文件类型、文件扩展名下打开合适的文档模板)