12. MFC消息编程与控件使用教程

1.前言

在前面我们写控制台项目时,整个思路都很简单,顺着写就完了,缺啥我们就写啥。

但到了窗口程序这里就不一样了,因为它的运行机制是消息循环

所以到这里我们就得开始转化写代码的思路:需要响应什么消息,我们就写什么代码!

比如我们想要控制程序窗口的大小,那我们就得响应窗口大小变化的消息,其它的类似。

甚至可以说,MFC程序其实就是在面对消息编程。

但不用担心,这些消息并不用你记住,只需要知道有这个东西即可,需要的时候直接在MFC里面找,还是非常方便的。

2.MFC消息的使用

老规矩,还是先建一个MFC的项目吧:

image-20231208185803037

这次的名字为:day12–USE_MFC,中间的选项和上一节一样即可。

既然是面向消息编程,那我们就先来看一看有哪些消息,右键项目,可以进入类向导:

image-20231208185944374

同时注意上方的项目、类名不要选错了:

image-20231208190045723

类名为Dlg结尾的就是窗口类,而我们一般也是为窗口添加消息响应。

然后我们就可以看一看这个窗口有哪些消息:

image-20231208190202560

可以看到,这些消息基本都是WM_开头的,即Windows Message,然后后面就跟着消息的名称。

比如常用的WM_CLOSE消息,就是程序关闭时的消息,如果我们想要响应这个消息也很简单,点击一下这个消息,然后点击右上角的添加处理程序,就会自动生成该消息的处理代码:

image-20231208190254099

然后我们回到该类中,可以看到代码中添加了三个东西,首先看到的应该是实现函数:

image-20231208190338590

然后还有消息映射:

image-20231208190421996

最后是头文件中的函数声明:

image-20231208190450791

知道这些的目的是,当你不想要这个函数了,你需要亲自把它们都删除掉!

当然你也可以直接在类向导里面去删,只不过其默认是将代码都注释掉,而不是直接删除。

然后我们试着在这个函数里面写一些我们自己的函数:

image-20231208190630063

然后运行程序,点击右上角的叉关闭窗口,退出程序时就会弹出一个对话框:

image-20231208190707329

是不是发现用MFC写程序确实非常方便!

这里注意一下我写的代码:

  • AfxMessageBox是MFC自己封装的一个对话框窗口,只能MFC里面用,windows提供的对话框函数为MessageBox,可以自己查查该怎么用。
  • L"":由于MFC默认使用的宽字节字符集,所以MFC里面的函数默认都只接受宽字节字符串,如果想要用多字节字符集,可以自己在项目属性里面更改。

同时还有另外一种写法为:_T("") ,这是一种通用的写法,它会在宽字节字符集下变为L"",多字节字符集下去掉L,其本质就是定义的一个宏,可以用前面我们使用的方法自行查看!

void Cday12USEMFCDlg::OnClose()
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	AfxMessageBox(_T("你正在关闭对话框"));  //使用更加通用的方式_T("")
	CDialogEx::OnClose(); //调用父类的默认处理函数,用于关闭窗口
}

这里再介绍几个常用的消息:

鼠标消息 作用
WM_LBUTTONDOWN 鼠标左键按下的消息
WM_LBUTTONUP 鼠标左键松开的消息
WM_LBUTTONDBLCLK 鼠标左键双击消息
WM_RBUTTONDOWN 鼠标右键按下的消息
WM_RBUTTONUP 鼠标右键松开的消息
WM_RBUTTONDBLCLK 鼠标右键双击消息
WM_MOUSEMOVE 鼠标移动的消息

这个很好记,就是几个常用单词的缩写,左是Left,右是Right,即首字母嘛,然后button为按钮,DWON为下,UP为上,DBLdouble双倍,CLKclick点击。

窗口消息 作用
WM_DESTROY 窗口摧毁时消息,此时窗口无法复原
WM_CLOSE 窗口关闭的消息,可以通过不执行CDialogEx::OnClose();达到不关闭窗口的目的
WM_SIZE 窗口大小改变的消息

上面是一些比较常用的消息,其它更多的消息可以自己慢慢遇到了再看。

3.虚函数

前面介绍了消息,我们还可以在旁边看到虚函数这一项:

image-20231208190922916

虚函数就是前面提到过的类的成员函数,前面加了virtual关键字的。

因为我们的类都是继承于MFC的标准类,所以我们就可以重写其基类的函数。

这里注意一下用词:重载重写有区别,简单来说,函数名相同、参数不同为重载,而在父子类之间函数名与参数可以完全都相同,也就是重写。

我们这里可以看到父类标准对话框的函数还是有很多的,比如我们前面提到过的OnInitDialog函数也在里面。

这里添加一个我们可能常会用到的虚函数OnOk

image-20231208191130459

使用方法与前面的相同,这个函数主要用于退出应用程序。

注意:一定要确定自己选择的是正确的项目与对话框类,因为如果我们生成了关于对话框,那每次进入类向导时,其默认选择的对话框类就是那个关于对话框的,这会导致后面的操作无效。

OnCancel所起的作用基本相同,主要是方便我们区分用户是用哪种方式退出程序,以便于做出不同的处理。

当然,你也可以通过注释掉调用父类的函数,这样它就不会退出程序了:

void Cday12USEMFCDlg::OnOK()
{
	// TODO: 在此添加专用代码和/或调用基类
	AfxMessageBox(_T("通过Ok关闭对话框"));

	//CDialogEx::OnOK();
}

生成的三个代码地点前面提到过,这里不再多说,然后我们运行试一试:

image-20231208191602447

这里我们通过按确定,想要退出程序,但弹出一个窗口。

按了确定键却不退出,这就是因为我们注释掉了调用父类函数的代码。

其它虚函数大家也可以自己都试一试,不明白的网上搜就是了。

除了上面所说的消息与虚函数,我们还可以看到:

image-20231208191644041

命令这一项不用管,它用于生成控件消息函数的,但我们有更简便的方法,一般不用它。

然后后面的成员变量方法,就是当前我们选择的这个类中的成员变量成员函数,一般也用不到,大部分时候我们都是直接在代码文件中手写的。

当然你可能也看到了右边的删除处理程序,但它只是将代码注释起来,并没有完全删除,没有强迫症的话,你用它也行。

但消息映射它似乎不会管,反正能手动删还是建议手动删除吧。

以上就是MFC的对话框的基本使用。

4.资源

前面提到了怎么在类向导中操作窗口,但我们并没有说过如何添加一个窗口。

一般我们的程序也不可能就一个窗口,那也太单调了,所以我们还得学会如何创建一个窗口。

而创建窗口在MFC里面也是非常的简单,就两步:

  1. 创建窗口资源
  2. 为窗口资源绑定一个类

首先我们来创建窗口资源,先来到资源视图,如果你没有看到资源视图,怎么找资源视图我应该就不用多说了吧?前面提到过。

image-20231208191820116

我们可以看到这里有许多类型的资源,但这里我们只先看窗口资源,即Dialog,右键Dialog选择插入即可:

image-20231208191916505

然后就生成了一个默认的窗口,其ID为资源视图中所示的:

image-20231208192013216

首先就是改ID,不然以后窗口的ID全是一个名后面跟一串数字,那谁知道它是干嘛的对吧?

直接右键这个对话框资源点击属性(或者直接按F4):

image-20231208192102033

然后改就行了,但要注意别跟其它的ID重复:

image-20231208192216086

自此,我们第一步就完成了,接下来是第二步,为这个窗口绑定类,方法很简单,就是右键这个窗口,添加类:

image-20231208192252112

然后给这个类命名,最好符合MFC的命名规范,那就是类名前加C,类名后加类型,这里是Dlg

还有就是基类,一般我们默认即可:

image-20231208192345868

然后我们就可以看到生成了两个文件:

image-20231208192423975

这两个文件与前面提到的Dlg一样操作,因为他们都是CDialogEx的子类嘛。

但一开始,他并没有初始化函数OnInitDialog,怎么添加还记得吗?Ctrl+Ahift+X打开类向导,选择该项目,该类,然后添加相应的虚函数即可,过程就不再赘述了。

此时我们就可以在主窗口中使用这个窗口,在主窗口类中添加这个窗口的头文件:

image-20231208192715729

然后我们就可以在任意一个函数里面,实例化一个对象,然后调用DoModal函数,比如我这里是在关闭函数里面调用的:

image-20231208192808573

这时我们运行程序,点击关闭,就会发现,我们刚刚创建的窗口弹出来了,是不是用着很简单!

image-20231208192843124

但这样用着有一个问题,那就是如果我们不关闭这个弹出的对话框,那么我们就没法点击它后面的对话框。

这种对话框叫做模态对话框,与之对应的叫做非模态对话框,使用方法如下。

我们直接定义一个新窗口的成员变量:

image-20231208192933394

然后在初始化函数中创建它:

image-20231208193147016

这里调用的Create函数,传入的参数就是这个对话框资源的ID,然后在我们想要使用的地方调用ShowWindow函数即可:

image-20231208193319916

其参数就是win APi的ShowWindow的参数。

这里将后面的那段代码注释掉了,因为不注释的话,程序就直接结束了!

然后你就可以看到,此时两个窗口你都可以随意点击了。

5.控件

说完窗口,我们再来谈谈非常重要的一环,那就是控件。

因为我们几乎所有的应用程序,其目的就是为了接收用户的输入,然后我们进行输出的过程。

而对话框实际上只起到了一个容器的作用,因为几乎所有控件都是在对话框中的。

那什么是控件呢?其实很简单,前面我们用到的按钮就是一个控件。

所谓控件,其实就是官方写好的、具有特定功能的窗口给我们用,本质上来说,控件也是一种特殊的窗口。

那我们就来看看怎么使用控件,以及认识一些我们比较常用的控件。

5.1 按钮控件

首先打开想要添加控件的对话框资源,然后打开工具箱视图,找不到的,可以在 视图->工具箱 中找到:

image-20231208193946572

然后我们就可以看到右边这些多种多样的控件,因为控件实在太多了,所以我这里只介绍几种非常常用的,其它控件后面章节会进行介绍。

首先,来看我们最常用的按钮控件,Button

这里注意一下,已经放在对话框上的控件,我们可以用鼠标选中它们,然后按Delete键进行删除。

然后,我们就可以直接用鼠标左键拖动旁边的控件到对话框中即可:

image-20231208194052053

然后我们对这些控件基本就只有两个操作:

  • 设置控件的属性
  • 响应控件的消息

首先来看设置控件的属性,右键这个控件,然后点击属性即可:

image-20231208194127094

对于按钮来说,我们最常用的就是下面两个属性:

image-20231208194321352

描述文字是按钮上显示的文字,而ID则是资源ID,可自行设置。

还有最上方的动态布局:

image-20231208194347444

意思是当你控件放在窗口内时,如果这个窗口的大小变化了,这个控件应该怎么变,就有大小移动两种变化类型,可以自己试一下。

然后是外观:

image-20231208194418056

故名思意,就是用来调整这个控件的样子的,这些只有自己试过才能印象深刻,都可以自己调一下,然后运行一下就知道了。

然后是行为:

image-20231208194457844

这里注意一下,上面没讲,其实当你点击任意一个属性,最下面都有对应的描述,只是有点简短,可能依旧有点难懂,还是得自己试一试。

行为里面可能最常用的是接收文件默认按钮

所谓接收文件,就是用户可以直接拖文件到这个控件身上,但你得处理对应的消息。

比如一些软件有拖动文件进来就将其打开的功能,就是使用的这个。

然后默认按钮,就是当你窗口上有许多按钮时,这个按钮将作为你按Enter键时,默认响应的按钮。

其它的基本就用不到,默认即可,需要了可以再自己研究一下。

上面说完属性,就来到第二步,响应消息,方法依旧很简单,直接右键控件,添加事件处理程序即可:

image-20231208194559505

这里一定要注意选对类,因为它默认不是当前窗口的类,所以得自己选择这个控件所在的窗口类:

image-20231208194656203

作者:余识
全部文章:0
会员文章:0
总阅读量:0
c/c++pythonrustJavaScriptwindowslinux