2.3 MFC方式

MFC方式使用标准C++语言和微软基础类库(Microsoft Foudation Class Library, MFC)来开发Windows应用程序,这里所说的标准C++语言是指ANSI/ISO C++语言。MFC库是一个C++类库,它通过把Windows API进行C++封装,屏蔽了Windows编程的内部复杂性,并通过集成开发环境的帮助,使得Windows界面开发可以以可视化的方式进行。MFC方式比SDK方式的开发效率高(注意是开发者的开发效率,不是指程序运行速度),是Visual C++开发界面程序的主流选择。这种方式要求开发者熟悉标准C++和Windows程序基本运作过程。这种方式开发的Windows程序习惯称为MFC程序。

2.3.1 通过向导生成一个简单的MFC程序

Visual C++ 2013对MFC开发提供了强大的可视化操作,甚至不需要编写一行代码,只需通过Visual C++ 2013提供的向导就可以生成一个完整的程序。下面我们通过向导来生成一个简单的MFC程序。

【例2.2】 通过向导生成一个简单的MFC程序

(1)打开Visual C++ 2013,选择菜单“新建”|“项目”,或直接按快捷键Ctrl+Shift+N,弹出“新建项目”对话框,在该对话框上,在左边展开“模板”|“Visual C++”|“MFC”,然后在右边选择“MFC应用程序”,如图2-5所示。

图2-5

接着在下方“名称”文本框中输入项目名称,在“位置”文本框中输入项目存放路径,最后单击“确定”按钮。随后出现“MFC应用程序向导”对话框,在该对话框的左边选择“应用程序类型”,然后在右边选择应用程序类型为“多个文档”,项目类型为“MFC标准”,接着单击“完成”按钮。此时一个多文档类型的MFC程序的框架完成了,而我们无须写一行代码。

(2)开始运行工程。单击菜单“调试”|“开始执行(不调试)”,或直接按Ctrl+F5键来运行工程,运行结果如图2-6所示。

图2-6

2.3.2 手工写一个简单的MFC程序

上面我们没有写一行代码就生成了一个MFC应用程序,所有代码都是Visual C++自动生成的。现在我们自己动手来写一个MFC应用程序,不借助向导。

【例2.3】 手工写一个简单的MFC程序

(1)打开Visual C++ 2013,选择菜单“新建”|“项目”,或直接按快捷键Ctrl+Shift+N,弹出“新建项目”对话框,在该对话框上,在左边展开“模板”|Visua C++|Win32,在右边选中“Win32项目”,如图2-7所示。

图2-7

然后在下面“名称”文本框中输入项目名称,如“Test”,并输入一个项目位置。最后单击“确定”按钮。随后会出现“Win32应用程序向导”对话框,单击左边的“应用程序设置”,然后在右边的附加选项下选择“空项目”,如图2-8所示。

图2-8

最后单击“完成”按钮。某些同志可能会疑惑,我们要建立的是MFC程序,怎么会新建一个Win32项目,这是因为只有Win32项目才有空项目这个选项,而不需要向导生成任何代码,我们需要在空项目中添加C++源文件和头文件。如果选择MFC项目则肯定会有一大堆向导代码。

(2)在“解决方案资源管理器”上,展开Test,对“源文件”右击,在右击菜单上选择“添加”|“新建项”,出现“添加新项”对话框,在该对话框上,选择“C++文件(.cpp)”,并输入名称“Test.cpp”,如图2-9所示。

图2-9

最后单击“添加”按钮。再在“解决方案资源管理器”上,展开Test,对“头文件”右击,在右击菜单上选择“添加”|“新建项”,出现“添加新项”对话框,在该对话框上,选择“头文件(.h)”,并输入名称“Test.h”,如图2-10所示。

图2-10

最后单击“添加”按钮。

(3)返回到IDE上,打开解决方案管理器,对管理器内的“Test”右击,然后在右键菜单上选择“属性”来打开Test的工程属性页,我们的Test工程的属性都在这个对话框上设置,在左边选择“常规”,在右边找到“MFC的使用”,然后在旁边选择“在共享DLL中使用MFC”,如图2-11所示。

图2-11

最后单击“确定”按钮。

DLL是动态链接库,相当于一个函数库。这里我们选择使用MFC动态链接库后,就可以使用MFC类库里面的东西了。

(4)打开Test.h,在Test.h中输入代码:

        #pragma once //为了防止头文件被重复引用
        #include <sdkddkver.h> //定义开发包SDK的版本
        #include <afxwin.h> //MFC部分类库在这个文件里,所以必须包含
        #include <afxwinappex.h> //应用程序类CWinAppEx在这里面定义
        #include <afxframewndex.h> //窗口框架类CFrameWndEx在这里面定义
        class CTestApp : public CWinAppEx //定义应用类
        {
        public:
            virtual BOOL InitInstance(); //重载虚函数
        };

打开Test.cpp,在Test.cpp中输入代码:

        #include "Test.h"
        CTestApp theApp; //定义引用类全局对象
        BOOL CTestApp::InitInstance() //应用类的初始化函数
        {
            CWinAppEx::InitInstance();
            SetRegistryKey(TEXT("Hello MFC app.")); //声明本程序的设置存储在注册表中,而不是INI文件
         AfxMessageBox(_T("Hello World,来自MFC")); //显示消息框
         return TRUE;
        }

每个MFC程序都要有一个继承自CWinApp(或CWinAppEx)的应用类,比如CTestApp,应用类的生命期从程序开始执行到执行结束,应用类必须重载CWinApp的虚函数InitInstance,程序的初始化操作通常放在这个函数里,这里我们让程序刚运行时就跳出一个消息框,因此把AfxMessageBox放在InitInstance里。AfxMessageBox是MFC中显示消息框的函数,_T作用与TEXT相同,都是为了让字符串同时支持Unicode字符集和多字节字符集环境。AfxMessageBox是一个全局函数,MFC中好多全局函数都以Afx开头,该函数的用法和MessageBox差不多,它的函数声明有两种形式:

        int AfxMessageBox(
          LPCTSTR lpszText,
          UINT nType = MB_OK,
          UINT nIDHelp = 0
        );
        int AFXAPI AfxMessageBox(
          UINT nIDPrompt,
          UINT nType = MB_OK,
          UINT nIDHelp = (UINT)-1
        );

其中,lpszText是要显示字符串内容;nType是消息框上按钮的类型;nIDHelp表示帮助事件的ID,如果是0表示使用当前程序的默认帮助;nIDPrompt是当前程序字符串资源表中的字符串ID号;函数的返回值可以取下列值:

● IDABORT:用户选择了退出按钮

● IDCANCEL:用户选择了取消按钮

● IDIGNORE:用户选择了忽略按钮

● IDNO:用户选择了否按钮

● IDOK:用户选择了确定按钮

● IDRETRY:用户选择了重试按钮

● IDYES:用户选择了是按钮

(5)开始运行工程。单击菜单“调试”|“开始执行(不调试)”,或直接按Ctrl+F5键来运行工程,运行结果如图2-12所示。

图2-12

2.3.3 手工写一个稍复杂的MFC程序

上面的例子虽然是一个MFC程序,但它没有涉及框架类、消息响应等内容,这里我们手工写一个稍复杂的MFC程序,在该程序里我们会创建一个MFC主窗口,在主窗口里显示一个字符串,并且在主窗口上单击鼠标左键时,会跳出一个消息框。

【例2.4】 手工写一个稍复杂的MFC程序

(1)打开Visual C++ 2013,同上例一样新建一个空的Win32项目。

(2)打开解决方案管理器,为项目添加两个头文件Test.h和MainFrm.h,以及两个源文件Test.cpp和MainFrm.cpp,在Test.cpp中实现应用类,在MainFrm.cpp中实现主窗口的框架类。

(3)打开Test.h,在Test.h中输入代码:

        #pragma once //为了防止头文件被重复引用
        #include <sdkddkver.h> //定义开发包SDK的版本
        #include <afxwin.h> //MFC部分类库在这个文件里,所以必须包含
        #include <afxwinappex.h> //应用程序类CWinAppEx在这里面定义
        #include <afxframewndex.h> //窗口框架类CFrameWndEx在这里面定义
        class CTestApp : public CWinAppEx //定义应用类
        {
        public:
         virtual BOOL InitInstance(); //重载虚函数
        };

打开Test.cpp,在Test.cpp中输入代码:

        #include "Test.h"
        #include "MainFrm.h"
        CTestApp theApp; //定义引用类全局对象
        BOOL CTestApp::InitInstance() //应用类的初始化函数
        {
            CWinAppEx::InitInstance();
            SetRegistryKey(TEXT("Hello MFC app.")); //声明本程序的设置存储在注册表中,而不是INI文件
            m_pMainWnd = new CMainFrame(); // m_pMainWnd在afxwin.h中定义,这里分配窗口框架的内存空间
            m_pMainWnd->ShowWindow(SW_SHOW); //显示窗口
            m_pMainWnd->UpdateWindow(); //更新绘制窗口
            return TRUE;
        }

这两段代码基本和上例类似,只是在应用类的初始化函数InitInstance中,为主窗口框架类指针分配了存储空间,然后显示了主框架窗口,其中主窗口框架类指针m_pMainWnd在afxwin.h中定义。

打开MainFrm.h,在MainFrm.h中输入代码:

        #pragma once //防止头文件被重复引用
        class  CMainFrame  :  public  CFrameWndEx  // 定义继承于  CFrameWndEx  的框架类CMainFrame
        {
        public:
         CMainFrame(); //构造函数
         DECLARE_MESSAGE_MAP() //声明消息映射表
         afx_msg void OnPaint(); //声明绘制窗口消息的处理函数
         afx_msg void OnLButtonDown(UINT nFlags, CPoint point); //声明鼠标单击左键消息的处理函数
        };

打开MainFrm.cpp,在MainFrm.cpp中输入代码:

        #include "Test.h"
        #include "MainFrm.h"
        CMainFrame::CMainFrame(void)
        {
            Create(NULL, _T("主窗口")); //创建主窗口,窗口标题栏上显示字符串“主窗口”
        }
        BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx) //开始定义消息映射表
         ON_WM_PAINT() //定义绘制窗口消息映射项
         ON_WM_LBUTTONDOWN() //定义鼠标单击左键消息映射项
        END_MESSAGE_MAP() //消息映射结束
        void CMainFrame::OnPaint() //定义绘制窗口消息的处理函数
        {
         CPaintDC dc(this); // 定义窗口的绘图设备描述表
         // TODO: Add your message handler code here
         CRect rc; //定义矩形坐标
         GetClientRect(&rc); //得到窗口客户区的大小
        /*
        在窗口中间输出一行字符串,DT_VCENTER  表示输出在  rc  范围内的垂直方向的中间,DT_SINGLELINE表示单行,DT_RIGHT表示输出在rc范围内的水平方向的右边
        */
         dc.DrawText(TEXT("Hello World!这是一个MFC程序!"), &rc,
        DT_Visual C++ENTER | DT_SINGLELINE | DT_RIGHT);
        }
        void CMainFrame::OnLButtonDown(UINT nFlags, CPoint point) //定义鼠标单击左键消息的处理函数
        {
         // TODO: Add your message handler code here and/or call default
         AfxMessageBox(TEXT("你单击了鼠标左键!")); //跳出一个消息框
         CFrameWndEx::OnLButtonDown(nFlags, point);  //调用父类框架的鼠标单击左键消息的处理函数
        }

框架类用来实现程序主窗口。一般一个MFC程序只拥有一个主窗口,在主窗口里还可以显示一个或多个子窗口。我们要在窗口中间写入一行字符串,因此我们要对绘制窗口消息WM_PAINT进行处理,在该消息的处理函数中,定位好坐标,然后调用画字符的函数DrawText。如果我们不在窗口中间画文字,那OnPaint是不需要的,大家可以注释掉它看看效果,当然其消息映射ON_WM_PAINT()和OnPaint的声明也要同时注释。

我们需要在窗口内单击鼠标左键,然后程序做出响应,所以我们要对窗口的单击鼠标左键的消息进行处理,函数OnLButtonDown就是该消息的处理函数,当用户在窗口范围内单击鼠标左键的时候,系统会调用该函数,在这个函数中,我们调用AfxMessageBox来跳出一个消息框,因此用户单击左键的时候,会出现一个消息框。

(4)开始运行工程。单击菜单“调试”|“开始执行(不调试)”,或直接按Ctrl+F5键来运行工程,运行结果如图2-13所示。

图2-13