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 |
|
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);
//.......
}