MFC使用
MFC是一个封装了WIN32 API的Windows下UI设计库。
一、WIN API
#include<windows.h>
windows使用句柄控制对象, Windows系统程序的入口为winMain
1.1 windows的消息处理机制
Windows程序设计是一种事件驱动方式的程序设计模式,主要是基于消息的。
每一个 Windows 应用程序开始执行后, 系统都会为该程序创建一个消息队列, 这个消息队列用来存放该程序创建的窗口的消息。
操作系统会感知到用户的操作事件,然后将事件包装成一个消息,投递到对应的应用程序的消息队列中。
然后应用程序通过一个消息循环不断地从消息队列中取出消息,并进行响应。
在这个处理过程中,操作系统也会给应用程序“ 发送消息”。所谓“ 发送消息”,实际上是操作系统调用程序中一个专门负责处理消息的函数,这个函数称为窗口过程。


二、MFC入门
MFC开发相关帮助文档:VC++之MFC类库中文手册、MSDN
2.1 MFC程序创建
MFC应用程序有程序类CWinAPP,每一个MFC应用程序都会有继承此基类的应用程序类,并且有且仅有一个应用程序类实例,且必须声明为全局。该应用程序类的InitInstance()是程序入口。
在VS2019中使用向导创建应用程序,一般选中基于对话框或基于单个文档(SDI)创建。
常使用"类视图”“资源视图"”工具箱“”属性“这几个VS中的视图参与开发。
”类视图“可以帮助查看项目的类结构
”资源视图“可以看见MFC中的视图资源,也可在此创建、添加资源。
”工具箱“中可以查看MFC的常用控件
”属性“可以查看对象属性,可以在对应的控件右键菜单中的”属性“中跳转到此视图
2.1.1 SDI
MFC应用程序框架结构的基石是文档/视图体系结构,它定义了一种程序结构,这种结构依靠文档对象保存应用程序的数据,并依靠视图对象控制视图中显示的数据,把数据本身与它的显示分离开。
数据的存储和加载由文档类来完成,数据的显示和修改则由视类来完成。 MFC在类CDocument和CView中为稳定视图提供了基础结构。CWinApp、CFrameWnd和其他类与CDocument和CView合作,把所有的片段连在了一起。
VS2019默认创建的SDI程序可直接运行,可以在用户界面功能和高级功能中裁剪
2.1.1 项目属性设置
- 字符集,使用Unicode字符集,可在”项目“-”属性“-”配置属性“-”常规“-”字符集“中选择
-
- TEXT(_T)宏,此宏可以自动适应字符类型,如果定义了预处理器程序符号_UNICODE,那么编译器将使用Unicode字符,如果没用定义该预处理器程序符号,那么编译器将使用ANSI字符。
- TCHAR类型,如果定义了_UNICODE符号TCHAR将为wchar_t类型。如果没用定义_UNICODE符号,TCHAR将变为普通古老的char类型。
- 添加支持_s函数的预处理命令,_CRT_SECURE_NO_WARNINGS,在“项目”-“属性”-“C/C++”-“预处理器”-“预处理器定义”中添加
2.2 消息映射机制
消息映射是一个将消息和成员函数相互关联的表。比如,框架窗口接收到一个鼠标左击消息,MFC将搜索该窗口的消息映射,如果存在一个处理WM_LBUTTONDOWN消息的处理程序,然后就调用OnLButtonDown。
2.2.1 将消息映射添加到一个类中所做的全部工作
- 在所操作的类中声明消息映射宏 DECLARE_MESSAGE_MAP() ,注意,后面不用加分号
- 放置标志消息的宏来执行消息映射,相应的类将在对BEGIN_MESSAGE_MAP和END_MESSAGE_MAP的调用之间处理消息。
2.2.2 自定义消息并设置消息映射
- 在头文件中自定义消息,NM_A是自定义的消息名,100是自定义的偏移值,WM_USER+100唯一表示NM_ANM_A
1 #define NM_A (WM_USER+100)
- 在需要响应此消息的类定义在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间添加消息映射。ON_MESSAGE()表示这是一个自定义消息的映射,NM_A是响应的消息,OnMyChange是自定义的响应函数。
1 ON_MESSAGE(NM_A, OnMyChange)
- 自定义响应函数可以参考
1 LRESULT CMainFrame::OnMyChange(WPARAM wParam, LPARAM lParam){}
- 触发消息可以参考
1 //类比::PostMessage和PostMessage和SendMessage 2 ::PostMessage(AfxGetMainWnd()->GetSafeHwnd(), NM_C,(WPARAM)NM_C, (LPARAM)0);
2.3 MFC框架中的一些重要函数
- InitInstance函数,应用程序类的一个虚函数,MFC应用程序的入口。
- PreCreateWindow函数,当框架调用CreateEx函数创建窗口时,会首先调用PreCreateWindow函数。通过修改传递给PreCreateWindow的结构体类型参数CREATESTRUCT,应用程序可以更改用于创建窗口的属性。在产生窗口之前让程序员有机会修改窗口的外观。最后再调用reateWindowEx函数完成窗口的创建。
- OnCreate函数,OnCreate是一个消息响应函数,是响应WM_CREATE消息的一个函数,而WM_CREATE消息是由Create函数调用的。一个窗口创建(Create)之后,会向操作系统发送WM_CREATE消息,OnCreate()函数主要是用来响应此消息的。
OnCreate与Create的区别
-
- Create()负责注册并产生窗口,像动态创建控件中的Create()一样,窗口创建之后会向操作系统发送WM_CREATE消息。
- OnCreate()不产生窗口,只是在窗口显示前设置窗口的属性如风格、位置等。
- OnCreate()是消息WM_CREATE的消息响应函数
- OnDraw和OnPaint
-
- OnPaint是WM_PAINT消息的消息处理函数,在OnPaint中调用OnDraw,一般来说,用户自己的绘图代码应放在OnDraw中。
- OnPaint()是CWnd的类成员,负责响应WM_PAINT消息。
- OnDraw()是CView的成员函数,没有响应消息的功能。
- 当在View类里添加了消息处理OnPaint()时,OnPaint()就会覆盖掉OnDraw()。
2.4 扩展知识
- MFC中后缀名为Ex的函数都是扩展函数。
- 在MFC中,以Afx为前缀的函数都是全局函数,可以在程序的任何地方调用它们。
- OnInitXXXX函数,如OnInitDlg是每种窗口对应的初始化函数
2.5 对话框
2.5.1 基于对话框的项目
2.5.2 模态窗口
1 CDialog dlg; 2 dlg.DoModal();
2.5.3 非模态窗口
1 CDialog dlg; //此对话框定义如果放在某一函数体内,则当程序离开此函数体会导致变量失效 2 dlg.Create(IDD_DIALOG_SHOW); //IDD_DIALOG_SHOW是对话框模板资源的ID,参数二是父窗口,缺省值是主程序窗口 3 dlg.ShowWindow(SW_SHOWNORMAL);
2.5.4 Dialog的属性
- 边框,常见是对话框外框,还可选择None
- 样式,常见是Popup,内嵌时选中Child
2.5.5 WM_CLOSE
对话框点击右上角的关闭按键会触发WM_CLOSE消息,重写其对应的OnClose事件可以改变其默认行为
2.6 常用控件
2.6.1 静态文本框CStatic
Static Text一般只用来显示文本信息,且一般不需要连接变量。
如果需要连接变量,需要修改ID,因为所有的静态文本框的缺省ID都是IDC_STATIC
常用接口
接口
|
功能
|
CWnd::SetWindowText
|
设置控件内容
|
CWnd::GetWindowText
|
获取控件内容
|
CStatic::SetBitmap
|
设置位图(后缀为bmp的图片)
|
静态文本框可以加载bmp图片
1 //设置静态控件窗口风格为位图居中显示 2 m_label.ModifyStyle(0xf, SS_BITMAP | SS_CENTERIMAGE); 3 //通过路径获取bitmap句柄 4 #define HBMP(filepath,width,height)(HBITMAP)LoadImage(AfxGetInstanceHandle(),filepath,IMAGE_BITMAP,width,height,LR_LOADFROMFILE|LR_CREATEDIBSECTION) 5 //静态控件设置bitmap 6 m_label.SetBitmap(HBMP(TEXT("./1.bmp"), 300, 250));
2.6.2 普通按键CButton
Button
常用接口
接口
|
功能
|
CWnd::SetWindowText
|
设置控件内容
|
CWnd::GetWindowText
|
获取控件内容
|
CWnd::EnableWindow
|
设置控件是否可用
|
2.6.3 编辑框CEdit
Edit Control,默认只支持单行,不接收回车,在控件上按下回车会跳转到OnOK(),会导致当前窗口被关闭。
解决此bug:重写OnOK方法,
关联变量
EditControl控件可以关联控件变量和值变量
- 关联控件变量,使用SetWindowText和GetWindowText来操作控件文本
- 关联值变量,使用UpdateData()操作控件文本,当参数为TRUE时将控件本文同步到值变量;当参数为FALSE时将值变量的值同步到控件文本
关联值变量时可以选择值变量的类型,参考文本的显示需求
常用属性设置
属性
|
含义
|
Number
|
True只能输入数字
|
Password
|
True密码模式
|
Want return
|
True接收回车键,自动换行,只有在多行模式下,才能换行
|
Multiline
|
True多行模式
|
Auto VScroll
|
True 当垂直方向字符太多,自动出现滚动条,同时设置Vertical Scroll才有效
|
Vertical Scroll
|
True当垂直方向字符太多,自动出现滚动条,和Auto VScroll配合使用
|
Horizontal Scroll
|
True当垂直方向字符太多,自动出现滚动条,和Auto HScroll配合使用
|
Read Only
|
True 只读
|
常用接口
接口
|
功能
|
CWnd::SetWindowText
|
设置控件内容
|
CWnd::GetWindowText
|
获取控件内容
|
2.6.4 组合框CComboBox
Combo Box是下拉框
常用属性设置
属性
|
含义
|
data
|
设置内容,不同内容间用英文的分号“;”分隔
|
type
|
显示风格,选择DropList则下拉框的item不能通过键盘修改
|
Sort
|
True 内容自动排序
|
常用接口
接口
|
功能
|
CComboBox::AddString
|
组合框添加一个字符串
|
CComboBox::SetCurSel
|
设置当前选择项(当前显示第几项),下标从0开始
|
CComboBox::GetCurSel
|
获取组合框中当前选中项的下标
|
CComboBox::GetLBText
|
获取指定位置的内容
|
CComboBox::DeleteString
|
删除指定位置的字符串
|
CComboBox::InsertString
|
在指定位置插入字符串
|
常用事件
CBN_SELCHANGE,当下拉框选择改变时会触发此事件
2.6.5 列表控件CListCtrl
List Control控件
设置列表风格时常选择LVS_EX_GRIDLINES 和 LVS_EX_FULLROWSELECT
常用属性设置
view->Report(报表方式)
常用接口
接口
|
功能
|
CListCtrl::SetExtendedStyle
|
设置列表风格
|
CListCtrl::GetExtendedStyle
|
获取列表风格
|
CListCtrl::InsertColumn
|
插入某列内容,主要用于设置标题
|
CListCtrl::InsertItem
|
在某行插入新项内容
|
CListCtrl::SetItemText
|
设置某行某列的子项内容
|
CListCtrl::GetItemText
|
获取某行某列的内容
|
2.6.5 树控件CTreeCtrl
Tree Control的节点可以设置父子关系,且节点可以设置图标
节点图标设置
- 导入图标资源:将图标资源放入项目中的res文件夹,并在资源视图中右键icon->添加资源中添加相应资源。
- 加载图标,可以在OnInitXXX方法中完成
1 //加载图标 2 HICON icon[3]; 3 icon[0] = AfxGetApp()->LoadIconW(IDI_ICON1); 4 icon[1] = AfxGetApp()->LoadIconW(IDI_ICON2); 5 icon[2] = AfxGetApp()->LoadIconW(IDI_ICON3); 6 //定义图像列表对象,需要非临时对象 7 CImageList m_imageList; 8 //创建图像列表对象的空间 m_imageList.Create(30,30,ILC_COLOR32,3,3); //参数一二是图标的横纵像素,参数三是像素级别,参数四和参数五一般保存一致,参数四是列表中最初包含的图标数 9 //给图像列表添加图片 10 for (int i = 0; i < 3; i++) 11 { 12 //图片列表加载图标 13 m_imageList.Add(icon[i]); 14 } 15 //树控件设置图片列表 16 m_treeCtrl.SetImageList(&m_imageList, TVSIL_NORMAL); 17 //给树创建节点 18 //根节点,父节点,子节点 19 HTREEITEM root = m_treeCtrl.InsertItem(TEXT("中国"), 0, 0, NULL); //参数一是节点文本,参数二是节点未选择状态下的图标(ImageList中的序号),参数三是节点被选中状态下的图标,参数四是父节点,参数五是子节点 20 HTREEITEM fathter = m_treeCtrl.InsertItem(TEXT("北京"), 1, 1, root); 21 HTREEITEM son = m_treeCtrl.InsertItem(TEXT("海淀"), 2, 2, fathter);
常用属性设置
has buttons
|
True 有展开按钮
|
has lines
|
True 有展开线
|
lines at root
|
True 根节点有展开线
|
常用接口
接口
|
功能
|
AfxGetApp()
|
获取应用程序对象指针
|
CWinApp::LoadIcon
|
加载自定义图标
|
CImageList::Create
|
创建图像列表
|
CImageList::Add
|
图像列表追加图标
|
CTreeCtrl::SetImageList
|
设置图形状态列表
|
CTreeCtrl::InsertItem
|
插入节点
|
CTreeCtrl::SelectItem
|
设置默认选中项
|
CTreeCtrl::GetSelectedItem
|
获取选中项
|
CTreeCtrl::GetItemText
|
获取某项内容
|
常用事件
TVB_SELCHANGED
2.6.6 标签控件CTabCtrl
Tab Control可以将一些对话框内嵌在tab页内,只要创建非模态对话框,并且在非模态对话框Create时设定父对象为TabCtrl控件
常用接口
接口
|
功能
|
CWnd::GetClientRect()
|
|
CTabCtrl::GetItemRect
|
|
CDialog::SetWindowPos
|
|
CTabCtrl::InsertItem
|
设置标题头
|
CWnd::ShowWindow
|
可选SW_SHOW和SW_HIDE
|
CTabCtrl::GetCurFocus
|
返回被选中的标签下标
|
CTabCtrl::GetCurSel
|
获取当前选中
|
CTabCtrl::SetCurSel
|
获取选中项
|
2.7 静态拆分窗口
- 首先定义需要的视图类,可以是自定义类,也可以是模板类,但是他们的基类必须是视图类
- 使用CSplitterWnd类型对象
1 CSplitterWnd m_splitter;
- 重写框架类的OnCreateClient方法
1 // 创建静态拆分窗口,1行2列,CSplitterWnd::CreateStatic 2 m_spliter.CreateStatic(this, 1, 2); 3 // 创建视图:CSplitterWnd::CreateView 4 //0, 0 : 放在第0行第0列的位置 5 //RUNTIME_CLASS(CSelectView) :需要头文件#include "SelectView.h", CSelectView在SelectView.h中声明 6 // CSize(250, 500):指定视图宽度和高度 7 //pContext : 为OnCreateClient()最后一个形参 8 m_spliter.CreateView(0, 0, RUNTIME_CLASS(CSelectView), CSize(200, 500), pContext); 9 //0, 1: 放在第0行第1列的位置 10 //CDispalyView,需要头文件#include "DispalyView.h" 11 m_spliter.CreateView(0, 1, RUNTIME_CLASS(CDispalyView), CSize(600, 500), pContext); 12 //return CFrameWnd::OnCreateClient(lpcs, pContext); 13 return TRUE;
2.8 挂载窗口
1 CCreateContext Context; 2 //CUserDlg类需要包含头文件#include "UserDlg.h" 3 Context.m_pNewViewClass = RUNTIME_CLASS(CUserDlg); 4 Context.m_pCurrentFrame = this; 5 Context.m_pLastView = (CFormView *)m_spliter.GetPane(0, 1); 6 m_spliter.DeleteView(0, 1); 7 m_spliter.CreateView(0, 1, RUNTIME_CLASS(CUserDlg), CSize(600,500), &Context); 8 CUserDlg *pNewView = (CUserDlg *)m_spliter.GetPane(0, 1); 9 m_spliter.RecalcLayout(); 10 pNewView->OnInitialUpdate(); 11 m_spliter.SetActivePane(0, 1);
一些全局
- 加载图标
1 HICON icon = AfxGetApp()->LoadIconW(IDR_XXX); //IDR_XXX是图标模板资源的ID
- 设置窗口图标
1 //设置图标,IDI_ICON_WIN为图标资源ID,此为WINAPI函数 2 //如果是修改主界面图标,则在OnCreate中 3 SetClassLong(m_hWnd, GCL_HICON,(LONG),AfxGetApp()->LoadIconW(IDI_ICON_WIN)); 4 //m_hWnd是窗口句柄,只要是CWnd树下的类都具有此成员变量
- 设置窗口大小和居中显示
1 MoveWindow(0,0,800,500); 2 CenterWindow();
- CTreeView中有CTreeCtrl成员,可以使用GetTreeCtrl()获取
- 在SDI程序中以对话框开始,在XXXApp::InitInstance方法中,在CWinApp::InitInstance();前创建模态窗口
一些基于向导的操作
- 在资源视图,在具体的资源上右键,添加类、添加变量、类向导
- 在属性界面,消息界面可以创建消息对应的向导
- 对于菜单编辑,可以在具体的菜单节点上“右键”-“添加事件处理程序”定义菜单节点的点击响应函数
- 在添加类时,可以选择修改基类;在添加变量时,有些控件可以选择添加值变量和控件变量
- 添加变量时发生的事:
-
- 在所属类的声明中声明了对应变量
- 在所属类定义中,DoDataExchange方法中关联变量和模板
1 DDX_Control(pDX, IDC_COMBO1, m_Cbx); 2 DDX_Text(pDX, IDC_EDIT1, m_price1);
- 在所属类的构造函数定义的列表初始化中初始化
- 点击控件,然后输入文字,即替换控件的文本;双击控件,会创建并跳转到点击控件的响应函数
- 修改SDI程序的主界面标题,其中包含主标题和副标题。
-
- 主标题在Doc类中修改,在OnNewDocument方法中使用SetTitle方法
- 副标题在框架类(Form)中修改,在OnCreate方法中使用SetTitle方法
- 继承View创建的类,可能会提示View基类未定义,此时要包含头文件# include
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)