本文主要介绍:在MFC中,菜单打开命令的响应过程。
一、MFC打开命令的响应过程:
File->Open 对应的ID为ID_FILE_OPEN,其响应过程如下:
注:如果自己已将ID_FLIE_OPEN在MFC中重载了,则会直接响应重载函数,不会按以下过程响应。
1.点击File->Open,首先响应的函数为: CWinApp::OnFileOpen(),其函数原型为:
void CWinApp::OnFileOpen() { ASSERT(m_pDocManager != NULL); m_pDocManager->OnFileOpen(); }
2.由上面的程序可知,接着调用的是: CDocManager::onFileOpen(),该函数功能是:显示打开文件的对话框,并获取文件的路径,其函数原型为:
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 }
CDocument* CWinApp::OpenDocumentFile(LPCTSTR lpszFileName) { ASSERT(m_pDocManager != NULL); return m_pDocManager->OpenDocumentFile(lpszFileName); }
4.再调用函数: CDocManager::OpenDocumentFile(LPCTSTR lpszFileName),该函数遍历文档模板,对每个文档进行匹配,若该文件已经在某个文档中打开,则会激活该文档视图,否则用匹配的文档模板,调用下一个打开函数,其原型为:
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显示框架窗口。其原型为:
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)函数,从而打开相应的文件。其默认生成原型为:
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()函数:
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)函数,使后面函数依次被调用。