MFC Doc/View笔记(一) 打开文件代码跟踪



VC2008 sp1,向导生成的doc/view MDI应用程序,主线程继承自CWinAppEx。

菜单选择打开文件,首先是App响应ID_FILE_OPEN命令,从事件映射:

ON_COMMAND(ID_FILE_OPEN, &CWinAppEx::OnFileOpen)

可以看到执行的是CWinAppEx::OnFileOpen()。在CWinAppEx类里没找到OnFileOpen,应该直接继承CWinApp的OnFileOpen。找到代码如下:

void CWinApp::OnFileOpen()

{

ENSURE(m_pDocManager != NULL);

m_pDocManager->OnFileOpen();

}

可以看到里面又调用CDocManagerOnFileOpen()。在此处加个断点,作为跟踪的起点。用菜单打开一个文件开始跟踪。F11进入CDocManager::OnFileOpen(),看到先打开选择文件的对话框,选择文件得到文件名再调用CWinApp的OpenDocumentFile(),代码如下:

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

}

 

进了CWinApp::OpenDocumentFile,我日,又转到CDocManagerOpenDocument()

CDocument* CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)

{

ENSURE_VALID(m_pDocManager);

return m_pDocManager->OpenDocumentFile(lpszFileName);

}

 

一个CDocManager可能注册了了多个template,比如说一个应用可以打开文本,也可以打开图片。CDocManager::OpenDocumentFile()先不知道打开的是什么文件,不知道是用哪一个template来操作。一个个尝试匹配,找到最合适的那个。细节暂时忽略,应该是按注册的扩展名。

CDocument* CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)

{

if (lpszFileName == NULL)

{

AfxThrowInvalidArgException();

}

// 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;

Checked::tcsncpy_s(szTemp, _countof(szTemp), lpszFileName, _TRUNCATE);

LPTSTR lpszLast = _tcsrchr(szTemp, '\"');

if (lpszLast != NULL)

*lpszLast = 0;

 

if( AfxFullPath(szPath, szTemp) == FALSE )

{

ASSERT(FALSE);

return NULL; // We won't open the file. MFC requires paths with

             // length < _MAX_PATH

}

 

TCHAR szLinkName[_MAX_PATH];

if (AfxResolveShortcut(AfxGetMainWnd(), szPath, szLinkName, _MAX_PATH))

Checked::tcscpy_s(szPath, _countof(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 posOpenDoc = pOpenDocument->GetFirstViewPosition();

if (posOpenDoc != NULL)

{

CView* pView = pOpenDocument->GetNextView(posOpenDoc); // get first one

ASSERT_VALID(pView);

CFrameWnd* pFrame = pView->GetParentFrame();

 

if (pFrame == NULL)

TRACE(traceAppMsg, 0, "Error: Can not find a frame for document to activate.\n");

else

{

pFrame->ActivateFrame();

 

if (pFrame->GetParent() != NULL)

{

CFrameWnd* pAppFrame;

if (pFrame != (pAppFrame = (CFrameWnd*)AfxGetApp()->m_pMainWnd))

{

ASSERT_KINDOF(CFrameWnd, pAppFrame);

pAppFrame->ActivateFrame();

}

}

}

}

else

TRACE(traceAppMsg, 0, "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);

}

那个pOpenDocument没看明白,先不管它。知道进了CDocTemplate的OpenDocumentFile就行。我这里是MDI程序,进的是CMultiDocTemplate::OpenDocumentFileCDocument()

CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,

BOOL bMakeVisible)

{

CDocument* pDocument = CreateNewDocument();

if (pDocument == NULL)

{

TRACE(traceAppMsg, 0, "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

TRACE(traceAppMsg, 0, "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

TRACE(traceAppMsg, 0, "CDocument::OnOpenDocument returned FALSE.\n");

pFrame->DestroyWindow();

return NULL;

}

pDocument->SetPathName(lpszPathName);

}

 

InitialUpdateFrame(pFrame, pDocument, bMakeVisible);

return pDocument;

}

 

上面先分别用CreateNewDocument()和CreateNewFrame()创建了文档和视图的框架。暂时没有跟踪进去如何创建,反正知道这时候DocView(应该在Frame里面创建了)的实例都已经存在了。之后pDocument->OnOpenDocument()执行打开操作,再InitialUpdateFrame()更新窗口。

注意CDocument的OnOpenDocument() 缺省有一个重载,可能需用改写,因为不一定用它内置的CArchive串行化方法保存文件。

 

void CDocTemplate::InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc,

BOOL bMakeVisible)

{

// just delagate to implementation in CFrameWnd

pFrame->InitialUpdateFrame(pDoc, bMakeVisible);

}

 

进CFrameWnd::InitialUpdateFrame看看窗口咋初始化:

void CFrameWnd::InitialUpdateFrame(CDocument* pDoc, BOOL bMakeVisible)

{

// if the frame does not have an active view, set to first pane

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)

{

// send initial update to all views (and other controls) in the frame

SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);

 

// give view a chance to save the focus (CFormView needs this)

if (pView != NULL)

pView->OnActivateFrame(WA_INACTIVE, this);

 

// finally, activate the frame

// (send the default show command unless the main desktop window)

int nCmdShow = -1;      // default

CWinApp* pApp = AfxGetApp();

if (pApp != NULL && pApp->m_pMainWnd == this)

{

nCmdShow = pApp->m_nCmdShow; // use the parameter from WinMain

pApp->m_nCmdShow = -1; // set to default after first time

}

ActivateFrame(nCmdShow);

if (pView != NULL)

pView->OnActivateView(TRUE, pView, pView);

}

 

// update frame counts and frame title (may already have been visible)

if (pDoc != NULL)

pDoc->UpdateFrameCounts();

OnUpdateFrameTitle(TRUE);

}

 

这里又发消息WM_INITIALUPDATE给里面的View窗口,这是我要关注的地方了,刚才开始就是不知道View怎么初始化,才发狠心跟踪代码,这里终于找到了。重载一下OnInitialUpdate()函数,这应该是做事的地方了。

void CMyView::OnInitialUpdate()

{

CView::OnInitialUpdate();

 

// TODO: 在此添加专用代码和/或调用基类

}

 

瞟一眼:CView::OnInitialUpdate

void CView::OnInitialUpdate()

{

OnUpdate(NULL, 0, NULL);        // initial update

}

还有啊,再瞟一眼CView::OnUpdate

void CView::OnUpdate(CView* pSender, LPARAM /*lHint*/, CObject* /*pHint*/)

{

ASSERT(pSender != this);

UNUSED(pSender);     // unused in release builds

 

// invalidate the entire pane, erase background too

Invalidate(TRUE);

}

这回差不多到头了。Invalidate应该会引发WM_PAINT消息,后面该是OnPaint()的事了。

 

思路大致清楚了。虽然这个Doc/View框架很复杂,就打开文件及显示内容这一部分来说,要关注的就是DocOnOpenDocument()读入数据,View在OnInitialUpdate()里初始化,OnPaint()里做显示。至于Doc/View里做内容修改与交互,以及文档的保存,下一步再研究。

PS:转自:http://skywolfblog.blog.163.com/blog/static/1779021802012222114736392/

文档视图结构(Document/View Architecture)是MFC的精髓,也是Observer模式的具体实现框架Document/View Architecture通过将数据和其表示分开,提供了很好的数据层次和表现层次的解耦。然而, 虽然我们使用MFC AppWizard就可以很轻松地获得个支持Document/View Architecture的MFC程序框架Document/View Architecture本身的复杂性加上VC产生的系列代码足够让我们眼花缭乱,时陷入云里雾里不可自拔。 加上我们更多的人都没有经过Windows SDK Programming 的学习和经历就直接进行MFC Programming的学习和开发, 更是面对堆的代码不知所措。 之于Document/View Architecture,侯捷先生的《深入浅出MFC书确实进行了很深入的分析和研究,网络上也有很多在侯捷先生著述 的基础上的进步的文章出现,但是个人觉得这里面有点瑕疵(仅代表k_eckel浅见):太过深入, 这些分析和研究都最终会定位到Windows SDK中窗口的创建过程、MFC中对Document/View Architecture支持所提供的复杂无序的宏等 对于没有Windows SDK Programming 经验和经历的学习者和对于MFC不是很熟悉的学习者无异于是徒增烦恼,个本来就很复杂的问题 更加地复杂化了。我的观点是这个过程是必要的,然而不是每个人所必需的,或者说不是每个人在初期学习和绝大多数项目开发中所 必需的。我向来对众多的仅仅学会了拖拉点拽就以为会了MFC(侯捷先生在《深入浅出MFC书中对这个群体有个照面,这里不罗嗦) 不以为然,但是我依然认为轻量级学习成本是重要的,容易上手,易于接受是门技术成功或者说有价值的个很大的决定性因素。 因此提供个轻量级的学习过程对于学习来说是很有必要性的,本系列文章就遵循这样个理念,对MFCDocument/View Architecture 进行个分析(姑且也可以称之为深入),尽量将对Document/View Architecture的理论研究(侯捷先生书中很多内容)和实际的项目 开发结合起来,最后提供个简单但是全面的Document/View Architecture项目开发(主要是界面框架设计和开发)的实际例子,供参考。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值