淺談MFC多文檔視圖中文檔是如何對應多個視圖的?

MfC打開過程詳解及應用                          

  

本文主要介紹:在MFC中,菜單打開命令的響應過程。

一、MFC打開命令的響應過程:

File->Open 對應的ID爲ID_FILE_OPEN,其響應過程如下:

注:如果自己已將ID_FLIE_OPEN在MFC中重載了,則會直接響應重載函數,不會按以下過程響應。

1.點擊File->Open,首先響應的函數爲: CWinApp::OnFileOpen(),其函數原型爲:

  1. void CWinApp::OnFileOpen()  
  2. {  
  3.     ASSERT(m_pDocManager != NULL);  
  4.         m_pDocManager->OnFileOpen();  
  5. }  
void CWinApp::OnFileOpen()
{
	ASSERT(m_pDocManager != NULL);
        m_pDocManager->OnFileOpen();
}

2.由上面的程序可知,接着調用的是: CDocManager::onFileOpen(),該函數功能是:顯示打開文件的對話框,並獲取文件的路徑,其函數原型爲:

  1. void CDocManager::OnFileOpen()  
  2. {  
  3.        // prompt the user (with all document templates)  
  4.     CString newName;  //彈出打開文件的對話框,獲取文件路徑  
  5.     if (!DoPromptFileName(newName, AFX_IDS_OPENFILE,OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, TRUE, NULL))  
  6.         return// open cancelled  
  7.     AfxGetApp()->OpenDocumentFile(newName);  
  8.       // if returns NULL, the user has already been alerted  
  9. }  
void CDocManager::OnFileOpen()
{
       // prompt the user (with all document templates)
	CString newName;  //彈出打開文件的對話框,獲取文件路徑
    if (!DoPromptFileName(newName, AFX_IDS_OPENFILE,OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, TRUE, NULL))
		return; // open cancelled
    AfxGetApp()->OpenDocumentFile(newName);
      // if returns NULL, the user has already been alerted
}


3.接着調用函數: CWinApp::OpenDocumentFile(LPCTSTR lpszFileName),其函數原型爲:

  1. CDocument* CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)  
  2. {  
  3.     ASSERT(m_pDocManager != NULL);  
  4.     return m_pDocManager->OpenDocumentFile(lpszFileName);  
  5. }  
CDocument* CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)
{
    ASSERT(m_pDocManager != NULL);
    return m_pDocManager->OpenDocumentFile(lpszFileName);
}

4.再調用函數: CDocManager::OpenDocumentFile(LPCTSTR lpszFileName),該函數遍歷文檔模板,對每個文檔進行匹配,若該文件已經在某個文檔中打開,則會激活該文檔視圖,否則用匹配的文檔模板,調用下一個打開函數,其原型爲:

  1. CDocument* CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)  
  2. {  
  3.        // find the highest confidence  
  4.     POSITION pos = m_templateList.GetHeadPosition();  
  5.     CDocTemplate::Confidence bestMatch = CDocTemplate::noAttempt;  
  6.     CDocTemplate* pBestTemplate = NULL;  
  7.     CDocument* pOpenDocument = NULL;  
  8.        lstrcpyn(szTemp, lpszFileName, _MAX_PATH);  
  9.        LPTSTR lpszLast = _tcsrchr(szTemp, '"');  
  10.   
  11.        if (lpszLast != NULL)  
  12.               *lpszLast = 0;  
  13.                 
  14.        AfxFullPath(szPath, szTemp);  
  15.        TCHAR szLinkName[_MAX_PATH];  
  16.          
  17.        if (AfxResolveShortcut(AfxGetMainWnd(), szPath, szLinkName, _MAX_PATH))  
  18.               lstrcpy(szPath, szLinkName);  
  19.   
  20.        while (pos != NULL)  
  21.        {  
  22.               CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetNext(pos);  
  23.               ASSERT_KINDOF(CDocTemplate, pTemplate);  
  24.   
  25.               CDocTemplate::Confidence match;  
  26.               ASSERT(pOpenDocument == NULL);  
  27.   
  28.               match = pTemplate->MatchDocType(szPath, pOpenDocument);  
  29.               if (match > bestMatch)  
  30.               {  
  31.                      bestMatch = match;  
  32.                      pBestTemplate = pTemplate;  
  33.               }  
  34.   
  35.               if (match == CDocTemplate::yesAlreadyOpen)  
  36.                      break;      // stop here  
  37.        }  
  38.        if (pOpenDocument != NULL)  
  39.        {  
  40.               POSITION pos = pOpenDocument->GetFirstViewPosition();  
  41.               if (pos != NULL)  
  42.               {  
  43.                      CView* pView = pOpenDocument->GetNextView(pos); // get first one  
  44.                      ASSERT_VALID(pView);  
  45.                      CFrameWnd* pFrame = pView->GetParentFrame();  
  46.                      if (pFrame != NULL)  
  47.                             pFrame->ActivateFrame();  
  48.                      else  
  49.                             TRACE0("Error: Can not find a frame for document to activate.\n");  
  50.   
  51.                      CFrameWnd* pAppFrame;  
  52.                      if (pFrame != (pAppFrame = (CFrameWnd*)AfxGetApp()->m_pMainWnd))  
  53.                      {  
  54.                             ASSERT_KINDOF(CFrameWnd, pAppFrame);  
  55.                             pAppFrame->ActivateFrame();  
  56.                      }  
  57.               }  
  58.               else  
  59.               {  
  60.                      TRACE0("Error: Can not find a view for document to activate.\n");  
  61.               }  
  62.               return pOpenDocument;  
  63.        }  
  64.        if (pBestTemplate == NULL)  
  65.        {  
  66.               AfxMessageBox(AFX_IDP_FAILED_TO_OPEN_DOC);  
  67.               return NULL;  
  68.        }  
  69.        return pBestTemplate->OpenDocumentFile(szPath);  
  70. }  
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;
       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);
}

5.調用函數:CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible),該函數是多文檔打開函數,先創建文檔的框架窗口,然後判斷路徑是否爲空,如果爲空,則重新設置文檔路徑;最後,調用InitialUpdateFrame顯示框架窗口。其原型爲:

  1. CDocument* CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible)  
  2. {  
  3.        CDocument* pDocument = CreateNewDocument();  
  4.        if (pDocument == NULL)  
  5.        {  
  6.               TRACE0("CDocTemplate::CreateNewDocument returned NULL.\n");  
  7.               AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);  
  8.               return NULL;  
  9.        }  
  10.   
  11.        ASSERT_VALID(pDocument);  
  12.   
  13.        BOOL bAutoDelete = pDocument->m_bAutoDelete;  
  14.        pDocument->m_bAutoDelete = FALSE;   // don't destroy if something goes wrong  
  15.        CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL);  
  16.        pDocument->m_bAutoDelete = bAutoDelete;  
  17.        if (pFrame == NULL)  
  18.        {  
  19.               AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);  
  20.               delete pDocument;       // explicit delete on error  
  21.               return NULL;  
  22.        }  
  23.   
  24.        ASSERT_VALID(pFrame);  
  25.   
  26.        if (lpszPathName == NULL)  
  27.        {  
  28.               // create a new document - with default document name  
  29.               SetDefaultTitle(pDocument);  
  30.   
  31.               // avoid creating temporary compound file when starting up invisible  
  32.               if (!bMakeVisible)  
  33.                      pDocument->m_bEmbedded = TRUE;  
  34.   
  35.               if (!pDocument->OnNewDocument())  
  36.               {  
  37.                      // user has be alerted to what failed in OnNewDocument  
  38.                      TRACE0("CDocument::OnNewDocument returned FALSE.\n");  
  39.                      pFrame->DestroyWindow();  
  40.                      return NULL;  
  41.               }  
  42.   
  43.               // it worked, now bump untitled count  
  44.               m_nUntitledCount++;  
  45.        }  
  46.        else  
  47.        {  
  48.               // open an existing document  
  49.               CWaitCursor wait;  
  50.               if (!pDocument->OnOpenDocument(lpszPathName))  
  51.               {  
  52.                      // user has be alerted to what failed in OnOpenDocument  
  53.                      TRACE0("CDocument::OnOpenDocument returned FALSE.\n");  
  54.                      pFrame->DestroyWindow();  
  55.                      return NULL;  
  56.               }  
  57.               pDocument->SetPathName(lpszPathName);  
  58.        }  
  59.   
  60.        InitialUpdateFrame(pFrame, pDocument, bMakeVisible);  
  61.        return pDocument;  
  62. }  
CDocument* CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible)
{
       CDocument* pDocument = CreateNewDocument();
       if (pDocument == NULL)
       {
              TRACE0("CDocTemplate::CreateNewDocument returned NULL.\n");
              AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
              return NULL;
       }

       ASSERT_VALID(pDocument);

       BOOL bAutoDelete = pDocument->m_bAutoDelete;
       pDocument->m_bAutoDelete = FALSE;   // don't destroy if something goes wrong
       CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL);
       pDocument->m_bAutoDelete = bAutoDelete;
       if (pFrame == NULL)
       {
              AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
              delete pDocument;       // explicit delete on error
              return NULL;
       }

       ASSERT_VALID(pFrame);

       if (lpszPathName == NULL)
       {
              // create a new document - with default document name
              SetDefaultTitle(pDocument);

              // avoid creating temporary compound file when starting up invisible
              if (!bMakeVisible)
                     pDocument->m_bEmbedded = TRUE;

              if (!pDocument->OnNewDocument())
              {
                     // user has be alerted to what failed in OnNewDocument
                     TRACE0("CDocument::OnNewDocument returned FALSE.\n");
                     pFrame->DestroyWindow();
                     return NULL;
              }

              // it worked, now bump untitled count
              m_nUntitledCount++;
       }
       else
       {
              // open an existing document
              CWaitCursor wait;
              if (!pDocument->OnOpenDocument(lpszPathName))
              {
                     // user has be alerted to what failed in OnOpenDocument
                     TRACE0("CDocument::OnOpenDocument returned FALSE.\n");
                     pFrame->DestroyWindow();
                     return NULL;
              }
              pDocument->SetPathName(lpszPathName);
       }

       InitialUpdateFrame(pFrame, pDocument, bMakeVisible);
       return pDocument;
}

6.最後調用函數:CDocument::OnOpenDocument(LPCTSTR lpszPathName),該函數一般在“***Doc.cpp”中重載(*** 爲工程名),因爲不同的文件打開的過程不同,所以可以根據需求,在“***Doc.cpp”中改寫OnOpenDocument(LPCTSTR lpszPathName)函數,從而打開相應的文件。其默認生成原型爲:

  1. BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName)  
  2. {  
  3.   
  4.        if (IsModified())  
  5.               TRACE0("Warning: OnOpenDocument replaces an unsaved document.\n");  
  6.   
  7.        CFileException fe;  
  8.        CFile* pFile = GetFile(lpszPathName,CFile::modeRead|CFile::shareDenyWrite, &fe);  
  9.   
  10.        if (pFile == NULL)  
  11.        {  
  12.               ReportSaveLoadException(lpszPathName, &fe,FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);  
  13.               return FALSE;  
  14.        }  
  15.   
  16.        DeleteContents();  
  17.        SetModifiedFlag();  // dirty during de-serialize  
  18.   
  19.        CArchive loadArchive(pFile, CArchive::load | CArchive::bNoFlushOnDelete);  
  20.        loadArchive.m_pDocument = this;  
  21.        loadArchive.m_bForceFlat = FALSE;  
  22.   
  23.        TRY  
  24.        {  
  25.               CWaitCursor wait;  
  26.               if (pFile->GetLength() != 0)  
  27.                      Serialize(loadArchive);     // load me  
  28.               loadArchive.Close();  
  29.               ReleaseFile(pFile, FALSE);  
  30.        }  
  31.        CATCH_ALL(e)  
  32.        {  
  33.               ReleaseFile(pFile, TRUE);  
  34.               DeleteContents();   // remove failed contents  
  35.    
  36.               TRY  
  37.               {  
  38.                      ReportSaveLoadException(lpszPathName, e,FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);  
  39.               }  
  40.               END_TRY  
  41.               DELETE_EXCEPTION(e);  
  42.               return FALSE;  
  43.        }  
  44.        END_CATCH_ALL  
  45.        SetModifiedFlag(FALSE);     // start off with unmodified  
  46.        return TRUE;  
  47. }  
BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName)
{

       if (IsModified())
              TRACE0("Warning: OnOpenDocument replaces an unsaved document.\n");

       CFileException fe;
       CFile* pFile = GetFile(lpszPathName,CFile::modeRead|CFile::shareDenyWrite, &fe);

       if (pFile == NULL)
       {
              ReportSaveLoadException(lpszPathName, &fe,FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);
              return FALSE;
       }

       DeleteContents();
       SetModifiedFlag();  // dirty during de-serialize

       CArchive loadArchive(pFile, CArchive::load | CArchive::bNoFlushOnDelete);
       loadArchive.m_pDocument = this;
       loadArchive.m_bForceFlat = FALSE;

       TRY
       {
              CWaitCursor wait;
              if (pFile->GetLength() != 0)
                     Serialize(loadArchive);     // load me
              loadArchive.Close();
              ReleaseFile(pFile, FALSE);
       }
       CATCH_ALL(e)
       {
              ReleaseFile(pFile, TRUE);
              DeleteContents();   // remove failed contents
 
              TRY
              {
                     ReportSaveLoadException(lpszPathName, e,FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);
              }
              END_TRY
              DELETE_EXCEPTION(e);
              return FALSE;
       }
       END_CATCH_ALL
       SetModifiedFlag(FALSE);     // start off with unmodified
       return TRUE;
}

二、總結

通過上述介紹,MFC打開命令的響應過程如下:

         1.首先調用CWinApp::OnFileOpen(),該函數調用CDocManager::OnFileOpen()函數;

         2.CDocManager::OnFileOpen()顯示打開文件的對話框,並獲取文件的路徑,然後該函數調用CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)函數;

         3.CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)函數直接調用CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)函數;

         4.CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)函數遍歷文檔模板,對每個文檔進行匹配,若該文件已經在某個文檔中打開,則會激活該文檔視圖,否則用匹配的文檔模板,調用CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible)函數;

         5.CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible)函數先創建文檔的框架窗口,然後判斷路徑是否爲空,如果爲空,則重新設置文檔路徑;接着,調用InitialUpdateFrame顯示框架窗口,最後調用CDocument::OnOpenDocument(LPCTSTR lpszPathName)函數;

         6.CDocument::OnOpenDocument(LPCTSTR lpszPathName)函數一般在“***Doc.cpp”中重載(*** 爲工程名),因爲不同的文件打開的過程不同,所以可以根據需求,在“***Doc.cpp”中改寫OnOpenDocument(LPCTSTR lpszPathName)函數,從而打開相應的文件。

三、運用

由於打開命令的響應過程,是一系列函數依次調用,因此,可以調用其中的一個環節,來打開文件。以打開圖片爲例,介紹一下應用:

在打開文件時,彈出的對話框默認的文件“所有文件(*.*)”,如下圖:


有時候我們需要打開特定的文件類型,如BMP、Jpg、Tif等類型,即需要彈出下面的對話框:


該功能實現的過程如下:(***代表工程名)

1.在“***.cpp”中重載ID_FILE_OPEN的響應函數OnFileOpen() ,即對ID_FILE_OPEN在C***App的類中添加一個響應函數,函數名爲OnFileOpen();

注:如果ID_FILE_OPEN已經重載爲:ON_COMMAND(ID_FILE_OPEN, &CWinAppEx::OnFileOpen),要把這行代碼屏蔽,不然點擊打開時,仍默認原來的響應,不會響應自己新重載的函數。

2.編寫C***App::OnFileOpen()函數:

  1. void CMyIMGApp::OnFileOpen()   
  2. {  
  3.     // TODO: Add your command handler code here  
  4.     CString strOpenFilter = "所有文件(*.*)|*.bmp; *.dib; *.gif; *.jpg; *.jpe; *.jpeg; *.tif; *.tiff; *.raw|位圖文件 (*.bmp;*.dib)|*.bmp; *.dib|GIF文件 (*.gif)|*.gif|JPEG文件 (*.jpg;*.jpe;*.jpeg)|*.jpg; *.jpe; *.jpeg|TIFF文件 (*.tif;*.tiff)|*.tif; *.tiff|RAW文件(*.raw)|*.raw|";  
  5.     CFileDialog FileDlg(TRUE, "*.bmp", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, strOpenFilter);  
  6.     if (FileDlg.DoModal() == IDOK)  
  7.         OpenDocumentFile(FileDlg.m_ofn.lpstrFile);  
  8. }  
void CMyIMGApp::OnFileOpen() 
{
	// TODO: Add your command handler code here
	CString strOpenFilter = "所有文件(*.*)|*.bmp; *.dib; *.gif; *.jpg; *.jpe; *.jpeg; *.tif; *.tiff; *.raw|位圖文件 (*.bmp;*.dib)|*.bmp; *.dib|GIF文件 (*.gif)|*.gif|JPEG文件 (*.jpg;*.jpe;*.jpeg)|*.jpg; *.jpe; *.jpeg|TIFF文件 (*.tif;*.tiff)|*.tif; *.tiff|RAW文件(*.raw)|*.raw|";
	CFileDialog FileDlg(TRUE, "*.bmp", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, strOpenFilter);
	if (FileDlg.DoModal() == IDOK)
		OpenDocumentFile(FileDlg.m_ofn.lpstrFile);
}

該函數是直接調用了上述環節的CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)函數,從而實現文件的打開。

說明:點擊打開菜單時,直接的響應函數是自己重載後的函數,通過重載只是改變了前兩個環節,最後通過調用OpenDocumentFile(LPCTSTR lpszFileName)函數,使後面函數依次被調用。

   到此知道了文檔是如何被打開的,接着往下看...                   


當打開一個文檔時,MFC如何選擇所使用的文檔模板對象?

當把一個 文檔名 作爲 變量 來調用CWinApp::OpenDocumentFile()時,MFC是 如何選擇 用來 打開一個給定文檔的 文檔模板對象的呢?

      如果跟蹤CWinApp::OpenDocumentFile()函數的整個調用過程,會發現該函數只是通過存儲在 應用程序對象 中的m_pDocManager指針簡單的調用CDocManager::OpenDocumentFile()函數,後面的這個函數完成選擇與文檔擴展名最匹配的文檔模板對象的全部工作。

      爲了完成這一部分的處理,CDocManager::OpenDocumentFile()函數遍歷它的文檔模板對象表,併爲表的每個成員(成員爲文檔模板對象)調用CDocTemplate::MatchDocType()函數,傳遞的參數是 要打開的 文檔的 文件名。

 

      而CDocTemplate::MatchDocType()執行下面的邏輯:

      (1)遍歷當前打開的文檔對象的列表,查找它們中的哪一個的文件名與我們的變量相同。如果能找到,返回 指向這個文檔對象的指針 和 yesAlreadyOpen確認值。

      (2)如果文件名與所有打開的文檔都不匹配,那麼檢查它的擴展名是否與 文檔模板字符串 中的filterExt字串匹配。如果匹配的話,返回一個yesAttemptNative確認值。否則返回yesAttemptForeign確認值。

 

      在遍歷中,CDocManager::OpenDocumentFile()函數始終保留一個指向文檔模板對象的指針,該對象提供了最後的確認,最後的確認值決定了CDocManager::OpenDocumentFile()函數將要發生的動作。

      @如果文件名與一個已經打開的文檔對象匹配(確認值==yesAlreadyOpen),那麼激活這個文檔的框架窗口和視圖。

      @如果文件的擴展名與某個文檔模板對象的filterExt字串匹配(確認值==yesAlreadyNative),那麼把這個文檔模板對象作爲最好的候選模板來使用,並調用它的OpenDocumentFile()函數。

      @如果文件名的擴展名與任何文檔模板對象的filterExt都不匹配(確認值==yesAlreadyForeign),那麼隨即的選擇第一個註冊過的文檔模板對象作爲最匹配的對象,並調用它的OpenDocumentFile()函數。注意:這種隨即的選擇通常都是錯誤的,在這種情況下,文檔對象的Serialize()函數將可能觸發一個錯誤異常,CDocument::OnOpenDocument()函數的默認句柄將捕獲該異常並顯示一條錯誤信息。

      從上面的分析中可以看出,如果打算把自己的 某個文檔模板對象 作爲所有 擴展名與特殊filterExt字串  不匹配 的文檔的默認模板,那麼該 默認的文檔模板對象 應該是AddDocTemplate()註冊的第一個模板。

     其中核心的CDocManager::OpenDocumentFile()函數的僞代碼如下:

  1. CDocument*  CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)  
  2. {  
  3.       //1、遍歷應用程序的文檔模板列表  
  4.       POSITION pos=m_templateList.GetHeadposition();//文檔模板列表中的第一個文檔模板對象的位置  
  5.       CDocTemplate::Confidence bestMatch = CDocTemplate::noAttempt;  
  6.       CDocTemplate* pBestTemplate = NULL;//爲與 文件 最匹配的 文檔模板 準備的指針  
  7.       CDocument* pOpenDocument=NULL;//保存已經打開的文檔指針,由MatchDocType()函數賦值  
  8.       TCHAR szPath[_MAX_PATH];  
  9.       while(pos != NULL)  
  10.       {  
  11.             CDocTemplate* pTemplate=(CDocTemplate*)m_templateList.GetNext(pos);//每個文檔模板對象的指針                   
  12.             //計算這個文檔模板對象的確認值  
  13.             CDocTemplate::Confidence match;  
  14.             match=pTemplate->MatchDocType(szPath/*想要打開的文件名*/ , pOpenDocument/*已經打開的文檔*/);  
  15.             //存儲到目前位置最好的匹配  
  16.             if(match > bestMatch)  
  17.             {  
  18.                   bestMatch=match;  
  19.                   pBestTemplate=pTemplate;  
  20.             }  
  21.             //如果文件名與一個已經打開的文檔對象匹配,則中斷跳出  
  22.             if(match == CDocTemplate::yesAlreadyOpen)  
  23.                   break;  
  24.       }  
  25.       if(pOpenDocument != NULL)  
  26.       {  
  27.             //如果我們已經找到了一個已經打開的文檔對象,則激活的它的框架窗口和視圖,並返回它的地址  
  28.             //......  
  29.             //激活框架和視圖的代碼省略......  
  30.             //......  
  31.              
  32.             return pOpenDocument;  
  33.       }   
  34.        
  35.       //沒有“最好”的模板,返回Error!  
  36.       if(pBestTemplate == NULL)  
  37.       {  
  38.             AfxMessageBox(AFX_IDP_FAILED_TO_OPEN_DOC);  
  39.             return NULL;  
  40.       }  
  41.       //使用最匹配的文檔模板對象打開給定的文件名  
  42.       return pBestTemplate->OpenDocumentFile(szPath);   
  43. }  

深入挖掘IDR_MAINFRAME

相信每個用過MFC的文檔類的讀者,特別是使用Application Wizard創建的單文檔模板,都能在資源中的String Table(字符串資源)中找到IDR_MAINFRAME這個ID,那麼這是個什麼呢,究竟有什麼神奇的功效?待我們一步一步揭開它神祕的面紗。
 
 
首先,我們可以發現在字符串都是用很多“\n”隔開的,這些字符都用來表示什麼呢?看來它的意義不簡單啊,好的,我們使出第一招---MSDN

所謂期望越大,失望也就越大,在MSDN中竟然沒有它的身影,不得不讓人感到失落,那麼它到底是何方神聖,連生死薄都沒有,看來來歷不小。
看來我們不得不使出第二招---在代碼中搜索,即使MSDN沒有,但是在代碼中總會調用它的,如果沒調用它,我想也沒有什麼特殊的意義了。

  
功夫不負有心人啊,很快就找到它了,這段代碼存在於CWinApp::InitInstance()中,於是我們不禁又要問CSingleDocTemplate又是什麼類呢,我們MSDN它。
  
我們可以得到以下信息:

  
翻譯一下,就是定義一個可以生效一個單文檔接口的文檔模板。它的基類就是CDocTemplate
我們再看它的構造函數:

  
也就是說,在這個類裏傳進去四個參數,根據信息我們可以知道它們分別是字符串資源ID,文檔類指針,窗口框架類指針,視圖類指針。
我們重點關係第一個參數,先查一下nIDResource是用來幹啥的,我們繼續看。
 
 
翻譯一下,指向一個與文檔類型一起用的資源ID,它可能包括菜單,圖標,加速鍵列表,字符串資源。這個字符串資源由多達用‘\n’隔離七個字符子鏈組成,這個字符描繪文檔類型,更多信息請看CDocTemplate::GetDocString()
哦,我們知道了一個大概了,這七個字符串子鏈都用來表示文檔類型,那麼又有問題了,它們都分別表示什麼文檔類型呢?我們把問題放在這裏。(對於後面的三個參數,就是把它們連接起來,由於與本主題關係不大,所以就不深究了。)
現在,我們可以關心一下它的父類,特別是前面提到的那個成員函數GetDocString(),關於它的介紹如下:

 
翻譯一下,獲得一個被文檔類型關聯的字符串。
  
我們重點關心index,這是一個枚舉量,總共有七個成員,有眉目了,這個七個成員想必就與前面提到的問題有關了,我們看描述。

大概說明一下:

CDocTemplate::windowTitle
  
主窗口標題欄上的字符串,(僅在SDI程序出現,MDI程序將以IDR_MAINFRAME字符串爲默認值。)
CDocTemplate::docName
  
缺省文檔的名稱。缺省是無標題。
CDocTemplate::fileNewName
  
文檔類型的名稱。如果應用程序支持多種類型的文檔,此字符串將顯示在"File/New"對話框中。如果沒有指定,就不能夠
CDocTemplate::filterName
  
文檔類型的描述和一個適用於此類型的通配符過濾器。這個字符串將出現在“File/Open”對話框中的文件類型列表框中。要和CDocTemplate::filterExt一起使用。
CDocTemplate::filterExt
  
文檔的擴展名。如果沒有指定,就不能夠在“File/Open”對話框中處理這種文檔。要和CDocTemplate::filterName一起使用。
CDocTemplate::regFileTypeId
  
如果你以::RegisterShellFileTypes向系統的註冊表註冊文件類型,此值會出現在HKEY_CLASSES_ROOT之下成爲其子項,並僅供Windows內部使用。如果沒有指定,這種文件類型就無法註冊。
CDocTemplate::regFileTypeName
  
這也是存儲在註冊表中的文件類型名稱。它會顯示於程序中用以訪問註冊表的對話框內。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章