Skip to content

手写MFC框架

1.创建项目

直接创建一个c++空项目,本项目命名为Blank_MFC,创建好之后目录中没有任何内容

image-20250619113231013

2.创建总头文件

创建一个Header.h,用来包含所有公共的头文件

右键解决方案资源管理器中,本项目的头文件=》添加=》新建项

image-20250619113612183

在弹出的窗口中,选择头文件=》输入文件名Header.h=》创建

image-20250619113724472

如果打开的窗口不是这样的,可能是默认打开了紧凑视图,点击显示所有模板即可

image-20250619113812044

创建好后,在此头文件中引入afxwin.h,这个头文件是MFC的头文件

c++
#pragma once
#include <afxwin.h>

3.创建总程序类

之前说过,一个MFC程序包含一个总程序类,一个对话框类,现在创建总程序(APP)类

右键项目=》添加=》类,也可以点击新建项,目的都是添加一个类

image-20250619114156846

我们创建一个类名为CMyApp的类,右边会自动生成头文件和cpp文件

基类必须继承CWinApp,因为CWinApp是MFC的总程序类,权限设置为public,确定即可

image-20250619114319625

创建好之后可以发现项目多了个CMyApp.hCMyApp.cpp,但是发现头文件中显示CWinApp不是类名或者结构名

image-20250619114806130

这时候我们需要设置下项目的属性

4.设置项目属性

4.1 打开项目属性设置

两种方法,一是右键项目,选择属性

image-20250619114946686

而是在vs的菜单栏,找到项目=》属性

image-20250619140412016

4.2 设置MFC的使用

高级=》MFC的使用=》在静态库中使用MFC

image-20250619140512491

4.3 设置窗口程序

连接器=》系统=》子系统=》选择窗口

image-20250619140721059

设置好之后=》应用=》确定,再回到代码处,报错就没了

5.创建对话框资源

右键资源文件=》添加=》资源

image-20250619142050870

选择对话框=》新建

image-20250619142151463

可以看到新增了一个对话框资源,并且多了一个对话框的编辑页

image-20250619142223330

单机这个对话框,找到属性页,可以看到这个对话框有个id属性,我们也可以自定义修改这个id

image-20250619142557904

5.1 如何找到对话框页面

有时候我们可能会把这个对话框编辑页关掉,想再次找到,需要双击刚才新建的资源文件,会跳到资源视图

image-20250619142409459

或者是在视图菜单中打开资源视图

找到=》Dialog=>你的对话框id,双击即可再次打开

image-20250619142452102

6.添加对话框类

添加完对话框后,并没有文件可以提供给我们去修改对话框里的内容,我们需要右键对话框=》添加类

输入我们的对话框类名=》旁边会自动继承CDialogEx类,这是对话框的基类,点击确定

image-20250619143030476

可以看到创建的对话框的头文件和cpp已经生成好了,并且每个文件里有一些内容

image-20250619143228256

创建好之后,有些地方会有报错,需要导入一些头文件

c++
#pragma once
#include "afxdialogex.h"
#include <afxwin.h>					// 导入mfc标准库	
#include "resource.h"				// 导入资源头文件
// 删除pch.h的导入

// CMainDlg 对话框

class CMainDlg : public CDialogEx
{
	DECLARE_DYNAMIC(CMainDlg)

public:
	CMainDlg(CWnd* pParent = nullptr);   // 标准构造函数
	virtual ~CMainDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_DIALOG1 };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()
};
c++

#include "CMainDlg.h"


// CMainDlg 对话框

IMPLEMENT_DYNAMIC(CMainDlg, CDialogEx)

CMainDlg::CMainDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DIALOG1, pParent)
{

}

CMainDlg::~CMainDlg()
{
}

void CMainDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}


BEGIN_MESSAGE_MAP(CMainDlg, CDialogEx)
END_MESSAGE_MAP()


// CMainDlg 消息处理程序

7.显示对话框

接下来需要再CMyApp类中引入我们的对话框,并调用对话框显示的账号

c++
#pragma once
#include <afxwin.h>

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

// 导出类实例
extern CMyApp myApp;
c++
#include "CMyApp.h"
#include "CMainDlg.h"			// 导入对话框头文件

// 创建主程序实例
CMyApp myApp; 

BOOL CMyApp::InitInstance() {
    // m_pMainWnd是MFC中一个指针变量,需要指向主对话框类实例
	CMainDlg dlg_Main;
	m_pMainWnd = &dlg_Main;
	dlg_Main.DoModal();			// DoModal()打开对话框

	return TRUE;
}

接下来就可以运行了

image-20250619150244740

但是仔细看这个按钮的样式,并不如使用模板生成的MFC项目美观

image-20250619151009483

8.美化控件

因为使用模板生成的MFC项目中有个framework.h,这里面引入了一些美化组件的头文件,我们可以直接复制到我们的Header.h中

c++
#pragma once
#include <afxwin.h>

#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN            // 从 Windows 头中排除极少使用的资料
#endif

//#include "targetver.h"

#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS      // 某些 CString 构造函数将是显式的

// 关闭 MFC 的一些常见且经常可放心忽略的隐藏警告消息
#define _AFX_ALL_WARNINGS

#include <afxext.h>         // MFC 扩展


#include <afxdisp.h>        // MFC 自动化类


#ifndef _AFX_NO_OLE_SUPPORT
#include <afxdtctl.h>           // MFC 对 Internet Explorer 4 公共控件的支持
#endif
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>             // MFC 对 Windows 公共控件的支持
#endif // _AFX_NO_AFXCMN_SUPPORT

#include <afxcontrolbars.h>     // MFC 支持功能区和控制条


#ifdef _UNICODE
#if defined _M_IX86
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_X64
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#else
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
#endif

这样再运行,我们的按钮也变得好看了

image-20250619151445586

9.劫持窗口信息

9.1 重写按钮退出逻辑

在默认生成的对话框中,有两个按钮,一个确认,一个取消,我们点击后发现,这两个按钮都会直接把对话框关闭,终止程序

这并不是我们想要的

实际上内部这两个按钮:

  • 点击 OK 按钮 自动调用 OnOK() → 内部执行 EndDialog(IDOK)
  • 点击 Cancel 按钮 自动调用 OnCancel() → 内部执行 EndDialog(IDCANCEL)

一般开发中我们也用不到这两个按钮,可以Ctrl+ADelele,删除

但是如果真的想用,可以重写这两个按钮的方法

c++
#pragma once
#include "afxdialogex.h"
#include <afxwin.h>				
#include "resource.h"			

// CMainDlg 对话框
class CMainDlg : public CDialogEx
{
	DECLARE_DYNAMIC(CMainDlg)

public:
	CMainDlg(CWnd* pParent = nullptr);   // 标准构造函数
	virtual ~CMainDlg();
public:
	virtual void OnOK() {
		// 做自己的操作
		EndDialog(IDOK);
	}
	virtual void OnCancel() {
		// 做自己的操作
		EndDialog(IDCANCEL);
	}

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_DIALOG1 };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()
};

除了EndDialog(),还可以调用DestroyWindow()销毁窗口

c++
virtual void OnOK() {
    // 做自己的操作
    DestroyWindow()
}
virtual void OnCancel() {
    // 做自己的操作
    DestroyWindow()
}

9.2 重写Esc退出逻辑

默认按Esc也会退出我们的窗口,这并不是我们想要的

我们需要重写一下PreTranslateMessage()函数,在Win32中有个一TranslateMessage()用来翻译消息,PreTranslateMessage()就是在这个翻译之前供我们写自己的操作

c++
class CMainDlg : public CDialogEx
{
public:
	CMainDlg(CWnd* pParent = nullptr);   // 标准构造函数
	virtual ~CMainDlg();
public:
	virtual BOOL PreTranslateMessage(MSG* pMsg) {
		if (pMsg->message == WM_KEYDOWN&&pMsg->wParam==VK_ESCAPE) {
			return TRUE;
		}
		return CDialogEx::PreTranslateMessage(pMsg);
	}

	// ..........
};

10.初始化信息

一个对话框中会包含很多控件,我们一般会给某些控件设置初始值,当然这并不是在Dialog的构造函数中

我们需要重写对话框的OnInitDialog()函数

作用:

  • 当通过 DoModal()Create()CreateIndirect() 创建对话框时,系统在显示前发送 WM_INITDIALOG 消息,触发 OnInitDialog。用于设置控件初始状态(如文本、可见性)、加载数据、调整布局或动态创建控件。

返回值逻辑

  • 返回 TRUE:系统自动将输入焦点设为对话框的第一个控件。
  • 返回 FALSE:需显式调用 SetFocus() 指定焦点控件(如 GetDlgItem(IDC_EDIT1)->SetFocus()
c++
class CMainDlg : public CDialogEx
{
public:
	CMainDlg(CWnd* pParent = nullptr);   // 标准构造函数
	virtual ~CMainDlg();
	virtual BOOL OnInitDialog();
}
c++
BOOL CMainDlg::OnInitDialog() {
    CDialogEx::OnInitDialog();  // 必须调用基类实现
   	// 书写自己的内容,初始化数据,指定控件聚焦
    return TRUE;  // 焦点由系统自动分配
}

MIT Licensed