MFC本质
MFC本质上就是一套开发框架,它可以帮助我们快速开发。MFC是以类的方式重新将Windows API进行了归类,MFC层次结构图链接。一提到类,相信对于了解C++或者其它比较高级一点的开发语言的同学肯定就不会感到那么恐惧MFC了。MFC既然是一套,所以你就不一定非得在VS环境下编译,它可以在任何支持C++的编译器下面进行编译,但是这些编译器必须是在Windows下的,因为它所使用的大多数API都是Windows下的,和Linux或者Unix是不兼容的。虽然说可以在任何编译器下进行编译MFC这套框架,但是我们还是最好在VS下面进行MFC的编译和开发,因为VS对MFC的支持是最好的。
下面是MFC层次结构图,看到这些图,我们对MFC是不是了解更加清晰了呢?
它就是一层一层的继承,和C++的继承是一模一样的。
上面所述的目的就是我们(主要是刚接触MFC的同学)不要觉得MFC有多难多复杂,它其实就是一个一个的类,一步一步的看这个类是从哪儿继承过来的,然后我们就知道这个类中都有什么函数(接口)可供我们使用了,用到什么函数,直接就去这个类里面查找就OK了!这么一解释,MFC是不是变得不是那么难了呢?
Dialog创建过程
XXX代表的数创建的对话框工程的名字。
MFC的对话框创建完成后,存放代码的有两个类,一个是XXXApp的类,另一个是XXXDlg的类。我们需要注意一下几个点:
第一个点,在XXXApp类中,有这样的一个虚函数:
virtual BOOL InitInstance();
这个函数在程序启动的时候被被动的调用。
第二个点,在XXXDlg类中,有这样的一个函数:
BOOL OnInitDialog() override;
注意:所有的OnXXX函数,代表的意思都是被被动的调用,意思就是当发生某些消息的时候就会调用OnXXX函数!
特别需要注意的是:
XXXDlg类是完成界面功能的,而XXXApp类是主要实现程序逻辑的功能的。
在MFC这套框架中,程序和窗口是分开的。窗口只是响应一些界面的消息,它一定不是来完成逻辑的,XXXApp才是来完成逻辑的。
详细的执行过程是这样的,XXXApp类会生成唯一的一个全局对象theApp,这个theApp会执行它的InitInstance()函数,而 InitInstance()函数里面会生成XXXDlg类的对象dlg,dlg调用它的函数DoModal(),DoModal()再调用BOOL OnInitDialog() override,此时,一个对话框界面就活生生的展现在我们的面前了,我们可以在对话框界面上做一些事情,当这个对话框关闭的时候,它也会返回一个值,我们可以根据返回值,也可以做一些相应的收尾工作。
这就是MFC的对话框创建和销毁的全部过程,是不是很简单呢?
对话框的使用
如果按照上面进行添加对话框标题,会弹出下面的错误
当我们单步调试,进入SetWindowText函数,如下图所示:
我们可以看到hWnd是一个空指针,所以就会出现崩溃。那么有的同学就会产生疑问,我们的对话框对象不是已经创建出来了吗?那么为什么不能调用SetWindowText函数呢?这是因为SetWindowText函数是针对于窗口来进行操作的,而窗口是属于内核对象,也就是意味着,有没有类,跟我内核对象是一点关系都没有的,也就是说,只要你有窗口,我就能做窗口相关的事情。
总的来说就是对话框对象是我们自己程序的对象,我们的程序对象维护了一个窗口内核对象,注意:我们只是维护一个窗口内核对象,它是不属于我们自己程序的,应该说,所有的内核对象都是不属于程序的,它们是属于操作系统的,我们能做的是,我们需要什么,操作系统帮我们拿出来我们需要的东西,然后在确保拿过来这个东西的时候,我们再对它们进行相关操作。,所以我们当前的对话框对象并没有拿到内核对象,因为必须在DoModal之后,才能拿到窗口内核对象,也就是说,DoModal函数里面才能拿到窗口内核对象。
我们的对话框类严格来说是一个窗口管理类,我们的窗口是一个内核对象,而窗口内核对象是通过操作系统来分配的,所以我们的窗口管理类唯一能做的是向操作系统要一个窗口内核对象(起到一个通知的作用),然后再让这些窗口对象做一些相应的改变,比如通过SetWindowText函数来修改对话框标题等。
那么问题又来了,SetWindowText函数只能在DoModal函数之后才能起作用,但是DoModal函数是一个阻塞函数,SetWindowText函数是不能被执行到的呀!!!这该怎么办呢?如果我们使用的是非模态对话框,那么在Create函数之后,是可以进行SetWindowText函数来修改标题的。
那么对于模态对话框,我们就没有办法解决这个问题了吗?办法是有的,比如我们可以对DoMoDal函数进行改造。我们可以想象一下DoModal函数里面做的事情:
可定CreateWindow -> 发送WM_CREATE消息 -> OnInitialDialog函数里面可以做初始化操作
所以我们可以重写OnInitialDialog函数,在OnInitialDialog函数中,我们首先可以调用父类里面的CDialogEx::OnInitialDialog函数,然后再做窗口设置操作,调用SetWindowText函数,来更改对话框标题,重写的OnInitialDialog函数如下所示:
此时,我们再看一下执行效果:
我们只重写OnInitialDialog函数,就解决了程序崩溃的问题,原来的调用没有任何一点儿改变:
从上面的例子可以看出,MFC实际上是以C++的方式对Windows里面的很多特性进行了一层一层的封装,然后对Windows中的每一个消息都相应的产生一个On…的函数,所以我们可以利用C++里面的特性,来对MFC进行各种各样的填充、扩充以及修改。
我们如果想正儿八经使用MFC这套框架的话,把MFC写的与众不同一些的话,我们更多的是对MFC的类进行继承,然后对特定的消息进行特定的操作。
下面介绍三种方式来定制MFC
1. 构造函数接收参数修改对话框风格的例子
2. 重载一个接收参数的DoModal函数
3. 设置一个方法
1. 构造函数接收参数例子
增加一个带参数的构造函数
上面的方式虽然可以,但是并不推荐。我们应该使用一个enum来枚举一些对话框风格
2. 重载接收参数的DoModal函数修改对话框风格的例子
3.设置一个方法改对话框风格的例子就不再举例子了
DoModal函数的返回值
当我们点击对话框上面的×号关闭对话框时,DoModal函数会返回2,它代表是IDCANCEL消息,然后就将对话框关闭掉了,如果我们想使用自己的按钮来关闭对话框,该如何实现呢?
那么这样实现我们的关闭按钮函数,可以将对话框关闭掉吗?
我们也不知道OnClose函数里面到底做了什么事情,调试一下!!!
从上面可以看出OnClose函数什么事情都没有做,所以这个按钮是不能将对话框关闭掉的。
如果我们调用父类的OnClose函数,效果会怎样呢?
通过调试,可以看出,跟调用本类中的OnClose函数是一模一样的。
那么我们该如何才能自己将对话框关闭呢?很简单,自己重载一个OnClose函数,让OnClose函数调用CloseWindow函数,就可以将对话框关闭。
MFC的按钮约定俗成的一些按钮。
我们还可以使用EndDialog函数,根据传入的不同的参数,来决定DoModal函数的返回值。
从上面可以看出,如果我们传入的是IDCANCEL,那么DoModal函数的返回值就是2了。