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

2.创建总头文件
创建一个Header.h,用来包含所有公共的头文件
右键解决方案资源管理器中,本项目的头文件=》添加=》新建项

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

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

创建好后,在此头文件中引入afxwin.h,这个头文件是MFC的头文件
#pragma once
#include <afxwin.h>3.创建总程序类
之前说过,一个MFC程序包含一个总程序类,一个对话框类,现在创建总程序(APP)类
右键项目=》添加=》类,也可以点击新建项,目的都是添加一个类

我们创建一个类名为CMyApp的类,右边会自动生成头文件和cpp文件
基类必须继承CWinApp,因为CWinApp是MFC的总程序类,权限设置为public,确定即可

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

这时候我们需要设置下项目的属性
4.设置项目属性
4.1 打开项目属性设置
两种方法,一是右键项目,选择属性

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

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

4.3 设置窗口程序
连接器=》系统=》子系统=》选择窗口

设置好之后=》应用=》确定,再回到代码处,报错就没了
5.创建对话框资源
右键资源文件=》添加=》资源

选择对话框=》新建

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

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

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

或者是在视图菜单中打开资源视图
找到=》Dialog=>你的对话框id,双击即可再次打开

6.添加对话框类
添加完对话框后,并没有文件可以提供给我们去修改对话框里的内容,我们需要右键对话框=》添加类
输入我们的对话框类名=》旁边会自动继承CDialogEx类,这是对话框的基类,点击确定

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

创建好之后,有些地方会有报错,需要导入一些头文件
#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()
};
#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类中引入我们的对话框,并调用对话框显示的账号
#pragma once
#include <afxwin.h>
class CMyApp :public CWinApp
{
public:
virtual BOOL InitInstance();
};
// 导出类实例
extern CMyApp myApp;#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;
}接下来就可以运行了

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

8.美化控件
因为使用模板生成的MFC项目中有个framework.h,这里面引入了一些美化组件的头文件,我们可以直接复制到我们的Header.h中
#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这样再运行,我们的按钮也变得好看了

9.劫持窗口信息
9.1 重写按钮退出逻辑
在默认生成的对话框中,有两个按钮,一个确认,一个取消,我们点击后发现,这两个按钮都会直接把对话框关闭,终止程序
这并不是我们想要的
实际上内部这两个按钮:
- 点击 OK 按钮 自动调用 OnOK() → 内部执行 EndDialog(IDOK)
- 点击 Cancel 按钮 自动调用 OnCancel() → 内部执行 EndDialog(IDCANCEL)
一般开发中我们也用不到这两个按钮,可以Ctrl+A,Delele,删除
但是如果真的想用,可以重写这两个按钮的方法
#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()销毁窗口
virtual void OnOK() {
// 做自己的操作
DestroyWindow()
}
virtual void OnCancel() {
// 做自己的操作
DestroyWindow()
}9.2 重写Esc退出逻辑
默认按Esc也会退出我们的窗口,这并不是我们想要的
我们需要重写一下PreTranslateMessage()函数,在Win32中有个一TranslateMessage()用来翻译消息,PreTranslateMessage()就是在这个翻译之前供我们写自己的操作
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())
class CMainDlg : public CDialogEx
{
public:
CMainDlg(CWnd* pParent = nullptr); // 标准构造函数
virtual ~CMainDlg();
virtual BOOL OnInitDialog();
}BOOL CMainDlg::OnInitDialog() {
CDialogEx::OnInitDialog(); // 必须调用基类实现
// 书写自己的内容,初始化数据,指定控件聚焦
return TRUE; // 焦点由系统自动分配
}