# 视图

# 视图窗口简述

在 MFC 的设计中,图形的显示的这部分工作,交由 视图窗口 负责。
框架窗口则担容器之任,成为菜单、标题栏、视图窗口等窗口的容身之所,通常不直接在其客户区中绘制。

视图窗口就被设计为一个没有标题栏,只有客户区的窗口,通常覆盖在框架窗口的客户区上。

# CView

在 MFC 中,视图窗口对应的类即 CView ,继承自 CWnd

# CView::OnDraw

CView 中声明了一个纯虚函数 CView::OnDraw ,因此继承必须重写这个函数,用于处理绘制消息;
这个函数是由 CView::OnPanit 调用的。

# 使用视图窗口

代码示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <afxwin.h>
#include <afxext.h>

class CMyView : public CView {
public:
virtual void OnDraw(CDC* pDC);
};

void CMyView::OnDraw(CDC* pDC) {
pDC->TextOut(100, 100, L"CMyView::OnDraw");
}



class CMyFrameWnd : public CFrameWnd {
DECLARE_MESSAGE_MAP()
public:
afx_msg int AFX_MSG_CALL OnCreate(LPCREATESTRUCT);
};

BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_WM_CREATE()
END_MESSAGE_MAP()


int AFX_MSG_CALL CMyFrameWnd::OnCreate(LPCREATESTRUCT pCS) {
CMyView* pView = new CMyView;
pView->Create(nullptr, L"MFCView", WS_CHILD | WS_VISIBLE | WS_BORDER,
CRect{}, this, AFX_IDW_PANE_FIRST); // AFX_IDW_PANE_FIRST及以上的id创建出来的视图窗口,边框将与客户区重叠,不使用我们传递的CRect对象
m_pViewActive = pView;
return CFrameWnd::OnCreate(pCS);
}



class CMyWinApp :public CWinApp {
public:
CMyWinApp();
virtual BOOL InitInstance();
};

CMyWinApp::CMyWinApp() {

}

BOOL CMyWinApp::InitInstance() {
CMyFrameWnd* frame = new CMyFrameWnd;
frame->Create(nullptr, L"MFCBase");
m_pMainWnd = frame;
frame->ShowWindow(SW_SHOW);
frame->UpdateWindow();
return TRUE;
}


CMyWinApp g_theApp;

老朋友了,相信读者阅读起来不会有什么困难,我们为框架窗口的客户区覆盖上了一个视图窗口。


# 文档

# 文档简述

在 MFC 中,将数据的管理交给 文档 负责,再与负责显示数据的 视图 进行数据交互。

# CDocument

MFC 提供的文档类。

一个文档可以同时与多个视图窗口交互

  • 内部维护了一个链表,连接所有与当前文档对象交互的视图对象。

一个视图窗口只能与一个文档交互

  • 内部由一个成员变量指向交互的文档对象。

# 使用文档

代码示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <afxwin.h>
#include <afxext.h>

#include "resource.h"

class CMyDoc : public CDocument {
};

class CMyView : public CView {
DECLARE_DYNCREATE(CMyView);
DECLARE_MESSAGE_MAP()
public:
int AFX_MSG_CALL OnCreate(LPCREATESTRUCT pCs);
virtual void OnDraw(CDC* pDC);
};

IMPLEMENT_DYNCREATE(CMyView, CView);

BEGIN_MESSAGE_MAP(CMyView, CView)
ON_WM_CREATE()
END_MESSAGE_MAP()

int AFX_MSG_CALL CMyView::OnCreate(LPCREATESTRUCT pCs) {
return CView::OnCreate(pCs); // 在此函数内部,建立文档与当前视图的关联
}

void CMyView::OnDraw(CDC* pDC) {
pDC->TextOut(100, 100, L"CMyView::OnDraw");
}



class CMyFrameWnd : public CFrameWnd {
DECLARE_MESSAGE_MAP()
public:
afx_msg int AFX_MSG_CALL OnCreate(LPCREATESTRUCT);
};

BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_WM_CREATE()
END_MESSAGE_MAP()


int AFX_MSG_CALL CMyFrameWnd::OnCreate(LPCREATESTRUCT pCS) {
return CFrameWnd::OnCreate(pCS); // 在此函数内部,通过动态创建机制创建视图窗口,并挂接到框架窗口的主活动视图中。
}



class CMyWinApp :public CWinApp {
public:
CMyWinApp();
virtual BOOL InitInstance();
};

CMyWinApp::CMyWinApp() {

}

BOOL CMyWinApp::InitInstance() {
CMyFrameWnd* frame = new CMyFrameWnd;
CMyDoc* pDoc = new CMyDoc;

CCreateContext createContext;
createContext.m_pCurrentDoc = pDoc; // 绑定文档类对象
createContext.m_pNewViewClass = RUNTIME_CLASS(CMyView); // 绑定视图类的静态成员变量

frame->LoadFrame(IDR_MENU_TOP, WS_OVERLAPPEDWINDOW, nullptr, &createContext); // 创建框架窗口

m_pMainWnd = frame;

frame->ShowWindow(SW_SHOW);
frame->UpdateWindow();
return TRUE;
}


CMyWinApp g_theApp;

这回,我们有了不少改动。

首先,我们先从应用的实例初始化开始看起:

CMyWinApp::InitInstance
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
BOOL CMyWinApp::InitInstance() {
CMyFrameWnd* frame = new CMyFrameWnd;
CMyDoc* pDoc = new CMyDoc;

CCreateContext createContext;
createContext.m_pCurrentDoc = pDoc; // 绑定文档类对象
createContext.m_pNewViewClass = RUNTIME_CLASS(CMyView); // 绑定视图类的静态成员变量

frame->LoadFrame(IDR_MENU_TOP, WS_OVERLAPPEDWINDOW, nullptr, &createContext); // 创建框架窗口

m_pMainWnd = frame;

frame->ShowWindow(SW_SHOW);
frame->UpdateWindow();
return TRUE;
}

我们修改了 CMyWinApp::InitInstance (及 CMyView 类),通过 MFC 的 动态创建机制 ,让 MFC 为我们创建视图窗口,并与主框架窗口建立关联。

说实话,我觉得 MFC 整这么多种选择就很烦 = =,这个也要学哪个也要学,既然要封装不妨封装彻底一点。

这里我们还建立了消息映射 CMyFrameWnd::OnCreateCMyView::OnCreate ,但是并没有做别的事,只是调用了父类实现的消息映射;
实际上并不需要我们去建立消息映射然后调用父类的成员函数,这里是为了方便读者看得更明显。


# 多视图

在一个 不规则框架窗口 中,可以同时存在多个视图窗口。

我们需要在主框架窗口的客户区中,放置不规则框架窗口;
再在不规则框架窗口中放置多个视图窗口。

# CSplitterWnd

拆分窗口类,即不规则框架窗口,其客户区可以放置多个视图窗口。

# 尝试多视图

代码示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <afxwin.h>
#include <afxext.h>

#include "resource.h"

class CMyDoc : public CDocument {
};

class CMyView : public CView {
DECLARE_DYNCREATE(CMyView);
DECLARE_MESSAGE_MAP()
public:
int AFX_MSG_CALL OnCreate(LPCREATESTRUCT pCs);
virtual void OnDraw(CDC* pDC);
};

IMPLEMENT_DYNCREATE(CMyView, CView);

BEGIN_MESSAGE_MAP(CMyView, CView)
ON_WM_CREATE()
END_MESSAGE_MAP()

int AFX_MSG_CALL CMyView::OnCreate(LPCREATESTRUCT pCs) {
return CView::OnCreate(pCs); // 在此函数内部,建立文档与当前视图的关联
}

void CMyView::OnDraw(CDC* pDC) {
pDC->TextOut(100, 100, L"CMyView::OnDraw");
}



class CMyFrameWnd : public CFrameWnd {
DECLARE_MESSAGE_MAP()
public:
afx_msg int AFX_MSG_CALL OnCreate(LPCREATESTRUCT);

virtual BOOL OnCreateClient(LPCREATESTRUCT pCs, CCreateContext* pContext);
private:
CSplitterWnd m_pSplitWnd;
};

BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_WM_CREATE()
END_MESSAGE_MAP()


int AFX_MSG_CALL CMyFrameWnd::OnCreate(LPCREATESTRUCT pCS) {
return CFrameWnd::OnCreate(pCS); // 在此函数内部,通过动态创建机制创建视图窗口
}

// 重写框架窗口创建客户区时调用的成员函数
BOOL CMyFrameWnd::OnCreateClient(LPCREATESTRUCT pCs, CCreateContext* pContext) {
m_pSplitWnd.CreateStatic(this, 1, 2); // 创建布局
m_pSplitWnd.CreateView(0, 0, RUNTIME_CLASS(CMyView), CSize(500,500), pContext);
m_pSplitWnd.CreateView(0, 1, pContext->m_pNewViewClass, CSize(500, 500), pContext);
return TRUE;
}


class CMyWinApp :public CWinApp {
public:
CMyWinApp();
virtual BOOL InitInstance();
};

CMyWinApp::CMyWinApp() {

}

BOOL CMyWinApp::InitInstance() {
CMyFrameWnd* frame = new CMyFrameWnd;
CMyDoc* pDoc = new CMyDoc;

CCreateContext createContext;
createContext.m_pCurrentDoc = pDoc; // 绑定文档类对象
createContext.m_pNewViewClass = RUNTIME_CLASS(CMyView); // 绑定视图类的静态成员变量

frame->LoadFrame(IDR_MENU_TOP, WS_OVERLAPPEDWINDOW, nullptr, &createContext); // 创建框架窗口

m_pMainWnd = frame;

frame->ShowWindow(SW_SHOW);
frame->UpdateWindow();
return TRUE;
}


CMyWinApp g_theApp;