MFC 文档类
目录
文档类概述
相关类CDocument,作用:提供了一个用于管理数据的类,封装了关于数据的管理(数据提取,数据转换,数据存储等),并和视图类进行数据交互。
文档类的使用
定义一个自己的文档类(CMyDoc ),派生自CDocument
#include <afxwin.h>
#include <afxext.h>
#include "resource.h"
class CMyDoc : public CDocument {
};
class CMyView : public CView {
DECLARE_DYNCREATE(CMyView) //动态创建机制
DECLARE_MESSAGE_MAP()
public:
virtual void OnDraw(CDC* pDC);
afx_msg int OnCreate(LPCREATESTRUCT pcs);
};
IMPLEMENT_DYNCREATE(CMyView, CView)
BEGIN_MESSAGE_MAP(CMyView, CView)
ON_WM_CREATE()
END_MESSAGE_MAP()
int CMyView::OnCreate(LPCREATESTRUCT pcs) {
return CView::OnCreate(pcs); //将文档类对象和视图类对象建立关联关系。
}
void CMyView::OnDraw(CDC* pDC) {
pDC->TextOut(100, 100, "我是视图窗口");
}
class CMyFrameWnd : public CFrameWnd {
DECLARE_MESSAGE_MAP()
public:
afx_msg int OnCreate(LPCREATESTRUCT pcs);
afx_msg void OnPaint();
};
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_WM_PAINT()
ON_WM_CREATE()
END_MESSAGE_MAP()
void CMyFrameWnd::OnPaint() {
PAINTSTRUCT ps = { 0 };
HDC hdc = ::BeginPaint(this->m_hWnd, &ps);
::TextOut(hdc, 200, 200, "我是框架窗口客户区", strlen("我是框架窗口客户区"));
::EndPaint(this->m_hWnd, &ps);
}
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs) {
return CFrameWnd::OnCreate(pcs);//动态创建视图类对象,并创建视图窗口
}
class CMyWinApp : public CWinApp {
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance() {
CMyFrameWnd* pFrame = new CMyFrameWnd;
CMyDoc* pDoc = new CMyDoc;
CCreateContext cct;
cct.m_pCurrentDoc = pDoc;//文档类对象地址
cct.m_pNewViewClass = RUNTIME_CLASS(CMyView);//&CMyView::classCMyView CRuntimeClass 成员变量
pFrame->LoadFrame(IDR_MENU1, WS_OVERLAPPEDWINDOW, NULL, &cct); // 内部依然是调用Create
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
程序框架过程
复习一下前面学习过的内容
WM_CREATE消息的两个附带信息
- wParam-不使用
- lParam -传来的信息为指针(CREATESTRUCT类型),这个指针指向位置保存了创建窗口函数( :CreateWindowEx )的全部12个参数
结合消息映射机制
ON_WM_CREATE()
处理WM_CREATE消息的函数为︰
afx_msg int OnCreate(LPCREATESTRUCT pcs);
流程如下:
- 利用框架类对象地址( pFame )调用LoadFrame函数,创建框架窗口
- 在处理框架窗口的WM_CREATE消息时,动态创建视图类对象,并创建视图窗口。
- 在处理视图窗口的WM_CREATE消息时,将文档类对象和视图类对象建立关联关系。
?
LoadFrame执行分析
利用框架类对象地址( pFame )调用LoadFrame函数,创建框架窗口
给这个函数下断点,F11进入,this 指针是 pFrame,需要关注 CCreateContext 对象 cct
调用 Create 函数?
调用 CreateEx 函数
cct 被赋值给 cs.lpCreateParams
PreCreateWindow函数:注册窗口类 Afx……,处理 lpszClassName 为NULL
AfxHookWindowCreate函数:埋了一个钩子对WM_CREATE函数感兴趣,将pFrame保存到全局变量 程序线程信息 中
调用?CreateWindowEx 函数,最后一个参数 cs.lpCreateParam 是 cct
然后就创建了主框架窗口
框架窗口创建之后就会产生消息,WM-CREATE
框架窗口 WM_CREATE 消息处理
在处理框架窗口的WM_CREATE消息时,动态创建视图类对象,并创建视图窗口
这里是调用父类来实现的,函数内部this是 pFrame
LPCREATESTRUCT pcs;? ? pcs指向也就是上面调用? CreateWindowEx 函数的12个参数
有获取了 cct,随后进入 OnCreateHelper 函数,this指针依然是 pFrame
?进入函数 OnCreateClient
?进入函数 CreateView ,估计就是创建视图窗口的
pContext 就是 cct? ,m_pNewViewClass 是RUNTIME_CLASS ,就是CMyView类的成员变量 classCMyView这个结构体
使用动态创建机制创建 CMyView 对象 pView 并返回
创建视图窗口,调用Create函数,cct作为最后一个参数
调用函数? this=pView , 最后一个参数是 cct
再次回来
创建视图窗口,最后一个参数也是? cct
之后会产生 WM_CREATE 消息,会被钩子勾走,完成对象与句柄的绑定。然后钩子把消息还给视图窗口
视图窗口 WM_CREATE 消息处理
在处理视图窗口的WM_CREATE消息时,将文档类对象和视图类对象建立关联关系
pcs,就是 ::CreateWindowEx 函数的12个参数
进入if语句:
pContext 是cct,cct->m_pCurrentDoc 是CMyDoc对象的地址
AddView函数内部 this 指针是 CMyDoc 的对象,参数this是pView
进入函数,m_viewList 是啥,无声明定义多半前面有this指针,是某个类或者结构体的成员,应该是文档类的成员变量,是一个链表
m_viewList.AddTail(pView);? ? ?AddTail函数往链表尾部添加一个视图类对象地址,文档类对象用一个链表成员变量,保存视图类对象地址
pView->m_pDocument = this;? ?视图类对象的成员变量保存了文档类对象地址,视图类对象用一个普通成员变量,保存文档类对象地址
对象关系图
- 应用程序类对象 theApp
- theApp->m_pMainWnd = pFrame ,框架类对象地址
- theApp->m_pMainWnd->m_pDocument =?pFrame->m_pDocument = pDoc,文档类对象地址pDoc
- theApp->m_pMainWnd->m_pDocument->m_viewList =?pDoc->m_viewList,存储了所有视图类对象地址
文档类对象用一个链表成员变量,保存视图类对象地址视图类对象用一个普通成员变量,保存文档类对象地址。
经过分析可知一个文档类对象可以对应多个视图类对象(视图窗口),而一个视图类对象(视图窗口)只能对应一个文档类对象。
这样的话,就相当于
文档对象存储数据,可以对应多个视图对象,即展示数据;而数据对象只能对应一个文档对象
这样的功能该如何实现?
窗口切分
相关类:CSplitterWnd-不规则框架窗口类,封装了关于不规则框架窗口的操作。
注:规则框架窗口只有一个客户区,不规则框架窗口有多个客户区。
窗口切分的使用:
- 重写CFrameWnd类的成员虚函数OnCreateClient
- 在虚函数中调用CSplitterWnd:CreateStatic创建不规则框架窗口
- 在虚函数中调CSplitterWnd::CreateView创建视图窗口。
窗口的关系:
- 最底下是框架窗口的客户区
- 上面一层是CSplitterWnd:CreateStatic创建不规则框架窗口,就是一个倒日型窗口,是框架出纳港口的子窗口
- 最上面就是两个视图窗口,分别是上面的子窗口
OnCreateClient 是一个虚函数,在CFrameWnd的实现中只能创建一个视图窗口。可以在CMyFrameWnd类中重写这个虚函数,就可以创建多个视图窗口。
OnCreateClient 被调用的时机是在处理WM_CREATE函数时
调用CreateView上面已经分析过了:
- 创建视图对象
- 创建视图窗口
- 产生WM_CREATE消息,父类处理这个消息,实现了文档类和视图类的对象关联
m_pViewActive = (CView*)split.GetPane(0,1); // GetPane获取某一个视图类对象,0,1位置那个,设置为活动该窗口
#include <afxwin.h>
#include <afxext.h>
#include "resource.h"
class CMyDoc : public CDocument {
};
class CMyView : public CView {
DECLARE_DYNCREATE(CMyView) //动态创建机制
DECLARE_MESSAGE_MAP()
public:
virtual void OnDraw(CDC* pDC);
afx_msg int OnCreate(LPCREATESTRUCT pcs);
};
IMPLEMENT_DYNCREATE(CMyView, CView)
BEGIN_MESSAGE_MAP(CMyView, CView)
ON_WM_CREATE()
END_MESSAGE_MAP()
int CMyView::OnCreate(LPCREATESTRUCT pcs) {
return CView::OnCreate(pcs); //将文档类对象和视图类对象建立关联关系。
}
void CMyView::OnDraw(CDC* pDC) {
pDC->TextOut(100, 100, "我是视图窗口");
}
class CMyFrameWnd : public CFrameWnd {
DECLARE_MESSAGE_MAP()
public:
afx_msg int OnCreate(LPCREATESTRUCT pcs);
afx_msg void OnPaint();
virtual BOOL OnCreateClient(LPCREATESTRUCT pcs, CCreateContext* pContext);
CSplitterWnd split;//不规则框架窗口
};
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_WM_PAINT()
ON_WM_CREATE()
END_MESSAGE_MAP()
BOOL CMyFrameWnd::OnCreateClient(LPCREATESTRUCT pcs, CCreateContext* pContext) {
//创建两个视图窗口
split.CreateStatic(this, 1, 2);// 参数1:父窗口,是框架窗口this;参数2,3:一行两列
// 安装视图窗口 0行0列安装一个视图窗口,CSize没啥用,反正也不会遵守,最后一个参数是cct
split.CreateView(0, 0, RUNTIME_CLASS(CMyView), CSize(100, 100), pContext);
// 安装视图窗口 0行1列安装一个视图窗口
split.CreateView(0, 1, pContext->m_pNewViewClass, CSize(100, 100), pContext);
// 这个CtreateView函数拿着视图类对象调用类工厂函数 CreateObject,创建视图窗口对象,再创建窗口动态创建机制
m_pViewActive = (CView*)split.GetPane(0,1); // GetPane获取某一个视图类对象,0,1位置那个,设置为活动该窗口
return TRUE;
}
void CMyFrameWnd::OnPaint() {
PAINTSTRUCT ps = { 0 };
HDC hdc = ::BeginPaint(this->m_hWnd, &ps);
::TextOut(hdc, 200, 200, "我是框架窗口客户区", strlen("我是框架窗口客户区"));
::EndPaint(this->m_hWnd, &ps);
}
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs) {
return CFrameWnd::OnCreate(pcs);//动态创建视图类对象,并创建视图窗口
}
class CMyWinApp : public CWinApp {
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance() {
CMyFrameWnd* pFrame = new CMyFrameWnd;
CMyDoc* pDoc = new CMyDoc;
CCreateContext cct;
cct.m_pCurrentDoc = pDoc;//文档类对象地址
cct.m_pNewViewClass = RUNTIME_CLASS(CMyView);//&CMyView::classCMyView CRuntimeClass 成员变量
pFrame->LoadFrame(IDR_MENU1, WS_OVERLAPPEDWINDOW, NULL, &cct); // 内部依然是调用Create
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
命令消息处理顺序
对象关系图:
在下图中框架类对象的成员变量m_pViewActive保存了一个视图类对象的地址,在上面框架窗口有多个视图窗口,那么它保存的是那个?
它保存的是活动窗口,就是鼠标点击某个窗口后,这个窗口就是活动窗口,这个窗口只能有一个。也就是鼠标点击后就会触发消息,保存这个窗口对象地址保存到成员变量中。
消息映射机制:圆形代表结构体静态变量? ? ?方形代表消息映射的数组静态变量
最下面的四个类是我们创建的,本身是没有这两个静态成员变量的,需要我们自己添加
WM_COMMAND处理顺序:视图类→文档类→框架类→应用程序类(默认顺序)
其他消息找自己窗口的线上处理。
#include <afxwin.h>
#include <afxext.h>
#include "resource.h"
class CMyDoc : public CDocument {
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnNew();
CString str;
};
BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
ON_COMMAND(ID_NEW, OnNew)
END_MESSAGE_MAP()
void CMyDoc::OnNew() {
this->str = "hello world"; //接受到的数据。
// this->UpdateAllViews( NULL );//刷新和这个文档类对象(this)关联的所有视图窗口
//this->m_viewList;
POSITION pos = this->GetFirstViewPosition(); //GetFirstXXXPosition
CView* pView = this->GetNextView(pos); //GetNextXXX
this->UpdateAllViews(pView);//刷新和这个文档类对象(this)关联的除了pView指向的视图窗口
}
class CMyView : public CView {
DECLARE_DYNCREATE(CMyView) //动态创建机制
DECLARE_MESSAGE_MAP()
public:
virtual void OnDraw(CDC* pDC);
afx_msg int OnCreate(LPCREATESTRUCT pcs);
afx_msg void OnNew();
};
IMPLEMENT_DYNCREATE(CMyView, CView)
BEGIN_MESSAGE_MAP(CMyView, CView)
// ON_COMMAND( ID_NEW, OnNew )
ON_WM_CREATE()
END_MESSAGE_MAP()
void CMyView::OnNew() {
AfxMessageBox("视图类处理的WM_COMMAND消息");
}
int CMyView::OnCreate(LPCREATESTRUCT pcs) {
return CView::OnCreate(pcs); //将文档类对象和视图类对象建立关联关系。
}
void CMyView::OnDraw(CDC* pDC) {
// CMyDoc* pDoc = (CMyDoc*)this->m_pDocument;
CMyDoc* pDoc = (CMyDoc*)this->GetDocument();
pDC->TextOut(100, 100, pDoc->str);
}
class CMyFrameWnd : public CFrameWnd {
DECLARE_MESSAGE_MAP()
public:
afx_msg int OnCreate(LPCREATESTRUCT pcs);
afx_msg void OnPaint();
virtual BOOL OnCreateClient(LPCREATESTRUCT pcs, CCreateContext* pContext);
CSplitterWnd split;//不规则框架窗口
afx_msg void OnNew();
};
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
// ON_COMMAND( ID_NEW, OnNew)
ON_WM_PAINT()
ON_WM_CREATE()
END_MESSAGE_MAP()
void CMyFrameWnd::OnNew() {
AfxMessageBox("框架类处理了新建被点击");
}
BOOL CMyFrameWnd::OnCreateClient(LPCREATESTRUCT pcs, CCreateContext* pContext) {
//创建两个视图窗口
split.CreateStatic(this, 1, 2);
split.CreateView(0, 0, RUNTIME_CLASS(CMyView), CSize(100, 100), pContext);
split.CreateView(0, 1, pContext->m_pNewViewClass, CSize(100, 100), pContext);
m_pViewActive = (CView*)split.GetPane(0, 0);
return TRUE;
}
void CMyFrameWnd::OnPaint() {
PAINTSTRUCT ps = { 0 };
HDC hdc = ::BeginPaint(this->m_hWnd, &ps);
::TextOut(hdc, 200, 200, "我是框架窗口客户区", strlen("我是框架窗口客户区"));
::EndPaint(this->m_hWnd, &ps);
}
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs) {
return CFrameWnd::OnCreate(pcs);//动态创建视图类对象,并创建视图窗口
}
class CMyWinApp : public CWinApp {
DECLARE_MESSAGE_MAP()
public:
virtual BOOL InitInstance();
afx_msg void OnNew();
};
BEGIN_MESSAGE_MAP(CMyWinApp, CWinApp)
// ON_COMMAND( ID_NEW, OnNew )
END_MESSAGE_MAP()
void CMyWinApp::OnNew() {
AfxMessageBox("应用程序类处理了WM_COMMAND消息");
}
BOOL CMyWinApp::InitInstance() {
CMyFrameWnd* pFrame = new CMyFrameWnd;
CMyDoc* pDoc = new CMyDoc;
CCreateContext cct;
cct.m_pCurrentDoc = pDoc;//文档类对象地址
cct.m_pNewViewClass = RUNTIME_CLASS(CMyView);//&CMyView::classCMyView
pFrame->LoadFrame(IDR_MENU1, WS_OVERLAPPEDWINDOW, NULL, &cct);
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
在应用程序类下断点分析可以观察到整个流程
查看调用堆栈?
WM_COMMAND 消息 与 WM_CREATE 消息是在 OnWndMsg 中分道扬镳
接下来进入 OnCommand 函数,但是这个函数的 this 指针是谁?应该是 pFrame 框架窗口对象
所以在函数OnCommand函数中的this指针也是pFrame
进入函数?OnCommand 函数,之后进入CFrameWnd::OnCmdMsg,内部this指针依然是pFrame
下面是?CFrameWnd::OnCmdMsg 函数内部的处理
CView* pView = GetActiveView();? this指针是pFrame,根据类之间的关系,框架类对象中保存了视图类对象,然后分别进入三条消息映射机制支脉去处理WM_COMMAND消息”
- 进入视图类的消息映射机制支脉中处理消息
- 进入框架类的消息映射机制支脉中处理消息
- 进入应用程序类的消息映射机制支脉中处理消息
消息只处理一次
进入 pView:OnCmdMsg 函数中,之后进入?CWnd::OnCmdMsg,内部this是pView
因为视图类没有处理函数,3次循环之后返回FALSE退出
进入文档类支脉,this 指针是pDoc
遍历文档类支脉,3次循环后返回FALSE退出
之后返回FALSE,回到起点
this指针是pFrame,进入框架类对象支脉,之后的流程和上面差不多
?this指针是pApp,进入应用程序类对象支脉,之后的流程和上面差不多,找到对应的处理函数
然后调用WM_COMMAND消息处理函数处理消息
另外这个处理顺序函数是一个虚函数,是可以重写,自定义顺序的。
文档类和视图类关系
文档类数据发生变化,刷新视图窗口,都会产生重绘消息
CMyDoc::OnNew()
void CMyDoc::OnNew() {
this->str = "hello world"; //接受到的数据。
// this->UpdateAllViews( NULL );//刷新和这个文档类对象(this)关联的所有视图窗口
//this->m_viewList;
POSITION pos = this->GetFirstViewPosition(); //GetFirstXXXPosition
CView* pView = this->GetNextView(pos); //GetNextXXX
this->UpdateAllViews(pView);//刷新和这个文档类对象(this)关联的除了pView指向的视图窗口
}
CMyView::OnDraw(CDC* pDC)?
void CMyView::OnDraw(CDC* pDC) {
// CMyDoc* pDoc = (CMyDoc*)this->m_pDocument;
CMyDoc* pDoc = (CMyDoc*)this->GetDocument();
pDC->TextOut(100, 100, pDoc->str);
}
视图类成员函数:获取和视图类对象关联的文档类对象,调用GetDocument(
文档类成员函数:当文档类数据发生变化时,调用UpDataAllViews刷新和文档类对象相关联的视图类对象(视图窗口)
?
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!