1. 程式人生 > >MFC文件檢視(二)

MFC文件檢視(二)

 一, 在單文件或者是多文件中,有預設的選單,這裡是這些選單項預設處理,如下:

選單內容

命令項ID

預設的處理函式

預有關聯

File

New

ID_FILE_NEW

CWinApp::OnFileNew

No

Open

ID_FILE_OPEN

CWinApp::OnFileOpen

No

Close(MDI ONLY)

ID_FILE_CLOSE

CDocumemt::OnFileClose

Yes

Save

ID_FILE_SAVE

CDocument:OnFileSave

Yes

Save As

ID_FILE_SAVEAS

CDocument::OnFileSaveAs

Yes

Print

ID_FILE_PRINT

CView::OnFilePrint

No

Print Pre&view

ID_FILE_PRINT_PREVIEW

CView::OnFilePrintPreview

No

Print Setup

ID_FILE_PRINT_SETUP

CWinApp::OnFilePrintSetup

No

“Recent File Name”

ID_FILE_MRU_FILE1~4

CWinApp::OnOpenRecentFile

Yes

Exit

ID_APP_EXIT

CWinApp::OnFileExit

Yes

Edit

Undo

ID_EDIT_UNDO

None

Cut

ID_EDIT_CUT

None

Copy

ID_EDIT_COPY

None

Paste

ID_EDIT_PASTE

None

View

Toolbar

ID_VIEW_TOOLBAR

FrameWnd::OnBarCheck

Yes

Status Bar

ID_VIEW_STATUS_BAR

FrameWnd::OnBarCheck

Yes

Window(MDI only)

New Window

ID_WINDOW_NEW

MDIFrameWnd::OnWindowNew

Yes

Cascade

ID_WINDOW_CASCADE

MDIFrameWnd::OnWindowCmd

Yes

Tile

ID_WINDOW_TILE_HORZ

MDIFrameWnd::OnWindowCmd

Yes

Arrange Icons

ID_WINDOW_ARRANGE

MDIFrameWnd::OnWindowCmd

Yes

Help

About AppName

ID_APP_ABOUT

None

上圖的最後一個欄位“是否預有關聯”,如果是Yes,意指只要你的選單中有此命令項,當它被點選,自然就會引發命令處理程式,應用程式不需要再任何類的Message Map中攔截此命令訊息,如果是No,則表示你必須在應用程式中攔截此訊息,例如:

BEGIN_MESSAGE_MAP(CSingleApp, CWinApp)
 ON_COMMAND(ID_APP_ABOUT, &CSingleApp::OnAppAbout)
 // 基於檔案的標準文件命令
 ON_COMMAND(ID_FILE_NEW, &CWinApp::OnFileNew)
 ON_COMMAND(ID_FILE_OPEN, &CWinApp::OnFileOpen)
 // 標準列印設定命令
 ON_COMMAND(ID_FILE_PRINT_SETUP, &CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()

二,[FILE/OPEN],[FILE/NEW]的實現過程,如下圖:

[FILE/NEW]具體如下:

1,CWinApp::OnFileOnew

void CWinApp::OnFileNew()
{
      if (m_pDocManager != NULL)
             m_pDocManager->OnFileNew();
}

2:CDocManager::OnFileNew()

void CDocManager::OnFileNew()
{
 if (m_templateList.IsEmpty()){return;}

 CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();
 if (m_templateList.GetCount() > 1)  //多個CDocTemplate彈出對話方塊讓使用者選擇
 {
  INT_PTR nID = dlg.DoModal();
  if (nID == IDOK)
   pTemplate = dlg.m_pSelectedTemplate;
  else
   return;     // none - cancel operation
 }

 //..............
 pTemplate->OpenDocumentFile(NULL);
}

3(1)單文件:CSingleDocTemplate::OpenDocumentFile

CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,
 BOOL bMakeVisible)
{
 CDocument* pDocument = NULL;
 CFrameWnd* pFrame = NULL;
 BOOL bCreated = FALSE;      // => doc and frame created
 BOOL bWasModified = FALSE;

 if (m_pOnlyDoc != NULL)  //如果已經有文件,重新初始化
 {
  // already have a document - reinit it
  pDocument = m_pOnlyDoc;
  pFrame = (CFrameWnd*)AfxGetMainWnd();
 }
 else //否則建立一個新的文件
 {
  pDocument = CreateNewDocument();
  bCreated = TRUE;
 }

 if (pFrame == NULL) //第一次建立Frame
 {
  ASSERT(bCreated);
  pFrame = CreateNewFrame(pDocument, NULL);
 }

 //[FILE/NEW]
 if (lpszPathName == NULL)
 {
  pDocument->OnNewDocument()
 }
 else   //[FILE/OPEN]
 {//如果開啟檔案失敗,建立一個新的Document
  if (!pDocument->OnOpenDocument(lpszPathName))
  {
    pDocument->OnNewDocument()
  }
  pDocument->SetPathName(lpszPathName);
 }

 CWinThread* pThread = AfxGetThread();
 ASSERT(pThread);
 if (bCreated && pThread->m_pMainWnd == NULL)
 {
  pThread->m_pMainWnd = pFrame;
 }

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

4.建立文件:CDocTemplate::CreateNewDocument()

CDocument* CDocTemplate::CreateNewDocument()
{//動態建立
 CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();
 AddDocument(pDocument);
 return pDocument;
}

5.建立Frame:CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)

CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)
{

 ASSERT(m_nIDResource != 0); // must have a resource ID to load from
 CCreateContext context;
 context.m_pCurrentFrame = pOther;
 context.m_pCurrentDoc = pDoc;
 context.m_pNewViewClass = m_pViewClass;
 context.m_pNewDocTemplate = this;

 CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();
 // 從資源裡建立新的Frame
 pFrame->LoadFrame(m_nIDResource,WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,NULL, &context)
 return pFrame;
}

6.CFrameWnd::LoadFrame

BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,CWnd* pParentWnd, CCreateContext* pContext)
{
 // only do this once
 ASSERT_VALID_IDR(nIDResource);
 ASSERT(m_nIDHelp == 0 || m_nIDHelp == nIDResource);

 Create(lpszClass, strTitle, dwDefaultStyle, rectDefault,pParentWnd, ATL_MAKEINTRESOURCE(nIDResource), 0L, pContext);
 
 //......

 return TRUE;
}
隨著Create呼叫,會發出WM_CREATE訊息,隨後如下:

CFrameWnd::OnCreate-->CFameWnd::OnCreateHelper-->CFrameWnd::OnCreateClient(過載它可實現多個檢視顯示)

-->CreateView

CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID)
{
 //動態建立
 CWnd* pView = (CWnd*)pContext->m_pNewViewClass->CreateObject();
 //建立視窗
 pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,CRect(0,0,0,0), this, nID, pContext)
 //...........
 return pView;
}

7.新的文件內部操作初始化,可以過載新增需要的文件初始化:CDocument::OnNewDocument()

BOOL CDocument::OnNewDocument()
{
 DeleteContents();
 m_strPathName.Empty();      // no path name yet
 SetModifiedFlag(FALSE);     // make clean
 return TRUE;
}

8.新的文件根據檔案初始化CDocument::OnOpenDocument(LPCTSTR lpszPathName)可過載新增文件初始化資訊

BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName)
{
 //開啟檔案
 CFileException fe;
 CFile* pFile = GetFile(lpszPathName,CFile::modeRead|CFile::shareDenyWrite, &fe);

 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);     // 序列化
  loadArchive.Close();
  ReleaseFile(pFile, FALSE);
 }
 CATCH_ALL(e)
 {
  ReleaseFile(pFile, TRUE);
  DeleteContents();   // remove failed contents
 }
 END_CATCH_ALL

 SetModifiedFlag(FALSE);     // start off with unmodified

 return TRUE;
}

9.刪除文件處理(過載新增自己的處理):CDocument::DeleteContents()

void CDocument::DeleteContents()
{
}

10.CDocTemplate::InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc,BOOL bMakeVisible)

void CDocTemplate::InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc,BOOL bMakeVisible)
{
 // just delagate to implementation in CFrameWnd
 pFrame->InitialUpdateFrame(pDoc, bMakeVisible);
}

11.CFrameWnd::InitialUpdateFrame(CDocument* pDoc, BOOL bMakeVisible)

void CFrameWnd::InitialUpdateFrame(CDocument* pDoc, BOOL bMakeVisible)
{
 // 如果Frame沒有Active檢視,設定為第一個
 CView* pView = NULL;
 if (GetActiveView() == NULL)
 {
  CWnd* pWnd = GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE);
  if (pWnd != NULL && pWnd->IsKindOf(RUNTIME_CLASS(CView)))
  {
   pView = (CView*)pWnd;
   SetActiveView(pView, FALSE);
  }
 }

 if (bMakeVisible)
 {
  //向這個Frame的所有檢視傳送WM_INITIALUPDATE訊息
  SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);
  //根據ON_MESSAGE_VOID(WM_INITIALUPDATE, CView::OnInitialUpdate),所有View都會呼叫OnInitialUpdate
  //這是View初始化的地方。
 }

 //.......
}

3(2)多文件:CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible)

CDocument* CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible)
{
 CDocument* pDocument = CreateNewDocument();    //建立文件
 CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL);   //建立Frame

 //[FILE/NEW]
 if (lpszPathName == NULL)
 {
  if (!pDocument->OnNewDocument())
  {
   pFrame->DestroyWindow();
   return NULL;
  }
  m_nUntitledCount++;
 }
 else  //[FILE/OPEN]
 {
  // 開啟已存在的文件
  if (!pDocument->OnOpenDocument(lpszPathName))
  {
   pFrame->DestroyWindow();
   return NULL;
  }
  pDocument->SetPathName(lpszPathName);
 }

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

[FILE/OPEN]

和[FILE/NEW]不同之處在於:

void CDocManager::OnFileOpen()
{
 // 彈出對話方塊選擇檔案
 CString newName;
 if (!DoPromptFileName(newName, AFX_IDS_OPENFILE,
   OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, TRUE, NULL))
  return;


 AfxGetApp()->OpenDocumentFile(newName);
}

CDocument*  CWinApp::OpenDocumentFile(LPCSTR lpszFileName)

{

  ASSERT(m_pDocManager != NULL)

  //回到了和[FILE/OPEN]一樣的地方

  return m_pDocManager->OpenDocumentFile(lpszFileName);

}

在程式一執行是通過命令列資訊類來處理的。

BOOL CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)

{

  AfxGetApp()->OnCmgMsg(ID_FILE_NEW,0,NULL,NULL);

  //.......

}