目录
视图窗口概述
视图窗口的使用
视图窗口创建流程
命令消息 WM_COMMAND 处理顺序
对象关系
作用:提供了一个用于显示数据的窗口
关于视图窗口
框架窗口的消息映射机制:CMyFrameWnd---CFrameWnd---CWnd
视图窗口的消息映射机制:CMyView---CView---CWnd
如何使用?
在框架窗口的 WM_CREATE 消息处理函数中创建
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs) {
CMyView* pView = new CMyView;
pView->Create(NULL, "MFCView", WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(0, 0, 200, 200), this,
AFX_IDW_PANE_FIRST);
this->m_pViewActive = pView;
return CFrameWnd::OnCreate(pcs);
}
WS_CHILD
, WS_VISIBLE
和 WS_BORDER
是窗口风格(Window Style)的标志,用于指定窗口的样式和行为。
WS_CHILD
表示该窗口是一个子窗口,它位于父窗口内部,并随父窗口的移动和调整大小而相应变化。WS_VISIBLE
表示该窗口是可见的,即在创建后立即显示在屏幕上。WS_BORDER
表示该窗口有一个边框,用于提供视觉上的分隔和装饰AFX_IDW_PANE_FIRST:添加之后,视图窗口就会完全覆盖掉框架窗口的客户区。如果客户由多个视图窗口的话,其他视图窗口的创建函数AFX_IDW_PANE_FIRST+1……即可。
但是多个视图窗口中只能由一个是激活窗口。
想要创建成功需要重写纯虚函数,这个纯虚函数是在父类 CVIEW 中的,父类有纯虚函数,所以是抽象类,子类继承父类也是抽象类。所以需要重写纯虚函数才能成功创建
void CMyView::OnDraw(CDC* pDC) {
pDC->TextOut(100, 100, "CMyView::OnDraw");
}
在未处理WM_PAINT消息时,这个函数是由父类来调用的处理的
这个是调用父类的消息处理函数
void CView::OnPaint()
{
// standard paint routine
CPaintDC dc(this);
OnPrepareDC(&dc);
OnDraw(&dc);
}
这个函数的功能和处理函数WM_PAINT功能是一样的
void CMyView::OnPaint() {
PAINTSTRUCT ps = { 0 };
HDC hdc = ::BeginPaint(this->m_hWnd, &ps);
::TextOut(hdc, 200, 200, "CMyView::OnPaint", strlen("CMyView::OnPaint"));
::EndPaint(this->m_hWnd, &ps);
}
如果重写了WM_PAINT消息,就不会再调用重写的虚函数了,优先交给WM_PAINT消息处理函数处理。
下断点分析
进入这个函数
熟悉的窗口结构体
创建之前的窗口处理函数,(1)注册窗口(2)更改cs未空的成员
埋个钩子,对WM_CREATE消息感兴趣,保存对象到全局变量程序线程信息,并且在钩子回调函数绑定句柄与对象
视图窗口的创建与框架窗口的创建是一样的
给 CMyApp,CMyFrameWnd,CMyView三个添加 WM_COMMAND 消息处理函数
#include
#include "resource.h"
class CMyView : public CView {
DECLARE_MESSAGE_MAP()
public:
void OnDraw(CDC* pDC);
afx_msg void OnPaint();
afx_msg void OnNew();
};
BEGIN_MESSAGE_MAP(CMyView, CView)
ON_COMMAND( ID_NEW, OnNew )
ON_WM_PAINT()
END_MESSAGE_MAP()
void CMyView::OnNew() {
AfxMessageBox("视图类处理了WM_COMMAND消息");
}
void CMyView::OnPaint() {
PAINTSTRUCT ps = { 0 };
HDC hdc = ::BeginPaint(this->m_hWnd, &ps);
::TextOut(hdc, 200, 200, "CMyView::OnPaint", strlen("CMyView::OnPaint"));
::EndPaint(this->m_hWnd, &ps);
}
void CMyView::OnDraw(CDC* pDC) {
pDC->TextOut(100, 100, "CMyView::OnDraw");
}
class CMyFrameWnd : public CFrameWnd {
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnPaint();
afx_msg int OnCreate(LPCREATESTRUCT pcs);
afx_msg void OnNew();
};
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_WM_PAINT()
ON_COMMAND( ID_NEW, OnNew )
ON_WM_CREATE()
END_MESSAGE_MAP()
void CMyFrameWnd::OnNew() {
AfxMessageBox("框架类处理了WM_COMMAND消息");
}
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs) {
CMyView* pView = new CMyView;
pView->Create(NULL, "MFCView", WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(0, 0, 200, 200), this,
AFX_IDW_PANE_FIRST);
this->m_pViewActive = pView;
return CFrameWnd::OnCreate(pcs);
}
void CMyFrameWnd::OnPaint() {
PAINTSTRUCT ps = { 0 };
HDC hdc = ::BeginPaint(this->m_hWnd, &ps);
::TextOut(hdc, 100, 100, "我是框架窗口客户区", strlen("我是框架窗口客户区"));
::EndPaint(this->m_hWnd, &ps);
}
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;
pFrame->Create(NULL, "MFCView", WS_OVERLAPPEDWINDOW, CFrameWnd::rectDefault,
NULL, (CHAR*)IDR_MENU1);
this->m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
可以知道 WM_COMMADN 消息处理的顺序:视图类---框架类---应用程序类
值得一提的是,this->m_pViewActive = pView; 激活当前视图窗口,否则需要先点击以下视图窗口,才能点击 文件--新建。
CMyApp 应用程序类是全局的,有一个成员变量,保存了CMyFrameWnd对象地址
CMyFrameWnd对象有一个成员变量保存了 CMyView 对象的地址
也就说说拿到了CMyApp就可以拿到所有对象。
注意:this->m_pViewActive = pView; 还可以激活当前窗口
把这句代码注释掉对比以下:
没有点击视图窗口,点击 文件--新建,是框架类处理的命令消息
点击视图窗口,点击 文件--新建,是视图类处理的命令消息
添加上后,点击 文件--新建,是视图类处理的命令消息
这句代码可以直接把这个窗口设置为激活状态。