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

    技术2022-05-19  22

    一、问题的提出 我建立了一个名为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类。


    最新回复(0)