MFC(Microsoft Fundamental Class)全称叫做微软基础类库,是微软公司开发的一个C++类库,主要封装了大部分Windows API函数。此外,MFC不仅仅是一个类库,还是一个框架,每次新建一个MFC的工程,VS会自动生成一些基础文件,封装MFC的内核,这样在编程时就不需要考虑一些底层功能的实现,程序员可以专心实现自己想要的功能。
本博客主要采用的还是非常“古老”的VS2005,虽然软件很老,但对于初学者来说比较友好,不会有太多花里胡哨的东西。首先我们来学习如何新建工程。
如果使用的VS版本比较高,如VS 2019,那么使用MFC之前要在C++桌面程序中添加MFC支持,具体过程可以参考这篇博客。
首先打开VS,选择“文件”->“新建”->“项目”
新建项目结束之后,在Visual C++中选择MFC中的MFC应用程序,然后给项目工程取个名字并确定一个路径,然后点击确定。
补充一点:一般用VS2005学习C++时,新建的都是win32中的Windows控制台程序,所谓控制台,也就是运行程序之后弹出的小黑框,和Devc++差不多。
点击确定之后,可以看到项目设置的界面:
可以看到左侧列表中的各项设置,依次点击下一步即可对这些一一设置,但如果觉得它默认设置也可以接受,可以随时点击完成采用其默认设置。一般来说,简单的程序只需要设置应用程序类型即可。在这个界面,点击下一步。
在这个界面,只需要选择应用程序类型即可,其他设置保持默认,如果不需要其他的高级操作,直接点击完成即可。
那么问题来了,单文档、多文档、对话框它们有什么区别呢?
单文档和多文档的区别最典型的例子就是记事本和Word,至于对话框,可以理解为制作一个交互界面,但实际上单文档和多文档都可以添加对话框。因此我对这几个概念的理解就是,单文档和多文档一开始运行出来的是一个文档视图窗口,可以在上面进行绘制图案等操作,而对话框则只有对话框,并在对话框上面添加一些控件进行编程。个人认为初学者完全没有必要纠结这些内容,一般选择单文档或对话框即可,相对简单。
下面将以单文档为例介绍MFC,大都也适用于对话框和多文档。
新建项目之后,可以看到解决方案资源管理器窗口中看到包含了三个文件夹,分别是头文件,源文件,资源文件,其中包含的文件及其作用如下图所示。
打开类视图窗口,可以看到创建项目时已包含的类
明显可以发现,这个类和文件是一一对应的,比如Doc、View、App、MainFrame类,如果点开其左边的加号,可以看到其继承的是哪些类,这样或许能够对Windows程序框架有一个更好的了解,但如果觉得太复杂也不用纠结。
新建一个项目工程之后,可以直接点击运行,就可以看到一个主界面,包括了菜单栏,工具栏和主要的显示界面,这个是底层程序已经写好的,不需要程序员去考虑是怎么生成的。
对于新手来说,最简单的入门操作就是在主界面上绘制图形,比如画一个圆,然后给它填充一个颜色。要实现这个功能,首先是要清楚和界面绘制相关的类有哪些,其次就是要了解绘制图案的步骤。
对于第一个问题,最为推荐手边备用一个 MFC文档,主要记录的是MFC中类的名字和使用方法,遇到不熟悉的类,查这个文档肯定要比去网上搜来得快捷。
对于第二个问题,绘制一个图案,一般的步骤是什么?参考这个链接,里面提到,在MFC中绘制图像,一般要涉及两个对象,一是设备上下文对象,包括CDC及其派生类;二是GDI(Graphic Device Interface)对象,包括CFont、CBrush、CPen等。这两者的关系可以理解为前者是环境,后者是工具。常用的GDI类如下所列。
总结来说,使用GDI绘图,一般的操作是:
附:MFC中DrawText函数的使用
学完了文档视图下绘制界面等操作,接下来就是重头戏——对话框及其常用的操作。一般想到制作一个GUI界面,主要想实现的还是交互功能,而这就需要用到对话框。
在资源界面中(双击name.rc文件(name代表工程名)或在菜单栏视图中找)可以看到有一个Dialog文件夹,这是本项目中所有的对话框,双击一个打开。
然后选中对话框之后,在属性栏(如果没有去视图里面找),即可看到对话框对应的属性。
常用属性总结如下:
属性 | 取值及其含义 |
---|---|
Border | 如果选择默认选项,则窗口不能缩放,选择resizing则可以缩放 |
Caption | 窗口的名字 |
ID | 窗口ID,不建议修改 |
Maximum/Minimum Box | 窗口最大化和最小化按钮是否显示 |
MENU | 窗口是否添加菜单栏,需要先新建一个菜单栏,而且不能出现多个对话框使用一个菜单的情况 |
Title Bar | 上面的标题栏,最好要有,不然窗口无法拖动 |
确定对话框属性之后,首要的就是给对话框添加一个类,这样该对话框中的控件也就“有了家”,建立的控件对应的消息响应函数一般也都会作为这个对话框类的成员函数或变量。
在对话框任意位置右键,选择“添加类”:
从上面可以看出,这个类继承的是CDialog类,也就是它含有CDialog类中全部的成员函数。对于这些,初学者不需要全部了解,但一些常用的基本操作还是得要会——比如弹出模态对话框和非模态对话框。
按下一个按钮弹出一个对话框,这种操作可以说是交互程序中最常见的了,那么,弹出的对话框就存在两种形式——模态对话框和非模态对话框。模态是指弹出对话框之后就不能点击非对话框中的选项,必须要先处理弹出对话框的消息;而非模态,就是弹出对话框之后还可以点击其他内容(暂时先不管弹出的对话框)。
来看几个代码:
/*模态对话框*/
//CTest是对话框添加的一个类
CTest dlg1;
dlg1.DoModal(); //弹出模态对话框
/*非模态对话框*/
//CPop是需要弹出的对话框对应的类
//先在弹出该对话框的对话框(“父对话框”)对应的类中添加一个变量,注意引用子对话框的头文件
CPop *dlg_pop;
//在弹出子对话框的消息响应程序中
dlg_pop = new CPop(); //先创建一个全局对象(记得之后给它delete掉)
dlg_pop->Create(ID_pop); //ID_pop是子对话框的ID
dlg_pop->ShowWindow(SW_SHOWNORMAL);//这个函数照抄
如果新建的项目类型是单文档,那么在资源视图就会有一个MENU的文件夹,同时会有一个默认的Menu,也就是主文档视图对应的menu;如果新建的项目类型是对话框,那么资源视图默认是没有Menu文件夹的,需要自己创建,方法就是在资源视图右键,选择添加资源。
建立菜单之后,在资源视图双击菜单,即可对它进行编辑。
这里需要注意的是,如果想要实现下拉菜单,那么下拉菜单的上一级的属性必须设置为Popup:
同样,需要注意,如果需要给按钮添加事件响应程序,则需要将popup属性设置为false。
了解完了对话框的基本操作,接下来就是学习对话框中的控件了,而这我认为也是这个项目工程的核心灵魂。
静态文本可能是这些控件当中最简单的控件了。直接在工具栏中将静态文本拖到对话框中,然后编辑Caption,使其显示不同的文字,如果没有其他什么要求,那它的使用到此为止,但如果想要调整显示文字的字体呢?就需要为静态文本添加变量,在程序中进行编辑。
如果出现上面的报错,就改一下静态文本的ID即可,不要用默认ID。
之前编程的时候突然遇到静态文本添加变量的按钮是灰色的,但是可以添加类。经过新建一个工程然后仔细比对属性之后发现,如果对话框ID改变的话,就会导致这个问题,但是目前只遇到主对话框出现这个问题,其他对话框没有出现(对话框程序,不是单文档)。
附:如何设置静态文本显示字体?
对话框中的按钮使用非常简单,在对话框界面找到工具箱窗口,然后将其中的Button拖到对话框中,首先需要编辑Button的属性,一般来说就是按钮上显示的文字Caption和控件的ID。
设置好属性之后,就可以添加按钮的事件处理程序了,即按下这个按钮会执行哪些代码。方法就是右键按钮,选择“添加事件处理程序”:
在接下来的处理程序向导中,要选择消息类型(如单击,双击等)和所在的类(一般就选择按钮所在对话框对应的类,所以添加按钮之前要先给对话框添加一个类。)
然后就会自动跳转到对应的程序位置,编辑即可。
创建完按钮之后,如果要删除按钮,千万别直接删除按钮,还要考虑代码部分的情况,所以在删除按钮之前,首先要先删除与之关联的消息处理程序。
在按钮属性窗口中,点击事件处理程序(一个黄色的小闪电),找到添加的事件处理程序,然后删除,这样相关的代码就会被注释掉,编译也就不受影响了。
其他的控件也应该参考这种删除方式,不要贸然直接删掉,可能会导致编译不通过。
文本编辑控件,顾名思义,就是用来编辑输入文本的呗,但其实不够准确,因为它还可以显示文本,这一点需要注意。
CEdit val; //对话框类中声明一个变量
//消息响应函数中
CString str_in, str_out;
val.GetWindowText(str_in); //编辑框中输入的字符串被函数处理之后写入到变量str_in中。
val.SetWindowText(str_out); //将str_out设置文本编辑框显示的字符串
前面提到,文本编辑框获取文本和设置文本得到的数据类型都是CString(据说这个数据类型实际上是根据string类型重新封装过的),而比较常用的数据类型都是string或者char*,所以涉及到一个数据类型转换的问题。
string
和char*
相互转换/*char*到string*/
char str1[12];
....
string str2 = str1; //char*到string直接赋值即可
/*string到char**/
string str3 = "Hello";
str3.c_str(); //得到的就是char*类型的字符串
更加复杂的转换操作可以参考这篇教程。
CString
和char*
相互转换CString
和string
类型相互转换