1 MFC入门
1.1 为什么学习MFC
在
Windows
平台上做
GUI
开发,
MFC
是一个不错的选择。
学习
MFC
不仅可以学习到
MFC
本身,而且可以学习
MFC
框架的设计思想。
1.2 Windows消息机制
基本概念解释
SDK:软件开发工具包(
Software Development Kit
),一般都是一些被软件工程师用于为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件的开发工具的集合。
API函数:Windows操作系统提供给应用程序编程的接口。Windows应用程序API是通过C语言实现的,所有主要的Windows函数都在
Windows.h
头文件中进行了声明。Windows操作系统提供了1000多种API函数。
窗口:一个Windows应用程序至少有一个窗口,称为主窗口。窗口是屏幕上的一块矩形区域,是Windows应用程序与用户进行交互的接口。一个应用程序窗口通常都包含标题栏、菜单栏、系统菜单、最小化框、最大化框、可调边框,有的还有滚动条。窗口可分为客户区和非客户区。窗口可以有一个父窗口,有父窗口的称为子窗口。
在Windows应用程序中,窗口是通过窗口句柄来标识的。要对某个窗口进行操作,首先就要得到这个窗口的句柄。
句柄:在Windows程序中,有各种各样的资源(窗口、图标、光标、画刷等),系统在创建这些资源时会为它们分配内存,并返回这些资源的标识号,即句柄。例如,有图标句柄、光标句柄、画刷句柄。
消息与消息队列:Windows程序设计是一种完全不同于传统DOS方式的程序设计方法。它是一种事件驱动方式的程序设计模式,主要基于消息。
每一个Windows应用程序开始执行后,系统会为该程序创建一个消息队列,这个消息队列用来存放该程序创建的窗口的消息。
例如,用户在窗口中画图时,按下鼠标左键。此时,操作系统会感知到这一事件,于是将该事件包装成一个消息,投递到应用程序的消息队列中,等待应用程序的处理。
然后,应用程序通过一个消息循环不断从消息队列中取出消息,并进行响应。在这个处理过程中,操作系统也会给应用程序“发送消息”。所谓“发送消息”,实际上是操作系统调用程序中一个专门负责处理消息的函数,这个函数称为窗口过程。
WinMain函数:当Windows操作系统启动一个程序时,它调用的是该程序的WinMain函数(实际上是由插入到可执行文件中的启动代码调用的)。Winmain是Windows程序的入口点函数,与DOS程序的入口点函数main作用相当,当WinMain函数结束或返回时,Windows应用程序结束。
Windows编程模型
一个简单但完整
Win32
程序(#include <windows.h>),该程序实现的功能是创建一个窗口,并在该窗口中响应键盘及鼠标消息,程序实现步骤为:
WinMain
函数的定义
int WINAPI WinMain(
HINSTANCE hInstance,// 应用程序实例句柄
HINSTANCE hPrevInstace,// 上一个应用程序实例,在win32环境中,默认为NULL
LPSTR lpCmdLine,// 命令行参数 char * argv[]int nShowCmd // 窗口显示样式)
- 创建一个窗口
// 1.设计窗口
WNDCLASS wc;// 窗口类变量
wc.cbClsExtra =0;// 类的额外内存
wc.cbWndExtra =0;// 窗口的额外内存
wc.hbrBackground =(HBRUSH)GetStockObject(WHITE_BRUSH);// 设置背景
wc.hCursor =LoadCursor(NULL, IDC_HAND);// 设置光标,若第一个参数为NULL,代表使用系统提供的光标
wc.hIcon =LoadIcon(NULL, IDI_ERROR);// 设置图标,若第一个参数为NULL,代表使用系统提供的光标
wc.hInstance = hInstance;// 应用程序实例句柄,为WinMain第1个形参
wc.lpfnWndProc = WinProc;// 回调函数 窗口过程函数名字
wc.lpszClassName =TEXT("MyWin");// 类的名字
wc.lpszMenuName =NULL;// 没有菜单
wc.style =0;// 显示风格,填0,使用默认风格// 2.注册窗口类RegisterClass(&wc);// 3.创建窗口
HWND hWnd =CreateWindow(TEXT("MyWin"),TEXT("测试"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,NULL,NULL, hInstance,NULL);// 4.显示及更新ShowWindow(hWnd, SW_SHOWNORMAL);UpdateWindow(hWnd);
HWND CreateWindow(
LPCTSTR lpClassName,//类名
LPCTSTR lpWindowName,//标题名
DWORD dwStyle,//指定创建的窗口的样式 WS_OVERLAPPEDWINDOWint x,//默认值 CW_USEDEFAULTint y,//指定窗口左上角的x,y坐标int nWidth,//默认值 CW_USEDEFAULTint nHeight,//指定窗口的宽度,高度
HWND hWndParent,//指定被创建窗口的父窗口句柄
HMENU hMenu,//指定窗口菜单的句柄
HINSTANCE hInstance,//窗口所属的应用程序实例的句柄
LPVOID lpParam);//通常设置为NULL
- 建立消息循环
// 5.通过循环取消息
MSG msg;while(1){if(GetMessage(&msg,NULL,0,0)== FALSE){break;}else{TranslateMessage(&msg);//翻译DispatchMessage(&msg);//把收到的消息传到窗口回调函数进行分析和处理}}
/*
* Message structure
*/typedefstructtagMSG{
HWND hwnd;//消息所属窗口
UINT message;//消息标识符
WPARAM wParam;//指定消息的附加信息
LPARAM lParam;//指定消息的附加信息
DWORD time;//标识一个消息产生时的时间
POINT pt;//表示产生这个消息时光标或鼠标的坐标#ifdef_MAC
DWORD lPrivate;#endif} MSG,*PMSG, NEAR *NPMSG, FAR *LPMSG;
BOOL GetMessage(
LPMSG lpMsg,//指向一个消息结构体
HWND hWnd,//指定接收哪一个窗口的消息。设置为NULL,用于接收属于调用线程的所有窗口的窗口消息
UINT wMsgFilterMin,//指定消息的最小值
UINT wMsgFilterMax);//指定消息的最大值//如果wMsgFilterMin和wMsgFilterMax都设置为0, 则接收所有消息。
- 编写窗口过程函数
LRESULT CALLBACK WinProc(// CALLBACK和WINAPI 作用一样
HWND hWnd,// 信息所属的窗口句柄
UINT uMsg,// 消息类型
WPARAM wParam,// 附加信息(如键盘哪个键按下)
LPARAM lParam // 附加信息(如鼠标点击坐标));
完整示例代码
要创建一个
win32
项目,在
VS2019
中点击Windows桌面向导
选择应用程序类型为桌面应用程序,并勾选空项目
注意,添加新建项是
.c
文件
#include<windows.h>
LRESULT CALLBACK WinProc(// CALLBACK和WINAPI 作用一样
HWND hWnd,// 消息所属的窗口句柄
UINT uMsg,// 具体消息名称
WPARAM wParam,// 附加信息(如键盘哪个键按下)
LPARAM lParam // 附加信息(如鼠标点击坐标)){switch(uMsg){case WM_KEYDOWN://键盘按下MessageBox(hWnd,TEXT("键盘按下"),TEXT("键盘"), MB_OK);break;case WM_CLOSE://所有以Window结尾的方法不会进入到消息队列去,而是直接执行DestroyWindow(hWnd);// DestroyWindow 发送另一个消息 WM_DESTROYbreak;case WM_DESTROY:PostQuitMessage(0);break;case WM_LBUTTONDOWN://鼠标左键按下{int xPos =LOWORD(lParam);int yPos =HIWORD(lParam);char buf[1024];wsprintf(buf,TEXT("x = %d, y = %d"), xPos, yPos);MessageBox(hWnd, buf,TEXT("鼠标左键按下"), MB_OK);break;}case WM_PAINT://绘图事件{
PAINTSTRUCT ps;//绘图结构体
HDC hdc =BeginPaint(hWnd,&ps);TextOut(hdc,100,100,TEXT("HELLO!"),strlen("HELLO"));EndPaint(hWnd,&ps);break;}default://以windows默认方式处理returnDefWindowProc(hWnd, uMsg, wParam, lParam);}return0;}// 程序入口函数// #define WINAPI __stdcall 参数的传递顺序:从右到左依次入栈,并在函数返回前清空堆栈int WINAPI WinMain(
HINSTANCE hInstance,// 应用程序实例句柄
HINSTANCE hPrevInstace,// 上一个应用程序实例,在win32环境中,默认为NULL
LPSTR lpCmdLine,// 命令行参数 char * argv[]int nShowCmd // 窗口显示样式){// 1.设计窗口
WNDCLASS wc;// 窗口类变量
wc.cbClsExtra =0;// 类的额外内存
wc.cbWndExtra =0;// 窗口的额外内存
wc.hbrBackground =(HBRUSH)GetStockObject(WHITE_BRUSH);// 设置背景
wc.hCursor =LoadCursor(NULL, IDC_HAND);// 设置光标,若第一个参数为NULL,代表使用系统提供的光标
wc.hIcon =LoadIcon(NULL, IDI_ERROR);// 设置图标,若第一个参数为NULL,代表使用系统提供的光标
wc.hInstance = hInstance;// 应用程序实例句柄,为WinMain第1个形参
wc.lpfnWndProc = WinProc;// 回调函数 窗口过程函数名字
wc.lpszClassName =TEXT("MyWin");// 类的名字
wc.lpszMenuName =NULL;// 没有菜单
wc.style =0;// 显示风格,填0,使用默认风格// 2.注册窗口类RegisterClass(&wc);// 3.创建窗口
HWND hWnd =CreateWindow(TEXT("MyWin"),TEXT("测试"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,NULL,NULL, hInstance,NULL);// 4.显示及更新ShowWindow(hWnd, SW_SHOWNORMAL);UpdateWindow(hWnd);// 5.通过循环取消息
MSG msg;while(1){if(GetMessage(&msg,NULL,0,0)== FALSE){break;}else{TranslateMessage(&msg);//翻译DispatchMessage(&msg);//把收到的消息传到窗口回调函数进行分析和处理}}return0;}
执行结果
1.3 MFC入门
微软基础类库(英语:Microsoft Foundation Classes,简称MFC)是一个微软公司提供的类库(class libraries),以C++类的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。其中包含的类包含大量Windows句柄封装类和很多Windows的内建控件和组件的封装类。
MFC把Windows SDK API函数包装成了几百个类,MFC给Windows操作系统提供了面向对象的接口,支持可重用性、自包含性以及其他OPP原则。MFC通过编写类来封装窗口、对话框以及其他对象,引入某些关键的虚函数(覆盖这些虚函数可以改变派生类的功能)来完成,并且MFC设计者使类库带来的总开销降到了最低。
编写第一个MFC应用
mfc.h
#include<afxwin.h>//mfc头文件classMyApp:publicCWinApp// CWinApp应用程序类{public:virtual BOOL InitInstance();};classMyFrame:publicCFrameWnd{public:MyFrame();};
mfc.cpp
#include"mfc.h"
MyApp app;// 有且只有一个全局的应用程序类对象
BOOL MyApp::InitInstance()// 程序入口地址{
MyFrame* frame =new MyFrame;// 1.创建框架类对象
frame->ShowWindow(SW_SHOWNORMAL);// 2.显示窗口
frame->UpdateWindow();// 3.更新窗口
m_pMainWnd = frame;// 4.保存框架类对象指针(保存指向应用程序的主窗口的指针)return TRUE;// 初始化正常返回TRUE}MyFrame::MyFrame(){Create(NULL,TEXT("mfc"));}
编译项目,报错
C:\Program Files(x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\atlmfc\include\afx.h(24,1): fatal error C1189: #error: Building MFC application with /MD[d](CRT dll version)requires MFC shared dll version. Please #define _AFXDLL ordonot use /MD[d]
解决方法:打开项目属性页,在MFC的使用中选择在共享DLL中使用MFC
成功运行
程序执行流程:
- 程序开始时,先实例化应用程序对象(有且只有一个)
- 执行程序的入口函数
InitInstance()
- 给框架类
MyFrame
对象动态分配空间(自动调用它的构造函数),在其构造函数内部,通过CWnd::Create
创建窗口 - 框架类对象显示窗口
CWnd::ShowWindow
- 框架类对象更新窗口
CWnd::UpdateWindow
- 保存框架类对象指针
CWinThread::m_pMainWnd
CFrameWnd::Create
BOOL Create(
LPCTSTR lpszClassName,// 如果为NULL,使用预定义的缺省CFrameWnd属性
LPCTSTR lpszWindowName,// 指向代表窗口名的以空终止的字符串,用作标题条的文本。
DWORD dwStyle = WS_OVERLAPPEDWINDOW,// 指定窗口风格属性const RECT &rect = rectDefault,// 定义窗口大小和位置
CWnd* pParentWnd =NULL,// 指定框架窗口的父窗口,对最高层框架窗口来说应为NULL
LPCTSTR lpszMenuName =NULL,// 指定与窗口一起使用的菜单资源名
DWORD dwExStyle =0,// 指定窗口扩展的风格属性
CCreateContext* pContext =NULL// 指向CCreateContext结构的指针);
CWnd::ShowWindow
BOOL ShowWindow(int nCmdShow );//SW_SHOWNORMAL 激活并显示窗口。如果窗口是最小化或最大化的,则Windows恢复它原来的大小和位置。 //返回值:如果窗口原来可见,则返回非零值;如果CWnd原来是隐藏的,则返回0。
消息映射机制
消息映射是一个将消息和成员函数相互关联的表。比如,框架窗口接收到一个鼠标左击消息,
MFC
将搜索该窗口的消息映射,如果存在一个处理
WM_LBUTTONDOWN
消息的处理程序,然后就调用
OnLButtonDown
。
将消息映射添加到一个类中所做的全部工作:
- 所操作类中,声明消息映射宏
- 通过放置标识消息的宏来执行消息映射,相应的类将在对
BEGIN_MESSAGE_MAP
和END_MESSAGE_MAP
的调用之间处理消息 - 对应消息处理函数分别在类中声明,类外定义
mfc.h
#include<afxwin.h>//mfc头文件classMyApp:publicCWinApp// CWinApp应用程序类{public:virtual BOOL InitInstance();};classMyFrame:publicCFrameWnd{public:MyFrame();
afx_msg voidOnLButtonDown(UINT nFlags, CPoint point);
afx_msg voidOnChar(UINT, UINT, UINT);
afx_msg voidOnPaint();// 声明宏 提供消息映射机制DECLARE_MESSAGE_MAP()};
mfc.cpp
#include"mfc.h"
MyApp app;// 有且只有一个全局的应用程序类对象
BOOL MyApp::InitInstance()// 程序入口地址{
MyFrame* frame =new MyFrame;// 1.创建框架类对象
frame->ShowWindow(SW_SHOWNORMAL);// 2.显示窗口
frame->UpdateWindow();// 3.更新窗口
m_pMainWnd = frame;// 4.保存框架类对象指针(保存指向应用程序的主窗口的指针)return TRUE;// 初始化正常返回TRUE}BEGIN_MESSAGE_MAP(MyFrame, CFrameWnd)// 开始ON_WM_LBUTTONDOWN()ON_WM_CHAR()ON_WM_PAINT()END_MESSAGE_MAP()// 结束MyFrame::MyFrame(){Create(NULL,TEXT("mfc"));}voidMyFrame::OnLButtonDown(UINT, CPoint point){//TCHAR buf[1024];//wsprintf(buf, TEXT("x = %d, y = %d"), point.x, point.y);//MessageBox(buf);// MFC中使用字符串 CStirng
CString str;
str.Format(TEXT("x = %d, y = %d"), point.x, point.y);MessageBox(str);}voidMyFrame::OnChar(UINT key, UINT, UINT){
CString str;
str.Format(TEXT("按下了%c键"), key);MessageBox(str);}voidMyFrame::OnPaint(){
CPaintDC dc(this);
dc.TextOutW(100,100,CString("NJ"));// 画椭圆
dc.Ellipse(100,100,150,150);}
运行结果
Windows字符集
- 多字节字符集(8位的ANSI字符集)
在Windows98以及以前的版本使用8位ANSI字符集,它类似于我们程序员熟悉的ASCII字符集。
char sz[]="ABCDEFG";char*psz ="ABCDEFG";int len =strlen(sz);
- 宽字符集(16位的Unicode字符集)
在WindowsNT和Windows2000后开始使用16位的Unicode字符集,它是ANSI字符集的一个超集。Unicode适用于国际市场销售的应用程序,因为它包含各种各样来自非U.S.字母表的字符,比如中文,日文,韩文,西欧语言等。
//在字符串前加字母L表示将ANSI字符集转换成Unicode字符集。wchar_t wsz[]= L"ABCDEFG";wchar_t*pwsz = L"ABCDEFG";int len =wcslen(wsz);//测试宽字节字符串的长度
- TEXT(_T)宏
MFC中的TEXT宏可以自动适应字符类型,如果定义了预处理器程序符号_UNICODE,那么编译器将使用Unicode字符,如果没用定义该预处理器程序符号,那么编译器将使用ANSI字符。
MessageBox(TEXT("鼠标左键"));MessageBox(_T("鼠标左键"));
- TCHAR类型
如果定义了_UNICODE符号TCHAR将变为wchar_t类型。如果没用定义_UNICODE符号,TCHAR将变为普通古老的char类型。
char * 与 CString之间的转换
// char * -> CStringchar* p ="ccc";
CString str =CString(p);// CString -> char *
CStringA tmp;
tmp = str;char* pp = tep.GetBuffer();
1.4 用向导生成一个MFC应用
用VS2019创建新项目时,选择MFC应用
项目创建好后,点击视图
→
\to
→类视图
直接运行,显示窗口
打开任务管理器,可以看见进程
双击类名,即可打开
.h
文件
双击类中成员函数,即可打开对应
.cpp
文件
文档/视图结构体系
MFC
应用程序框架结构的基石是文档/视图体系结构,它定义了一种程序结构,这种结构依靠文档对象保存应用程序的数据,并依靠视图对象控制视图中显示的数据,把数据本身与它的显示分离开。
数据的存储和加载由文档类来完成,数据的显示和修改则由视类来完成。
MFC
在类
CDocument
和
CView
中为稳定视图提供了基础结构。
CWinApp
、
CFrameWnd
和其他类与
CDocument
和
CView
合作,把所有的片段连在了一起。
CView
类也派生于
CWnd
类,框架窗口是视图窗口的一个父窗口。主框架窗口(
CFrameWnd
)是整个应用程序外框所包括的部分,即粗框以内的内容,而视类窗口只是主框架中空白的地方。
因此,框架窗口是视窗口的父窗口,那么视类窗口就应该始终覆盖在框架类窗口之上。就好比框架窗口是一面墙,视类窗口就是墙纸,它始终挡在这面墙前边。也就是说,所有操作,包括鼠标单击、鼠标移动等操作都只能由视类窗口捕获。
添加消息处理
在消息列表中找到
WM_LBUTTONDOWN
消息
那么文件中会自动发生三处改动
- 头文件中声明消息处理函数
- 对应cpp文件中添加消息宏
- 添加消息处理函数实现
在函数定义中添加如下代码
voidCmfcGuideView::OnLButtonDown(UINT nFlags, CPoint point){// TODO: 在此添加消息处理程序代码和/或调用默认值CView::OnLButtonDown(nFlags, point);
CString str;
str.Format(TEXT("x = %d, y = %d"), point.x, point.y);MessageBox(str);}
可以达到和之前一样的效果
MFC框架中一些重要的函数
CmfcGuideApp::InitInstance
函数:应用程序类的一个虚函数,MFC应用程序的入口CMainFrame::PreCreateWindow
函数
当框架调用
CreateEx
函数创建窗口时,会首先调用
PreCreateWindow
函数。
通过修改传递给
PreCreateWindow
的结构体类型参数
CREATESTRUCT
,应用程序可以更改用于创建窗口的属性。在产生窗口之前让程序员有机会修改窗口的外观。
最后再调用
CreateWindowEx
函数完成窗口的创建。
CMainFrame::OnCreate
函数
OnCreate
是一个消息响应函数,是响应
WM_CREATE
消息的一个函数,而
WM_CREATE
消息是由
Create
函数调用的。一个窗口创建(Create)之后,会向操作系统发送
WM_CREATE
消息,
OnCreate()
函数主要是用来响应此消息的。
CmfcGuideView::OnDraw
函数
通常我们不必编写
OnPaint
处理函数。当在
View
类里添加了消息处理
OnPaint()
时,
OnPaint()
就会覆盖掉
OnDraw()
。
拓展知识点
MFC
中后缀名为Ex
的函数都是扩展函数- 在
MFC
中,以Afx
为前缀的函数都是全局函数,可以在程序的任何地方调用它们
2 基于对话框编程
2.1 创建基于对话框的MFC应用
点击完成,却报错!
依照博客:VS 2019 解决对COM组件的调用返回了错误HRESULT E_FAIL
以管理员身份打开
Developer Command Prompt for VS 2019
找到
VS2019
安装目录
cd 到文件位置后,输入:
gacutil -i Microsoft.VisualStudio.Shell.Interop.11.0.dll
显示:程序集已成功添加到缓存中,表示已成功!
再依照上述步骤新建基于对话框的MFC应用程序即可。奈何还是无用依旧报错。在网上偶然看见说,项目名称使用全英文,试了下,不报错了。
双击
.rc
文件打开资源视图( 菜单栏
→
\to
→视图
→
\to
→其他窗口
→
\to
→资源视图,同样可以打开)
双击第二个选项
IDD_CDIALOGTEST_DIALOG
即可看见对话框,也一般称为设计界面,打开菜单栏
→
\to
→视图
→
\to
→工具箱,可以进行选择控件拖拽至对话框中
点击运行便弹出程序窗口
另外,打开类视图,可以看见一共有三个类
类名作用CAboutDlg版本信息对话框,从CDialogEx继承过来CCDialogTestApp应用程序类,从CWinApp继承过来CCDialogTestDlg对话框类,从CDialogEx继承过来
其中对话框类中有两个重要的方法函数:
DoDataExchange
:该函数主要完成对话框数据的交换和校验OnInitDialog
:相当于对对话框进行初始化处理
2.3 模态对话框
选中对话框,右击属性,可以修改对话框的标题栏等。
ctrl a + delete
可选中对话框中所有控件并删除
菜单栏
→
\to
→视图
→
\to
→工具箱,拖拽1个按钮进对话框
如何修改按钮的文字?方法一:属性中修改;方法二:直接选中但不双击,直接打字进行修改。(下图为方法一)
接下来,实现点击按钮,弹出一个对话框。
但此时没有多余对话框,咋办?当然先创建出一个对话框:右击
Dialog
,点击插入Dialog
修改其
ID
为
IDD_EXEC
,注意一般为大写
此时,模态对话框是有了。可以在模态对话框中删除默认控件,并添加一个按钮以显示模态对话框弹出。
但是在主对话框中点击按钮是没有反应的。点击模态对话框
→
\to
→右击
→
\to
→添加类
如此,类视图中多了一个自定义类
CDlgExec
方式一:在主对话框中添加按钮处理函数:创建对话框,并以模态方式运行
方式二:选中模态对话框按钮控件,右击选择添加事件处理程序
方式三:直接双击模态对话框按钮控件(因此,和Qt中双击是修改控件文字的操作是不一样的)
添加头文件
#include"CDlgExec.h"
// 模态对话框按钮点击事件voidCCDialogTestDlg::OnBnClickedButton1(){// TODO: 在此添加控件通知处理程序代码
CDlgExec dlg;
dlg.DoModal();//以模态方式运行}
2.4 非模态对话框
与上文类似,先在主对话框中拖入一个按钮,并修改其文字为:非模态对话框
再插入一个对话框,并更改其
ID
为
IDD_SHOW
,并拖入一个按钮,修改文字:非模态对话框弹出
只有对话框可不行,还得为对话框添加类,
CDlgShow
当然,你也就会发现,类试图中多了一个类(其实每个窗口都会对应一个类)
在主对话框中,双击非模态对话框控件,进入该按钮的点击事件处理函数,编写程序
#include"CDlgShow.h"
// 非模态对话框点击事件voidCCDialogTestDlg::OnBnClickedButton2(){// TODO: 在此添加控件通知处理程序代码
CDlgShow dlg;
dlg.Create(IDD_SHOW);
dlg.ShowWindow(SW_SHOWNORMAL);}
运行程序,会发现对话框一闪而过。原因:
dlg
是局部变量,函数运行完就会消失。
解决办法:将
dlg
变量放在类
CCDialogTestDlg
的私有变量中,成为成员变量即可。(记得,添加头文件)
// 非模态对话框点击事件voidCCDialogTestDlg::OnBnClickedButton2(){// TODO: 在此添加控件通知处理程序代码//CDlgShow dlg;
dlg.Create(IDD_SHOW);
dlg.ShowWindow(SW_SHOWNORMAL);}
关闭非模态对话框后,当在主对话框中再次点击非模态对话框按钮时,会报错
原因:
Create
函数只能创建一次,再次创建就会出错。
解决办法:将创建窗口的语句放在
OnInitDialog
下,可以保证只创建一次非模态对话框的窗口
如此一来,在主窗口初始化时便创建了非模态对话框,点击按钮只是显示出来
3 常用控件
3.1 静态文本框 CStaticText
在同一解决方案下继续新建MFC项目,创建好后设定启动项
静态文本框是最简单的控件,它主要用来显示文本信息,不能接受用户输入,一般不需要连接变量,也不需要处理消息。
MFC中的静态文本框等同于Qt中的
QLabel
。它与上述按钮控件一样,同样有两种方式修改文本,不再赘述。
再拖两个按钮控件上去
- 点击按钮1,可将静态文本框设置为:呵呵
- 点击按钮2,可弹出显示窗口,显示静态文本框中内容
在
Qt
中任何控件都有
ObjectName
,但
MFC
中不存在,但是
MFC
可以关联变量。选中控件,右击
→
\to
→添加变量
由于
XXX_STATIC
静态
ID
是不能关联变量,故需把
ID
修改后,再关联变量
修改
ID
后就可以添加为控件添加变量了
修改访问类变量访问权限为
private
,添加类变量名称为
m_text
,点击完成
创建好控件对应的类变量后,自然可以在按钮1的点击处理函数中编程
// 点击按钮,使得哈哈哈哈变为呵呵voidCCStaticTextTestDlg::OnBnClickedButton1(){// TODO: 在此添加控件通知处理程序代码// 设置文本
m_text.SetWindowTextW(TEXT("呵呵"));}
运行程序
在按钮2的点击处理函数中编程
// 点击按钮,获取文本中内容,并且弹出voidCCStaticTextTestDlg::OnBnClickedButton2(){// TODO: 在此添加控件通知处理程序代码
CString str;
m_text.GetWindowTextW(str);MessageBox(str);}
再拖入一个静态文本框控件。右击
→
\to
→添加变量,别忘了改一下控件
ID
在主对话框类
CCStaticTextTestDlg
的
OnInitDialog
初始化函数中添加代码
//设置静态控件窗口风格为位图居中显示
m_pic.ModifyStyle(0xf, SS_BITMAP | SS_CENTERIMAGE);//通过路径获取bitmap句柄#defineHBMP(filepath,width,height)(HBITMAP)LoadImage(AfxGetInstanceHandle(),filepath,IMAGE_BITMAP,width,height,LR_LOADFROMFILE|LR_CREATEDIBSECTION)//宽高设置 按照控件大小设置
CRect rect;
m_pic.GetWindowRect(rect);//静态控件设置bitmap
m_pic.SetBitmap(HBMP(TEXT("./1.bmp"), rect.Width(), rect.Height()));
另外,按钮控件不但可以处理点击事件,也可以像静态文本框控件这样去关联变量,并且可以不用修改其
ID
。关联之后在窗口类中就存在一个私有变量,从而可以借助该变量去设置按钮的文本、属性等。常用接口如下:
接口功能CWnd::SetWindowTextW设置控件内容CWnd::GetWindowTextW获取控件内容CWnd::EnableWindow设置控件是否变灰
voidCCStaticTextTestDlg::OnBnClickedButton3(){// TODO: 在此添加控件通知处理程序代码
m_btn.SetWindowTextW(TEXT("o_o"));
CString str;
m_btn.GetWindowTextW(str);MessageBox(str);
m_btn.EnableWindow(FALSE);}
3.2 编辑框 CEdit
继续新建项目,
CEditCtrlTest
,在对话框中拖入控件
Edit Control
运行程序后,在编辑框中输入按回车,会自动退出对话框。若是想设置为多行编辑框,右击
→
\to
→属性
另外,该控件可以设置属性中
Auto VScroll
和
垂直滚动
达到类似
txt
上下滚动的效果
若是想再实现一个一模一样的编辑框,按住
ctrl
拖动一下即可。在工具箱中拖入两个按钮以实现功能。
为两个编辑框分别添加变量:
m_edit1
,
m_edit2
当对话框中控件较多时,同时添加变量较多时,该如何方便得查看类中的所有变量呢?右击对话框
→
\to
→类向导
→
\to
→成员变量
先在编辑框中添加一下默认文本内容,在
OnInitDialog
函数中编写初始化程序
// TODO: 在此添加额外的初始化代码
m_edit1.SetWindowTextW(TEXT("MFC"));
为
copy
按钮添加处理事件程序
voidCCEditCtrlTestDlg::OnBnClickedButton1(){// TODO: 在此添加控件通知处理程序代码
CString str;
m_edit1.GetWindowText(str);
m_edit2.SetWindowTextW(str);}
为
close
按钮添加事件处理程序
voidCCEditCtrlTestDlg::OnBnClickedButton2(){// TODO: 在此添加控件通知处理程序代码// 退出程序exit(0);}
但是一般而言,我们只希望关闭当前对话框,不希望关闭整个程序
voidCCEditCtrlTestDlg::OnBnClickedButton2(){// TODO: 在此添加控件通知处理程序代码// 退出当前对话框CDialog::OnOK();}
再回过头解决一下小
bug
:单行编辑框点击回车就退出的问题。
解决办法:重写
OnOk
,注释掉其中代码
为了介绍编辑框另一个使用方法——关联基本类型变量,再拖入一个编辑框和两个按钮。并为编辑框添加变量,但此时注意,类别选择
Value
添加设置内容控件的点击处理函数
voidCCEditCtrlTestDlg::OnBnClickedButton3(){// TODO: 在此添加控件通知处理程序代码
m_text = L"hhh";UpdateData(FALSE);}
添加获取内容控件的点击处理函数
voidCCEditCtrlTestDlg::OnBnClickedButton4(){// TODO: 在此添加控件通知处理程序代码UpdateData(TRUE);MessageBox(m_text);}
3.4 组合框 CComboBox
继续在同一解决方案下创建新项目,
ComboBoxCtrl
。在对话框中拖入
Combo Box
控件,经如下设置后运行程序,可显示下拉框的效果
但是一般在开发中,不会通过属性去设置。组合框常用接口如下:
接口功能CComboBox::AddString组合框添加一个字符串CComboBox::SetCurSel设置当前选择项(当前显示第几项),下标从0开始CComboBox::GetCurSel获取组合框中当前选中项的下标CComboBox::GetLBText获取指定位置的内容CComboBox::DeleteString删除指定位置的字符串CComboBox::InsertString在指定位置插入字符串
使用接口自然需要关联变量,为下拉框控件关联变量
在主对话框类的
OnInitdialog
函数中添加下拉框初始化代码
// TODO: 在此添加额外的初始化代码
m_cbx.AddString(TEXT("韩立"));
m_cbx.AddString(TEXT("墨居仁"));
m_cbx.AddString(TEXT("银月"));
m_cbx.AddString(TEXT("紫灵"));
m_cbx.SetCurSel(2);
m_cbx.InsertString(3,TEXT("小瓶"));
m_cbx.DeleteString(1);
组合框常用的事件为:
CBN_SELCHANGE
,当选择组合框某一项时,自动触发此事件。
在控件属性中选择该事件,或者双击控件进入事件处理函数
voidCComboBoxCtrlDlg::OnCbnSelchangeCombo1(){// TODO: 在此添加控件通知处理程序代码int index = m_cbx.GetCurSel();
CString str;
m_cbx.GetLBText(index, str);MessageBox(str);}
3.5 列表控件 CListCtrl
继续新建项目,
ListCtrl
。在工具箱中拖入一个列表控件,并设置属性:
view
→
\to
→
Report
添加变量
列表控件常用接口如下
接口功能CListCtrl::SetExtendedStyle设置列表风格CListCtrl::GetExtendedStyle获取列表风格CListCtrl::InsertColumn插入某列内容,主要用于设置标题CListCtrl::InsertItem在某行插入新项内容CListCtrl::SetItemText设置某行某列的子项内容CListCtrl::GetItemText获取某行某列的内容
去窗口初始化函数中添加初始化代码
// TODO: 在此添加额外的初始化代码
CString str[]={TEXT("姓名"),TEXT("性别"),TEXT("年龄")};for(int i =0; i <3; i++){// 设置表头 参数1->索引 2->内容 3->对齐方式 4->列宽
m_list.InsertColumn(i, str[i], LVCFMT_LEFT,150);}
CString name[]={ L"韩立", L"紫灵", L"元瑶", L"冰风", L"巧倩"};
CString gender[]={ L"男", L"女", L"女", L"女", L"女"};
CString age[]={ L"24", L"22", L"23", L"24", L"25"};// 设置正文 表头不算正文for(int i =0; i <5; i++){int j =0;
m_list.InsertItem(i, name[i]);// 设置这个item其他列的数据
m_list.SetItemText(i,++j, gender[i]);
m_list.SetItemText(i,++j, age[i]);}
可以通过代码继续设置列表属性
m_list.SetExtendedStyle(m_list.GetExtendedStyle()| LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
3.6 树控件 CTreeCtrl
继续新建
MFC
应用,
CTreeCtrl
。在主对话框中拖入控件
CTreeCtrl
,先设置一下属性
接着给树控件关联变量
该控件常用接口如下
接口功能AfxGetApp()获取应用程序对象指针CWinApp::LoadIcon加载自定义图标CImageList::Create创建图像列表CImageList::Add图像列表追加图标CTreeCtrl::SetImageList设置图形状态列表CTreeCtrl::InsertItem插入节点CTreeCtrl::SelectItem设置默认选中项CTreeCtrl::GetSelectedItem获取选中项CTreeCtrl::GetItemText获取某项内容
编程前的准备工作:把ico资源文件放在项目res文件夹中
将
ico
资源文件放好位置后,在资源视图
→
\to
→
Icon
→
\to
→添加资源。注意,
VS2019
需要将资源文件改成
.bmp
后缀的才可以进行添加
在
OnInitDialog
函数中添加控件初始化代码
// TODO: 在此添加额外的初始化代码// 树控件使用// 1.设置图标// 准备HICON图标
HICON icons[4];
icons[0]=AfxGetApp()->LoadIconW(IDI_ICON1);
icons[1]=AfxGetApp()->LoadIconW(IDI_ICON2);
icons[2]=AfxGetApp()->LoadIconW(IDI_ICON3);
icons[3]=AfxGetApp()->LoadIconW(IDI_ICON4);// CImageList list; // 这个需要写到类成员变量中,否则出函数变量会被释放//30, 30: 图片的宽度和高度 ILC_COLOR32:样式 3, 3:有多少图片写多少
list.Create(30,30, ILC_COLOR32,4,4);// 添加具体图片for(int i =0; i <4; i++){
list.Add(icons[i]);}
m_tree.SetImageList(&list, TVSIL_NORMAL);// 2.设置节点
HTREEITEM root = m_tree.InsertItem(TEXT("根节点"),0,0,NULL);
HTREEITEM parent = m_tree.InsertItem(TEXT("父节点"),1,1, root);
HTREEITEM sub1 = m_tree.InsertItem(TEXT("子节点1"),2,2, parent);
HTREEITEM sub2 = m_tree.InsertItem(TEXT("子节点2"),3,3, parent);
树控件常用事件为:
TVN_SELCHANGED
,当选择某个节点时,自动触发此事件
voidCCTreeCtrlDlg::OnTvnSelchangedTree1(NMHDR* pNMHDR, LRESULT* pResult){
LPNMTREEVIEW pNMTreeView =reinterpret_cast<LPNMTREEVIEW>(pNMHDR);// TODO: 在此添加控件通知处理程序代码*pResult =0;
HTREEITEM selItem;//获得选择项
selItem = m_tree.GetSelectedItem();//获取选中的内容
CString cs = m_tree.GetItemText(selItem);MessageBox(cs);}
3.7 标签页的使用
新建
MFC
项目,
CtabCtrl
。将默认控件删除,并拖入
CTabCtrl
控件。
把
TabSheet.h
和
TabSheet.cpp
放在项目文件同级目录,并且添加到工程目录中。
给
ui
上
Tab Control
关联
Control
类型(
CTabSheet
)
两个标签页都需要各自添加对话框,并设置相应属性
自定义类:点击对话框模板
→
\to
→ 右击
→
\to
→ 添加类(
MyDlg1
、
MyDlg2
)
主对话框类中,定义自定义类对象,需要相应头文件
主对话框类中
OnInitDialog()
做初始化工作
// TODO: 在此添加额外的初始化代码
m_tab.AddPage(TEXT("系统管理"),&dlg1, IDD_DIALOG1);
m_tab.AddPage(TEXT("系统设置"),&dlg2, IDD_DIALOG2);
m_tab.Show();
版权归原作者 Star_ID 所有, 如有侵权,请联系我们删除。