【交互】MFC架构分析之多文档多视图1

一、基本补充

       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<

二、数据分析

         文档/视图主要架构是:

          【交互】MFC架构分析之多文档多视图1_第1张图片

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文档/视图架构之文档模板

你可能感兴趣的:(【交互】MFC架构分析之多文档多视图1)