MFC曾在Windows桌面开发领域大放异彩,但随着时间的推移,如今相比各种流行的GUI开发技术(QT、WPF、Flutter、Web技术等),MFC被贴上“古老”、“落后”的标签。
但有大量工业软件采用MFC进行开发,且在AutoCAD二次开发领域,MFC是官方指定的ObjectARX界面开发方式,掌握MFC技术具有一定的现实需求。
本文从整体上介绍MFC单文档和多文档应用程序的整体框架。
核心类
CWinApp - 程序类
- 定义一个全局CWinApp对象,作为程序爆破点
- 重写InitInstance()虚函数,在其中创建框架类对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include<afxwin.h>
class CMyWinApp :public CWinApp { public: virtual BOOL InitInstance() override; }; BOOL CMyWinApp::InitInstance() { CMyFrameWnd* pFrame = new CMyFrameWnd; pFrame->Create(NULL, "MFCView"); m_pMainWnd = pFrame; pFrame->ShowWindow(SW_SHOW); pFrame->UpdateWindow(); return TRUE; }
CMyWinApp theApp;
|
CFrameWnd - 框架类
- 作为容纳菜单、工具条、状态条、视口等元素的容器
- 在其WM_CREATE事件处理函数中创建视图类的对象等
CView - 视图类
使用步骤:
- 自定义程序类和框架类
- 自定义视图类,派生自CView,重写纯虚函数OnDraw(),用于绘制视图
- 在自定义框架类中的WM_CREATE消息处理函数中,调用Create()创建视图窗口,如果指定窗口ID为AFX_IDW_PANE_FIRST,则按照框架窗口大小确定视图大小
- OnDraw()虚函数由父类的WM_PAINT事件处理函数来调用,优先使用该方法来实现视图的绘制
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
| class CMyView :public CView { virtual void OnDraw(CDC* pDC) override; };
void CMyView::OnDraw(CDC* pDC) { pDC->TextOut(50, 50, "我是CMyView", strlen("我是CMyView")); }
class CMyFrameWnd :public CFrameWnd { private: CMyView* pView; DECLARE_MESSAGE_MAP() public: afx_msg void OnPaint(); afx_msg int OnCreate(LPCREATESTRUCT pcs); }; 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, 100, 100, "我是CMyFrameWnd", strlen("我是CMyFrameWnd")); ::EndPaint(this->m_hWnd, &ps); } int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs) { pView = new CMyView; pView->Create(NULL, "MFCView", WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(0, 0, 200, 200), this, AFX_IDW_PANE_FIRST); return CFrameWnd::OnCreate(pcs); }
|
CDocument - 文档类
作用:提供一个用于管理数据的类,封装数据管理(提取、转换、存储等)相关操作,并和视图进行数据交互。
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
| #include <afxwin.h> #include <afxext.h> #include "resource.h"
class CMyDoc :public CDocument {};
class CMyView :public CView { DECLARE_DYNAMIC(CMyView) virtual void OnDraw(CDC* pDC) override; }; IMPLEMENT_DYNAMIC(CMyView, CView) void CMyView::OnDraw(CDC* pDC) { pDC->TextOut(50, 50, "我是CMyView", strlen("我是CMyView")); }
class CMyFrameWnd :public CFrameWnd { };
class CMyWinApp :public CWinApp { public: virtual BOOL InitInstance() override; }; BOOL CMyWinApp::InitInstance() { CMyFrameWnd* pFrame = new CMyFrameWnd; CMyDoc* pDoc = new CMyDoc; CCreateContext cct; cct.m_pCurrentDoc = pDoc; cct.m_pNewViewClass = RUNTIME_CLASS(CMyView); pFrame->LoadFrame(IDR_MENU1, WS_OVERLAPPEDWINDOW, NULL, &cct); m_pMainWnd = pFrame; pFrame->ShowWindow(SW_SHOW); pFrame->UpdateWindow(); return TRUE; }
CMyWinApp theApp;
|
文档类和视图类的关系
- 调用GetDocument()可以获得与视图关联的文档对象
- 当文档类数据发生变化时,调用文档类对象的UpdateAllViews()函数刷新和文档类对象关联的所有视图类对象
ON_COMMAND消息处理优先级
视图类 > 文档类 > 框架类 > 程序类,该过程由代码逻辑决定。
手动创建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
| #include <afxwin.h> #include "resource.h"
class CMyDoc :public CDocument { DECLARE_DYNCREATE(CMyDoc) }; IMPLEMENT_DYNCREATE(CMyDoc, CDocument)
class CMyView :public CView { DECLARE_DYNCREATE(CMyView) public: virtual void OnDraw(CDC* pDC); }; IMPLEMENT_DYNCREATE(CMyView, CView) void CMyView::OnDraw(CDC* pDC) { pDC->TextOut(100, 100, _T("我是视图窗口")); pDC->MoveTo(200, 200); pDC->LineTo(300, 200); pDC->LineTo(300, 300); }
class CMyFrameWnd :public CFrameWnd { DECLARE_DYNCREATE(CMyFrameWnd) }; IMPLEMENT_DYNCREATE(CMyFrameWnd, CFrameWnd)
class CMyWinApp :public CWinApp { public: virtual BOOL InitInstance(); }; BOOL CMyWinApp::InitInstance() { CSingleDocTemplate* pTemplate = new CSingleDocTemplate( IDR_MENU1, RUNTIME_CLASS(CMyDoc), RUNTIME_CLASS(CMyFrameWnd), RUNTIME_CLASS(CMyView) ); AddDocTemplate(pTemplate); OnFileNew(); m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); return TRUE; }
CMyWinApp theApp;
|
单步调试分析其流程:
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
| // 构造单文档模板类对象 CSingleDocTemplate* pTemplate = new CSingleDocTemplate( IDR_MENU1, RUNTIME_CLASS(CMyDoc), RUNTIME_CLASS(CMyFrameWnd), RUNTIME_CLASS(CMyView)){ CDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass); // pTemplate->m_pOnlyDoc=NULL m_pOnlyDoc = NULL; }
// 将文档模板添加到theApp.m_pDocManager.m_templateList链表中 theApp.AddDocTemplate(pTemplate){ if (theApp.m_pDocManager == NULL){ // 初始化程序的文档管理器 theApp.m_pDocManager = new CDocManager; } // 将该模板添加到文档管理器 theApp.m_pDocManager->AddDocTemplate(pTemplate){ m_pDocManager.m_templateList.AddTail(pTemplate); } }
// 创建新文档 OnFileNew(){ m_pDocManager->OnFileNew(){ // 获取模板对象指针 CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead(); // 添加文档 pTemplate->OpenDocumentFile(NULL){ OpenDocumentFile(lpszPathName, TRUE, bMakeVisible){ CDocument* pDocument = NULL; CFrameWnd* pFrame = NULL; pDocument = CreateNewDocument(){ // 创建文档类对象 CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject(); AddDocument(pDocument){ CDocTemplate::AddDocument(pDoc){ // 为文档绑定模板类对象地址 pDoc->m_pDocTemplate = this; } m_pOnlyDoc = pDoc; } } // 创建框架类对象,pDocument为刚刚创建的文档类对象 pFrame = CreateNewFrame(pDocument, NULL){ CCreateContext context; context.m_pCurrentDoc = pDoc; context.m_pNewViewClass = m_pViewClass; // 接下来创建框架和视图,并将视图和文档类对象关联起来 CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject(); pFrame->LoadFrame(... , &context)){ pFrame->Create(..., &context){ pFrame->CreateEx(..., &context){ CREATESTRUCT cs; ... cs.lpCreateParams=lpParam; // 为窗体类名赋值,注册窗体类 PreCreateWindow(cs); // 设置钩子,设置窗口处理函数,绑定窗体指针和句柄 AfxHookWindowCreate(this); // 这里会激发钩子程序,设置好窗体处理函数,在转向WM_CREATE事件响应函数中 CreateWindowEx(..., cs.lpCreateParams); } } } } } } } }
OnCreate(LPCREATESTRUCT lpcs){ CCreateContext* pContext = (CCreateContext*)lpcs->lpCreateParams; OnCreateHelper(lpcs, pContext){ // 这是一个虚函数,可以重写以支持自己创建视图窗体 OnCreateClient(lpcs, pContext){ CreateView(pContext, AFX_IDW_PANE_FIRST){ CWnd* pView = (CWnd*)pContext->m_pNewViewClass->CreateObject(); // 创建视图窗体 pView->Create(..., pContext); } } } }
|
先定义视图类、文档类和主框架类(支持动态创建机制),在应用程序类的InitInstance()中:
- 创建一个单文档模板,并添加到文档管理器的模板列表中
- 调用OnFileNew()创建文档,程序会自动创建框架类对象、视图类对象和文档类对象。
手动创建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
| #include <afxwin.h> #include "resource.h"
class CMyDoc :public CDocument { DECLARE_DYNCREATE(CMyDoc) }; IMPLEMENT_DYNCREATE(CMyDoc, CDocument)
class CMyView :public CView { DECLARE_DYNCREATE(CMyView) public: virtual void OnDraw(CDC* pDC); }; IMPLEMENT_DYNCREATE(CMyView, CView) void CMyView::OnDraw(CDC* pDC) { pDC->TextOut(100, 100, _T("我是视图窗口")); pDC->MoveTo(200, 200); pDC->LineTo(300, 200); pDC->LineTo(300, 300); }
class CMyChildWnd :public CMDIChildWnd { DECLARE_DYNCREATE(CMyChildWnd) }; IMPLEMENT_DYNCREATE(CMyChildWnd, CMDIChildWnd)
class CMyFrameWnd :public CMDIFrameWnd {};
class CMyWinApp :public CWinApp { public: virtual BOOL InitInstance(); }; BOOL CMyWinApp::InitInstance() { CMultiDocTemplate* pTemplate = new CMultiDocTemplate( IDR_MENU2, RUNTIME_CLASS(CMyDoc), RUNTIME_CLASS(CMyChildWnd), RUNTIME_CLASS(CMyView) ); AddDocTemplate(pTemplate);
CMyFrameWnd* pFrame = new CMyFrameWnd(); pFrame->LoadFrame(IDR_MENU1); m_pMainWnd = pFrame; pFrame->ShowWindow(SW_SHOW); pFrame->UpdateWindow();
OnFileNew(); OnFileNew(); OnFileNew(); return TRUE; }
CMyWinApp theApp;
|
多文档与单文档应用程序的创建基本一致,只在某些细节上存在差异,不再单步调试跟踪。先定义视图类、文档类、子框架类(支持动态创建机制),在应用程序类中的InitInstance()中:
- 创建一个多文档模板,并添加到文档管理器的模板列表中
- 创调用OnFileNew()创建文档,程序会自动创建框架类对象、视图类对象和文档类对象
胸中丘壑:四大类关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| theApp: 程序全局对象 m_pMainWnd: 存储框架类对象的地址 m_pViewActive: 存储当前活动视图对象的地址 m_pDocument: 文档类对象地址pDoc m_viewList: 文档的视图对象数组地址 m_pDocManager: 文档管理器 m_templateList: 模板链表pTemplates,单文档和多文档模板存在区别,见后 // 对于单文档模板对象 pTemplate: 单文档模板对象 m_pOnlyDoc: 存储唯一的文档对象 m_pDocClass: RUNTIME_CLASS(CMyDoc) // 先创建这个 m_pFrameClass: RUNTIME_CLASS(CMyFrameWnd) m_pViewClass: RUNTIME_CLASS(CMyView)
// 对于单文档模板对象 pTemplate: 多文档模板对象 m_docList: 存储多个文档对象的地址 m_pDocClass: RUNTIME_CLASS(CMyDoc) m_pFrameClass: RUNTIME_CLASS(CMyFrameWnd) m_pViewClass: RUNTIME_CLASS(CMyView)
|