一、基本补充
1、POSITION 指向的是一个数据块的地址,指向CNode。相关的有GetHeadPostion、GetFirstViewPosition()等。
POSITION pos=GetFirstViewPosition();
CView* pView=GetNextView(pos);//里面会把pNode->pNext放在pos中,这样pos不用再改变了,直接判断
//while(pos!=NULL),返回值会自动,把找到的pNode->data返回,其中存的是CView对象的地址,然后里面会这样转换,
//return (CView*)pNode->data;
//CView* GetNextView(POSITION& rPosition) const;
2、void类型指针可接受任何类型的指针,但是把数据读出来是,要转换。如:int a = 5; void *p1 = &a ;cout<<*(int*)p1<
二、数据分析
文档/视图主要架构是:
CWinApp中有一个m_pDocManager指针,CWinApp::AddDocTemplate(CDocTemplate* pTemplate)时,会new 一个CDocManager,然后把数据放在m_templateList中。
//CWinApp中
class CWinApp : public CWinThread
{
public:
CDocManager* m_pDocManager;//一个数据指针,文档模板管理类,父类中有CWnd* m_pMainWnd
};
class CWinThread : public CCmdTarget
{
CWnd* m_pMainWnd; //主窗口对象指针
CWnd* m_pActiveWnd; //活动窗口对象
}
class CDocManager : public CObject
{
public:
CPtrList m_templateList;//一个对象,放文档模板的对象的地址
};
class class CPtrList : public CObject
{
public:
struct CNode
{
CNode* pNext;
CNode* pPrev;
void* data; //把要加入的指针放到这里
}
CNode* m_pNodeHead; //头节点
CNode* m_pNodeTail; //尾节点
int m_nCount; //初始化时=0,NewNode()时,节点总数m_nCount++,
};
pDocTemplate文档模板会保存菜单、文档、主框架、视图的CRuntimClass信息信息
文档模板类CDocTemplate还保存了它所支持的全部文档类的信息,包括所支持文档的文件扩展名、文档在框架窗口中的名字、图标等。
//CWinApp加入文档模板类
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTUselesssDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CTUselesssView));
//--------------------
//CSingleDocTemplate中
class CSingleDocTemplate : public CDocTemplate
{
CDocument* m_pOnlyDoc;//只有一个文档
}
//CMultiDocTemplate中
class CMultiDocTemplate : public CDocTemplate
{
CPtrList m_docList; //保存多个文档
}
//-------------------
class CDocTemplate : public CCmdTarget
{
protected:
CRuntimeClass* m_pDocClass; //保存正在运行的文档class,以便动态创建
CRuntimeClass* m_pFrameClass; //保存正在运行的框架class
CRuntimeClass* m_pViewClass; //保存正在运行的框架class
}
CDocTemplate::CDocTemplate()//初始化传参数
{
m_pDocClass = pDocClass; m_pFrameClass = pFrameClass; m_pViewClass = pViewClass;
}三、数据交互
CDocManager::OpenDocumentFile()打开文档时,遍历文档模板,对每个模板用MatchDocType(szPath,pOpenDocument)匹配文档类型。匹配时主要根据文件扩展名判断。若文件已经在某个文档中打开,则激活文档的第一个视图,否则用匹配的文档模板pBestTemplate->OpenDocumentFile(szPath)。
CDocTemplate::OpenDocumentFile调用CDocument::OnOpenDocument打开文件。
//MDI窗口中, InitInstance(){ pMainFrame->LoadFrame(IDR_MAINFRAME);//这里面会创建,整体的窗口,即主窗口 m_pMainWnd = pMainFrame;//CWinApp中保存pMainFrame ProcessShellCommand(cmdInfo)//调用OnFileNew } CDocManager::OnFileNew() { pTemplate->OpenDocumentFile(NULL); }
为什么PreCreateWindow()会过而不入呢?
四、窗口数据创建
class CChildFrame : public CMDIChildWnd : public CFrameWnd
class CMainFrame : public CMDIFrameWnd : public CFrameWnd
CMainFrame* pMainFrame = new CMainFrame;//new CMainFrame对象
//1、创建CMainFrame对应的窗口及MDIClient客户窗口
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))//这里面会创建最初的主窗口
CMainFrame::OnCreate()
{
CMDIFrameWnd::CreateClient()
{
m_hWndMDIClient = CreateWindowEx(.."mdiclient"..); //mdiclient为类名,即mdiclient窗口
}
}
//2、创建ChildFrame窗口和其子窗口CView
ProcessShellCommand(cmdInfo);//触发OnFileNew(),会调用
{
调用到m_pDocManager->OnFileNew();//
{
调用CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible);
}
}
CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible=TRUE)
{
pDocument->CreateNewDocument()//并将new出来的C**document对象加入到文档模板中
CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL);//CChildFrame,CRuntimeClass保存在
}
CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)//
{
CCreateContext context; context.m_pCurrentFrame = pOther;//NULL
context.m_pCurrentDoc = pDoc;//当前文档
context.m_pNewViewClass = m_pViewClass;//视图class
context.m_pNewDocTemplate = this;//文档模板
pFrame->LoadFrame(m_nIDResource,WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,NULL, &context);//创建CChildFrame窗口
{
AfxHookWindowCreate(this);//作用是绑定CChildFrame指针到前面的MDIClient窗口,里面会把CChildFrame指针保存到线程指
//针,然后收到消息时,wparam是句柄,在CBT的钩子函数中,会this->Attach((HWND)wparam)中,这样就绑定了。
(HWND)::SendMessage(pParentWnd->m_hWndMDIClient,WM_MDICREATE, 0, (LPARAM)&mcs);//创建之前,给MDIClient窗口发送消息来创建MDIClient客户窗口的子窗体,mcs是子窗口参数,最后返回子窗口句柄
//WM_MDICREATE,之后,还会发送WM_NCCRATE、WM_CREATE,进入CChildFrame::OnCreate(),参考6、7
}
//创建视图窗口
//CCreateContext来new CView,然后再pView->Create(,..AFX_IDW_PANE_FIRST..) 创建视图窗口
CView::OnCreate()
{
CCreateContext* pContext->m_pCurrentDoc->AddView(this);//这样就把视图pView添加到文档pDocument中
{
....
pView->m_pDocument = this;//同时将pView的成员m_pDocument指向该文档
}
}
以上调试,可在各个OnCreate处下断点。
五、活动视图和活动文档【8】
CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible)
{
.....//创建文档对象,和框架(创建CChildFrame窗口(CChildFrame::OnCreate中创建CView窗口))
InitialUpdateFrame(pFrame, pDocument, bMakeVisible);//pFrame->InitialUpdateFrame(pDocument,TRUE)
}
void CFrameWnd::InitialUpdateFrame(CDocument* pDoc, BOOL bMakeVisible)
{ CWnd* pWnd = GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE); CView* pView = (CView*)pWnd; SetActiveView(pView,FALSE)//只是将pView保存到CFrameWnd:: CView* m_pViewActive; CFrameWnd::ActivateFrame(nCmdShow);//ChildFrame { HWND hWndLastPop = ::GetLastActivePopup(m_hWnd);//刚开始,返回的就是ChildFrame的句柄 ::BringWindowToTop(hWndLastPop);//显示 ShowWindow()//显示ChildFrame窗口 } pView->OnActivateView() { if (IsTopParentActive())//开始为0,没有执行 SetFocus(); } } CFrameWnd::GetActiveView() return m_pViewActive; CFrameWnd::GetActiveDocument() return m_pViewActive->GetDocument();六、MDI主窗口(CMainFrame)和子窗口的关系
pFrame->LoadFrame()
{
Create()
{
//发送WM_MDICREATE消息,创建了MDI子窗口
HWND hWnd = (HWND)::SendMessage(pParentWnd->m_hWndMDIClient,WM_MDICREATE, 0, (LPARAM)&mcs); }
//取得当前的活动子窗口是通过向m_hWndMDIClient发送WM_MDIGETACTIVE }七、总结
//窗口创建顺序
1、创建CMainFrame主窗口和它的子窗口MDIClient
CMainFrame pMainFrame->LoadFrame(IDR_MAINFRAME))//创建CMainFrame主窗口,保存到CWinApp m_pMainWnd中
2、触发CMainFrame中的WM_CREATE消息
CMainFrame::OnCreate()//创建子窗口MDIClient,子窗口ToolBar,子窗口StatusBar
3、CWinApp::OnFileNew()
调用m_pDocManager->OnFileNew(),它又CMultiDocTemplate::OpenDocumentFile()
里面创建pDocument,pFrame(CChildFrame),
3.0、new document,一方面加入到pDocTemplate中,另一方面pDoc->m_DocTemplate = this,加入对应的pDocTemlate
3.1、pFrame->LoadFrame()//创建CChildFrame窗口,这里是发送消息给MDIClient,让内部创建子窗口,并返回句柄,
//里面带了一个参数,CCreateContext context,里面有
3.1.1、 CChildFrame::OnCreate()//触发CChildFrame::WM_CREATE消息
3.1.1.1、 CreateView(pContext, AFX_IDW_PANE_FIRST)//创建子窗口CView窗口,利用context new一个pView对象,然后pView->Create()
3.1.1.1.1、 CView::OnCreate()中,pContext->m_pCurrentDoc->AddView(this);//一方面,将pView加入到pDocument中;另一方面,将对应的pDocument,加入到pView->m_pDocument中。
3.2、 最后InitialUpdateFrame(pFrame, pDocument, bMakeVisible);
3.2.1、 pFrame->InitialUpdateFrame(pDocument, bMakeVisible);
3.2.1.1、 pView =GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE);
设置CChildFrame::CFrameWnd::m_pViewActive = pView;
3.2.1.2、SendMessageToDescendants(WM_INITIALUPDATE,)//向CChildFrame所有的子窗口发送该消息,即所有视图都能收到
4、MDI主窗口(CMainFrame)和子窗口(CChildFrame)的关系
获取活动子窗口,可发送MDIClient窗口WM_MDIGETACTIVE消息
::SendMessage(pParentWnd->m_hWndMDIClient,WM_MDIGETACTIVE, 0, (LPARAM)&mcs);
创建一个子窗口,可发送MDIClient窗口WM_MDICREATE
::SendMessage(pParentWnd->m_hWndMDIClient,WM_MDICREATE, 0, (LPARAM)&mcs);
要获取CMainFrame,则需要调用AfxGetThread()->m_pMainWnd
5、窗体架构
CMainFrame主窗口:子窗口MDIClient、ToolBar、StatusBar
窗口MDIClient:子窗口CChildFrame
窗口CChildFrame:子窗口CView窗口
参考:1、MFC指针的获取
2、void类型指针
3、MFC文档视图结构-Z_Q
4、深入浅出MFC文档/视图架构之文档模板
5、CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)遍与OnOpenDocument()区别
6、进入MFC讲坛的前沿(三)
7、使用Win32 API实现MDI程序
8、文档/视图结构中的各个部分是如何联系到一起的(2)
9、深入浅出MFC文档/视图架构之文档模板