- MFC 教程
- MFC - 首页
- MFC - 概述
- MFC - 环境设置
- MFC - VC++ 项目
- MFC - 入门
- MFC - Windows 基础
- MFC - 对话框
- MFC - Windows 资源
- MFC - 属性表
- MFC - Windows 布局
- MFC - 控件管理
- MFC - Windows 控件
- MFC - 消息和事件
- MFC - ActiveX 控件
- MFC - 文件系统
- MFC - 标准 I/O
- MFC - 文档/视图
- MFC - 字符串
- MFC - CArray
- MFC - 链接列表
- MFC - 数据库类
- MFC - 序列化
- MFC - 多线程
- MFC - 互联网编程
- MFC - GDI
- MFC - 库
- MFC 有用资源
- MFC - 快速指南
- MFC - 有用资源
- MFC - 讨论
MFC - 快速指南
MFC - 概述
Microsoft Foundation Class (MFC) 库提供了一组函数、常量、数据类型和类,用于简化创建 Microsoft Windows 操作系统应用程序。在本教程中,您将学习有关如何使用 MFC 启动和创建基于 Windows 的应用程序的所有知识。
先决条件
我们假设您了解以下内容:
- 关于 Windows 编程的一些知识。
- C++ 编程基础。
- 了解面向对象编程的基础知识。
什么是 MFC?
Microsoft Foundation Class 库 (MFC) 是用于 Microsoft Windows 编程的“应用程序框架”。MFC 提供了许多代码,这些代码是以下内容所必需的:
- 管理 Windows。
- 菜单和对话框。
- 执行基本的输入/输出。
- 存储数据对象集合等。
通过在 MFC 框架中添加特定于应用程序的代码,您可以轻松扩展或覆盖您 C++ 应用程序中的 MFC 框架的基本功能。
MFC 框架
MFC 框架提供了一组旨在简化 Windows 编程的可重用类。
MFC 为许多基本对象提供了类,例如在日常编程中使用的字符串、文件和集合。
它还为常见的 Windows API 和数据结构提供了类,例如窗口、控件和设备上下文。
该框架还为更高级的功能(例如 ActiveX 和文档视图处理)提供了坚实的基础。
此外,MFC 还提供了一个应用程序框架,包括构成应用程序体系结构层次结构的类。
为什么选择 MFC?
MFC 框架是一种强大的方法,可以让您建立在 Windows 专家程序员的工作基础上。MFC 框架具有以下优点。
它缩短了开发时间。
它使代码更具可移植性。
它还提供了巨大的支持,而不会降低编程自由度和灵活性。
它可以轻松访问“难以编程”的用户界面元素和技术。
MFC 通过数据访问对象 (DAO) 和开放数据库连接 (ODBC) 简化了数据库编程,并通过 Windows 套接字简化了网络编程。
MFC - 环境设置
Microsoft Visual C++ 是一种用于创建 Microsoft Windows 操作系统应用程序的编程环境。要在您的 C++ 应用程序中使用 MFC 框架,您必须已安装 Microsoft Visual C++ 或 Microsoft Visual Studio。Microsoft Visual Studio 还包含 Microsoft Visual C++ 环境。
Microsoft 提供了 Visual Studio 的免费版本,其中也包含 SQL Server,您可以从 https://www.visualstudio.com/en-us/downloads/downloadvisual- studio-vs.aspx 下载。
以下是安装步骤。
步骤 1 - 下载 Visual Studio 后,运行安装程序。将显示以下对话框。
步骤 2 - 单击“安装”以启动安装过程。
步骤 3 - Visual Studio 成功安装后,您将看到以下对话框。
步骤 4 - 关闭此对话框,并在需要时重新启动计算机。
步骤 5 - 从“开始”菜单打开 Visual Studio,这将打开以下对话框。首次启动时,准备工作需要一些时间。
步骤 6 - 接下来,您将看到 Visual Studio 的主窗口。
步骤 7 - 您现在可以开始您的应用程序了。
MFC - VC++ 项目
在本章中,我们将介绍不同类型的 VC++ 项目。Visual Studio 包括几种 Visual C++ 项目模板。这些模板有助于创建基本程序结构、菜单、工具栏、图标、引用和包含语句,这些语句适合您要创建的项目类型。以下是模板的一些主要功能。
它为许多这些项目模板提供了向导,并帮助您在创建项目时自定义项目。
创建项目后,您可以构建和运行应用程序。
您不必使用模板来创建项目,但在大多数情况下,使用项目模板效率更高。
修改提供的项目文件和结构比从头创建它们更容易。
在 MFC 中,您可以使用以下项目模板。
序号 | 项目模板和描述 |
---|---|
1 | MFC 应用程序 MFC 应用程序是基于 Microsoft Foundation Class (MFC) 库的 Windows 可执行应用程序。创建 MFC 应用程序最简单的方法是使用 MFC 应用程序向导。 |
2 | MFC ActiveX 控件 ActiveX 控件程序是模块化程序,旨在为父应用程序提供特定类型的功能。例如,您可以创建用于对话框、工具栏或网页上的按钮等控件。 |
3 | MFC DLL MFC DLL 是一个二进制文件,充当共享函数库,多个应用程序可以同时使用。创建 MFC DLL 项目最简单的方法是使用 MFC DLL 向导。 |
以下是一些也可以用于创建 MFC 应用程序的通用模板:
序号 | 项目模板和描述 |
---|---|
1 | 空项目 项目是构建应用程序所需的所有内容的逻辑容器。然后,您可以根据需要向解决方案中添加更多新的或现有的项目。 |
2 | 自定义向导 当您需要创建新的自定义向导时,Visual C++ 自定义向导是您要使用的工具。创建自定义向导最简单的方法是使用自定义向导。 |
MFC - 入门
在本章中,我们将查看一个正在工作的 MFC 示例。要创建 MFC 应用程序,您可以使用向导自定义您的项目。您也可以从头开始创建应用程序。
使用项目模板创建项目
以下是使用 Visual Studio 中提供的项目模板创建项目的步骤。
步骤 1 - 打开 Visual Studio 并单击“文件”→“新建”→“项目”菜单选项。
步骤 2 - 您现在可以看到“新建项目”对话框已打开。
步骤 3 - 从左侧窗格中选择“模板”→“Visual C++”→“MFC”。
步骤 4 - 在中间窗格中,选择“MFC 应用程序”。
步骤 5 - 在“名称”字段中输入项目名称“MFCDemo”,然后单击“确定”继续。您将看到以下对话框。
步骤 6 - 单击“下一步”。
步骤 7 - 选择上面对话框中显示的选项,然后单击“下一步”。
步骤 8 - 取消选中所有选项,然后单击“完成”按钮。
您现在可以看到 MFC 向导默认情况下创建了此对话框框和项目文件。
步骤 9 - 运行此应用程序,您将看到以下输出。
从头开始创建项目
您也可以从头开始创建 MFC 应用程序。要创建 MFC 应用程序,您需要执行以下步骤。
步骤 1 - 打开 Visual Studio 并单击“文件”→“新建”→“项目”菜单选项。
步骤 2 - 您现在可以看到“新建项目”对话框。
步骤 3 - 从左侧窗格中选择“模板”→“Visual C++”→“常规”。
步骤 4 - 在中间窗格中,选择“空项目”。
步骤 5 - 在“名称”字段中输入项目名称“MFCDemoFromScratch”,然后单击“确定”继续。您将看到已创建了一个空项目。
步骤 6 - 要将其设为 MFC 项目,请右键单击该项目并选择“属性”。
步骤 7 - 在左侧部分,单击“配置属性”→“常规”。
步骤 8 - 在“项目默认值”部分中选择“在共享 DLL 中使用 MFC”选项,然后单击“确定”。
步骤 9 - 因为它现在是一个空项目;我们需要添加一个 C++ 文件。因此,右键单击该项目并选择“添加”→“新建项...”
步骤 10 - 在中间窗格中选择“C++ 文件 (.cpp)”,在“名称”字段中输入文件名,然后单击“添加”按钮。
步骤 11 - 您现在可以看到“main.cpp”文件已添加到“源文件”文件夹下。
步骤 12 - 让我们在此文件中添加以下代码。
#include <iostream> using namespace std; void main() { cout << "***************************************\n"; cout << "MFC Application Tutorial"; cout << "\n***************************************"; getchar(); }
步骤 13 - 运行此应用程序时,您将在控制台上看到以下输出。
*************************************** MFC Application Tutorial ***************************************
MFC - Windows 基础
在本章中,我们将介绍 Windows 的基础知识。要创建程序(也称为应用程序),您需要从 MFC 的 CWinApp 派生一个类。CWinApp 代表Windows 应用程序类。
让我们通过创建一个新的 Win32 项目来了解一个简单的示例。
步骤 1 - 打开 Visual Studio 并单击“文件”→“新建”→“项目”菜单选项。
步骤 2 - 您现在可以看到“新建项目”对话框。
步骤 3 - 从左侧窗格中选择“模板”→“Visual C++”→“Win32”。
步骤 4 - 在中间窗格中,选择“Win32 项目”。
步骤 5 - 在“名称”字段中输入项目名称“MFCWindowDemo”,然后单击“确定”继续。您将看到以下对话框。
步骤 6 - 单击“下一步”。
步骤 7 - 选择上面对话框中显示的选项,然后单击“完成”。
步骤 8 - 创建了一个空项目。
步骤 9 - 要将其设为 MFC 项目,请右键单击该项目并选择“属性”。
步骤 10 - 在左侧部分,单击“配置属性”→“常规”。
步骤 11 - 在“项目默认值”部分中选择“在共享 DLL 中使用 MFC”选项,然后单击“确定”。
步骤 12 - 添加一个新的源文件。
步骤 13 - 右键单击您的项目并选择“添加”→“新建项...”
步骤 14 - 在“模板”部分中,单击“C++ 文件 (.cpp)”。
步骤 15 − 将名称设置为 Example 并单击添加。
窗口创建
任何应用程序都有两个主要部分:
- 类
- 框架或窗口
让我们按照以下步骤创建一个窗口:
步骤 1 − 要创建应用程序,我们需要从 MFC 的 CWinApp 派生一个类。
#include class CExample : public CWinApp { BOOL InitInstance() { return TRUE; } };
步骤 2 − 我们还需要一个框架/窗口来显示应用程序的内容。
步骤 3 − 为此,我们需要添加另一个类并从 MFC 的CFrameWnd类派生,并实现其构造函数并调用 Create() 方法,该方法将创建一个框架/窗口,如下面的代码所示。
class CMyFrame : public CFrameWnd { public: CMyFrame() { Create(NULL, _T("MFC Application Tutorial")); } };
步骤 4 − 如您所见,Create() 方法需要两个参数,类的名称,应将其作为 NULL 传递,以及窗口的名称,该名称是将在标题栏中显示的字符串。
主窗口
创建窗口后,要让应用程序使用它,可以使用指针来显示用于创建窗口的类。在这种情况下,指针将是 CFrameWnd。要使用框架窗口,请将其指针分配给 CWinThread::m_pMainWnd 成员变量。这在应用程序的 InitInstance() 实现中完成。
步骤 1 − 以下是 CExample 类中 InitInstance() 的实现。
class CExample : public CWinApp { BOOL InitInstance() { CMyFrame *Frame = new CMyFrame(); m_pMainWnd = Frame; Frame->ShowWindow(SW_NORMAL); Frame->UpdateWindow(); return TRUE; } };
步骤 2 − 以下是 Example.cpp 文件的完整实现。
#include <afxwin.h> class CMyFrame : public CFrameWnd { public: CMyFrame() { Create(NULL, _T("MFC Application Tutorial")); } }; class CExample : public CWinApp { BOOL InitInstance() { CMyFrame *Frame = new CMyFrame(); m_pMainWnd = Frame; Frame->ShowWindow(SW_NORMAL); Frame->UpdateWindow(); return TRUE; } }; CExample theApp;
步骤 3 − 当我们运行上述应用程序时,将创建以下窗口。
窗口样式
窗口样式是控制窗口外观、边框、最小化或最大化状态或其他调整大小状态等功能的特性。
序号 | 样式和说明 |
---|---|
1 | WS_BORDER 创建具有边框的窗口。 |
2 | WS_CAPTION 创建一个具有标题栏的窗口(暗示 WS_BORDER 样式)。不能与 WS_DLGFRAME 样式一起使用。 |
3 | WS_CHILD 创建一个子窗口。不能与 WS_POPUP 样式一起使用。 |
4 | WS_CHILDWINDOW 与 WS_CHILD 样式相同。 |
5 | WS_CLIPCHILDREN 在父窗口内绘制时,排除子窗口占据的区域。在创建父窗口时使用。 |
6 | WS_CLIPSIBLINGS 剪辑彼此相关的子窗口;也就是说,当特定子窗口收到绘制消息时,WS_CLIPSIBLINGS 样式会将所有其他重叠的子窗口从要更新的子窗口区域剪裁掉。(如果没有给出 WS_CLIPSIBLINGS 并且子窗口重叠,当您在子窗口的客户区内绘制时,可能会在相邻子窗口的客户区内绘制。)仅与 WS_CHILD 样式一起使用。 |
7 | WS_DISABLED 创建一个最初禁用的窗口。 |
8 | WS_DLGFRAME 创建一个具有双边框但没有标题的窗口。 |
9 | WS_GROUP 指定一组控件中的第一个控件,用户可以使用箭头键在这些控件之间移动。在第一个控件之后,使用 WS_GROUP 样式为 FALSE 定义的所有控件都属于同一组。下一个具有 WS_GROUP 样式的控件将开始下一组(即,一组在下一组开始的地方结束)。 |
10 | WS_HSCROLL 创建一个具有水平滚动条的窗口。 |
11 | WS_ICONIC 创建一个最初最小化的窗口。与 WS_MINIMIZE 样式相同。 |
12 | WS_MAXIMIZE 创建一个最大尺寸的窗口。 |
13 | WS_MAXIMIZEBOX 创建一个具有最大化按钮的窗口。 |
14 | WS_MINIMIZE 创建一个最初最小化的窗口。仅与 WS_OVERLAPPED 样式一起使用。 |
15 | WS_MINIMIZEBOX 创建一个具有最小化按钮的窗口。 |
16 | WS_OVERLAPPED 创建一个重叠窗口。重叠窗口通常具有标题栏和边框。 |
17 | WS_OVERLAPPED WINDOW 使用 WS_OVERLAPPED、WS_CAPTION、WS_SYSMENU、WS_THICKFRAME、WS_MINIMIZEBOX 和 WS_MAXIMIZEBOX 样式创建一个重叠窗口。 |
18 | WS_POPUP 创建一个弹出窗口。不能与 WS_CHILD 样式一起使用。 |
19 | WS_POPUPWINDOW 使用 WS_BORDER、WS_POPUP 和 WS_SYSMENU 样式创建一个弹出窗口。必须将 WS_CAPTION 样式与 WS_POPUPWINDOW 样式组合才能使控制菜单可见。 |
20 | WS_SIZEBOX 创建一个具有调整大小边框的窗口。与 WS_THICKFRAME 样式相同。 |
21 | WS_SYSMENU 创建一个在标题栏中具有控制菜单框的窗口。仅用于具有标题栏的窗口。 |
22 | WS_TABSTOP 指定任何数量的控件之一,用户可以使用 TAB 键在这些控件之间移动。TAB 键将用户移动到由 WS_TABSTOP 样式指定的下一个控件。 |
23 | WS_THICKFRAME 创建一个具有粗边框的窗口,可用于调整窗口大小。 |
24 | WS_TILED 创建一个重叠窗口。重叠窗口具有标题栏和边框。与 WS_OVERLAPPED 样式相同。 |
25 | WS_TILEDWINDOW 使用 WS_OVERLAPPED、WS_CAPTION、WS_SYSMENU、WS_THICKFRAME、WS_MINIMIZEBOX 和 WS_MAXIMIZEBOX 样式创建一个重叠窗口。与 WS_OVERLAPPEDWINDOW 样式相同。 |
26 | WS_VISIBLE 创建一个最初可见的窗口。 |
27 | WS_VSCROLL 创建一个具有垂直滚动条的窗口。 |
步骤 1 − 让我们看看一个简单的示例,我们将在其中添加一些样式。创建窗口后,要将其显示给用户,我们可以对其应用 WS_VISIBLE 样式,此外,我们还将添加 WS_OVERLAPPED 样式。以下是实现:
class CMyFrame : public CFrameWnd { public: CMyFrame() { Create(NULL, _T("MFC Application Tutorial"), WS_VISIBLE | WS_OVERLAPPED); } };
步骤 2 − 运行此应用程序后,将创建以下窗口。
您现在可以看到最小化、最大化和关闭选项不再出现。
窗口位置
为了定位显示在显示器上的内容,计算机使用类似于笛卡尔坐标系的坐标系,但原点位于屏幕的左上角。使用此坐标系,任何点都可以通过其到屏幕左上角的水平和垂直轴的距离来定位。
Win32 库提供了一个名为 POINT 的结构,定义如下:
typedef struct tagPOINT { LONG x; LONG y; } POINT;
“x”成员变量是从屏幕的左边界到该点的距离。
“y”变量表示从屏幕的上边界到该点的距离。
除了 Win32 的 POINT 结构外,Microsoft Foundation Class (MFC) 库还提供了 CPoint 类。
这提供了与 POINT 结构相同的功能。作为 C++ 类,它添加了定位点所需的更多功能。它提供了两个构造函数。
CPoint(); CPoint(int X, int Y);
窗口大小
虽然点用于在屏幕上定位对象,但每个窗口都有一个大小。大小提供了与对象相关的两个度量。
- 对象的宽度。
- 对象的高度。
Win32 库使用如下定义的 SIZE 结构:
typedef struct tagSIZE { int cx; int cy; } SIZE;
除了 Win32 的 SIZE 结构外,MFC 还提供了 CSize 类。此类具有与 SIZE 相同的功能,但添加了 C++ 类的功能。它提供了五个构造函数,允许您以任何您选择的方式创建大小变量。
CSize(); CSize(int initCX, int initCY); CSize(SIZE initSize); CSize(POINT initPt); CSize(DWORD dwSize);
窗口尺寸
当窗口显示时,可以通过其相对于显示器边界的位置在屏幕上识别它。还可以通过其宽度和高度来识别窗口。这些特性由Create()方法的rect参数指定或控制。此参数是一个可以通过 Win32 RECT 结构创建的矩形。
typedef struct _RECT { LONG left; LONG top; LONG right; LONG bottom; } RECT, *PRECT;
除了 Win32 的RECT结构外,MFC 还提供了 CRect 类,它具有以下构造函数:
CRect(); CRect(int l, int t, int r, int b); CRect(const RECT& srcRect); CRect(LPCRECT lpSrcRect); CRect(POINT point, SIZE size); CRect(POINT topLeft, POINT bottomRight);
让我们看看一个简单的示例,我们将在其中指定窗口的位置和大小
class CMyFrame : public CFrameWnd { public: CMyFrame() { Create(NULL, _T("MFC Application Tutorial"), WS_SYSMENU, CRect(90, 120, 550, 480)); } };
运行此应用程序后,将在屏幕的左上角创建以下窗口,如 CRect 构造函数的前两个参数中指定的那样。最后两个参数是窗口的大小。
窗口父级
在现实世界中,许多应用程序由不同的窗口组成。当应用程序使用各种窗口时,大多数对象都依赖于特定窗口。它可能是创建的第一个窗口,也可能是您指定的另一个窗口。这样的窗口称为父窗口。所有其他窗口都直接或间接地依赖于它。
如果您要创建的窗口依赖于另一个窗口,则可以指定它具有父级。
这是通过 CFrameWnd::Create() 方法的 pParentWnd 参数完成的。
如果窗口没有父级,则使用 NULL 值传递参数。
让我们看看一个只有一个窗口的示例,并且没有可用的父窗口,因此我们将使用 NULL 值传递参数,如下面的代码所示:
class CMyFrame : public CFrameWnd { public: CMyFrame() { Create(NULL, _T("MFC Application Tutorial"), WS_SYSMENU, CRect(90, 120, 550, 480), NULL); } };
运行上述应用程序后,您会看到相同的输出。
MFC - 对话框
在本章中,我们将介绍对话框。Windows 应用程序经常通过对话框与用户通信。CDialog 类提供了管理对话框的接口。Visual C++ 对话框编辑器使设计对话框和创建其对话框模板资源变得容易。
创建对话框对象是一个两阶段操作:
构造对话框对象。
创建对话框窗口。
让我们通过创建一个新的 Win32 项目来了解一个简单的示例。
步骤 1 - 打开 Visual Studio 并单击“文件”→“新建”→“项目”菜单选项。
步骤 2 - 您现在可以看到“新建项目”对话框。
步骤 3 - 从左侧窗格中选择“模板”→“Visual C++”→“Win32”。
步骤 4 - 在中间窗格中,选择“Win32 项目”。
步骤 5 − 在“名称”字段中输入项目名称“MFCDialogDemo”,然后单击“确定”继续。您将看到以下对话框。
步骤 6 - 单击“下一步”。
步骤 7 − 选择上面给出的对话框中显示的选项,然后单击“完成”。
步骤 8 - 创建了一个空项目。
步骤 9 − 要将其设为 MFC 项目,请右键单击该项目并选择“属性”。
步骤 10 - 在左侧部分,单击“配置属性”→“常规”。
步骤 11 - 在“项目默认值”部分中选择“在共享 DLL 中使用 MFC”选项,然后单击“确定”。
步骤 12 - 添加一个新的源文件。
步骤 13 − 右键单击您的项目,然后选择“添加”→“新建项”。
步骤 14 − 在“模板”部分,单击“C++ 文件 (.cpp)”。
步骤 15 − 将名称设置为 Example 并单击添加。
步骤 16 − 要创建应用程序,我们需要添加一个类并从 MFC 的 CWinApp 派生。
#include <afxwin.h> class CExample : public CWinApp { public: BOOL InitInstance(); };
对话框创建
步骤 1 − 要创建对话框,请右键单击解决方案资源管理器中的“资源文件”文件夹,然后选择“添加”→“资源”。
步骤 2 − 在“添加资源”对话框中,选择“对话框”,然后单击“新建”。
步骤 3 − 在实际以编程方式创建对话框之前,需要进行一些准备工作。
步骤 4 − 对话框可以首先手动创建为文本文件(在资源文件中)。
步骤 5 − 您现在可以看到在“资源文件”下创建的 MFCDialogDemo.rc 文件。
步骤 6 − 资源文件在设计器中打开。同一个文件可以作为文本文件打开。右键单击资源文件并选择“打开方式”。
步骤 7 − 选择“源代码(文本)”编辑器,然后单击“添加”按钮。
步骤 8 − 返回设计器,右键单击对话框并选择“属性”。
步骤 9 − 您需要从众多选项中进行选择。
步骤 10 − 与大多数其他控件一样,必须识别对话框。对话框的标识符 (ID) 通常以 IDD_ 开头,让我们将 ID 更改为 IDD_EXAMPLE_DLG。
对话框位置
对话框必须“物理”地位于应用程序上。因为对话框通常被创建为其他控件的父级,所以其位置取决于它与其父窗口或桌面的关系。
如果查看“属性”窗口,您会看到两个字段,“X 位置”和“Y 位置”。
X 是从显示器左边界到对话框左边界的距离。
Y 是从显示器上边界到对话框上边界的距离。
默认情况下,这些字段设置为零。您也可以按照上图所示进行更改。
如果您将这两个维度指定为 0,则对话框的左边界和上边界将被设置为使对象显示在屏幕的中央。
对话框尺寸
对话框的尺寸指的是它的宽度和高度。您可以在设计器窗口中使用鼠标调整宽度和高度。
您可以在状态栏上看到宽度和高度的变化。
对话框方法
用于在屏幕上显示对话框的基本类是 CDialog 类。要创建对话框,我们需要从 CDialog 派生一个类。CDialog 类本身提供了三个构造函数,如下所示:
CDialog(); CDialog(UINT nIDTemplate, CWnd* pParentWnd = NULL); CDialog(LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL);
让我们创建另一个类 CExampleDlg 并从 CDialog 派生它。我们将实现其默认构造函数和析构函数,如下面的代码所示。
class CExampleDlg : public CDialog { public: enum { IDD = IDD_EXAMPLE_DLG }; CExampleDlg(); ~CExampleDlg(); }; CExampleDlg::CExampleDlg():CDialog(CExampleDlg::IDD) { } CExampleDlg::~CExampleDlg() { }
我们需要在 CExample::InitInstance() 方法中实例化此对话框,如下面的代码所示。
BOOL CExample::InitInstance() { CExampleDlg myDlg; m_pMainWnd = &myDlg; return TRUE; }
模态对话框
对话框有两种类型:**无模式**和**模态**。模态和无模式对话框的区别在于创建和显示它们的过程。
无模式对话框
对于无模式对话框,您必须在对话框类中提供自己的公共构造函数。
要创建无模式对话框,请调用您的公共构造函数,然后调用对话框对象的 Create 成员函数来加载对话框资源。
您可以在构造函数调用期间或之后调用 Create。如果对话框资源具有 WS_VISIBLE 属性,则对话框会立即出现。
如果不是,则必须调用其 ShowWindow 成员函数。
模态对话框
要创建模态对话框,请调用在 CDialog 中声明的两个公共构造函数之一。
接下来,调用对话框对象的**DoModal**成员函数来显示对话框并管理与它的交互,直到用户选择“确定”或“取消”。
DoModal 进行的这种管理使得对话框成为模态对话框。对于模态对话框,DoModal 会加载对话框资源。
**步骤 1** - 要以模态方式显示对话框,请在 CExample::InitInstance() 事件中使用您的对话框变量调用 DoModal() 方法:
BOOL CExample::InitInstance() { CExampleDlg myDlg; m_pMainWnd = &myDlg; myDlg.DoModal(); return TRUE; }
**步骤 2** - 以下是 Example.cpp 文件的完整实现。
#include <afxwin.h> #include "resource.h" class CExample : public CWinApp { public: BOOL InitInstance(); }; class CExampleDlg : public CDialog { public: enum { IDD = IDD_EXAMPLE_DLG }; CExampleDlg(); ~CExampleDlg(); }; CExampleDlg::CExampleDlg():CDialog(CExampleDlg::IDD) { } CExampleDlg::~CExampleDlg() { } BOOL CExample::InitInstance() { CExampleDlg myDlg; m_pMainWnd = &myDlg; myDlg.DoModal(); return TRUE; } CExample MyApp;
**步骤 3** - 当以上代码编译并执行时,您将看到以下对话框。
基于对话框的应用程序
Microsoft Visual Studio 提供了一种更简单的方法来创建主要基于对话框的应用程序。以下是在 Visual Studio 中使用项目模板创建对话框基本项目的步骤:
**步骤 1** - 打开 Visual Studio 并单击“文件”→“新建”→“项目”菜单选项。您将看到“新建项目”对话框。
**步骤 2** - 从左侧窗格中,选择“模板”→“Visual C++”→“MFC”。
**步骤 3** - 在中间窗格中,选择“MFC 应用程序”。
**步骤 4** - 在“名称”字段中输入项目名称“MFCModalDemo”,然后单击“确定”继续。您将看到以下对话框。
**步骤 5** - 单击“下一步”。
**步骤 6** - 选择上述对话框中显示的选项,然后单击“下一步”。
**步骤 7** - 检查您希望在对话框上具有的所有选项,例如“最大化”和“最小化”框,然后单击“下一步”。
**步骤 8** - 单击“下一步”。
**步骤 9** - 它将生成这两个类。您可以更改类的名称,然后单击“完成”。
**步骤 10** - 现在您可以看到 MFC 向导默认创建此对话框和项目文件。
**步骤 11** - 运行此应用程序时,您将看到以下输出。
MFC - Windows 资源
**资源**是一个文本文件,允许编译器管理对象,例如图片、声音、鼠标光标、对话框等。Microsoft Visual Studio 通过在用于编程的同一环境中提供必要的工具,使得创建资源文件变得特别容易。这意味着,您通常不必使用外部应用程序来创建或配置资源文件。以下是一些与资源相关的重要的功能。
资源是向用户提供信息的界面元素。
位图、图标、工具栏和光标都是资源。
某些资源可以被操作以执行操作,例如从菜单中选择或在对话框中输入数据。
应用程序可以使用各种资源,这些资源彼此独立地运行,这些资源被分组到扩展名为 *.rc 的文本文件中。
大多数资源都是通过从“添加资源”对话框中选择所需的资源来创建的。
“添加资源”对话框提供了广泛的资源列表,可以根据需要使用,但如果您需要某些不可用的资源,则可以在执行程序之前手动将其添加到 *.rc 文件中。
标识符
**标识符**是一个符号,它是一个常量整数,其名称通常以 ID 开头。它由两部分组成:一个文本字符串(符号名称)映射到一个整数值(符号值)。
符号提供了一种描述性方式来引用资源和用户界面对象,无论是在您的源代码中还是在资源编辑器中使用它们时。
当您创建新的资源或资源对象时,**资源编辑器**将为资源提供默认名称,例如 IDC_DIALOG1,并为其分配一个值。
名称加值的定义存储在 Resource.h 文件中。
**步骤 1** - 让我们看一下上一章中**CMFCDialogDemo**示例,在该示例中,我们创建了一个对话框,其 ID 为**IDD_EXAMPLE_DLG**。
**步骤 2** - 转到“解决方案资源管理器”,您将在“头文件”下看到 resource.h 文件。继续在编辑器中打开此文件,您将看到对话框标识符及其整数值。
图标
**图标**是在窗口上使用的小图片,代表一个应用程序。它主要用于两种场景。
在窗口的框架上,它显示在标题栏中窗口名称的左侧。
在 Windows 资源管理器、桌面、“我的电脑”或“控制面板”窗口中。
如果您查看我们的 MFCModalDemo 示例,您会发现 Visual Studio 正在为标题栏使用默认图标,如下面的快照所示。
您可以按照以下步骤创建自己的图标:
**步骤 1** - 右键单击您的项目并选择“添加”→“资源”,您将看到“添加资源”对话框。
**步骤 2** - 选择“图标”并单击“新建”按钮,您将看到以下图标。
**步骤 3** - 在“解决方案资源管理器”中,转到“资源视图”并展开 MFCModalDemo > 图标。您将看到两个图标。IDR_MAINFRAME 是默认图标,IDI_ICON1 是新创建的图标。
**步骤 4** - 右键单击新创建的图标并选择“属性”。
**步骤 5** - IDI_ICON1 是此图标的 ID,现在让我们将其 ID 更改为 IDR_MYICON。
**步骤 6** - 现在您可以根据需要在设计器中更改此图标。我们将使用相同的图标。
**步骤 7** - 保存此图标。
**步骤 8** - 转到 CMFCModalDemoDlg.cpp 文件中的 CMFCModalDemoDlg 构造函数,它将如下所示。
CMFCModalDemoDlg::CMFCModalDemoDlg(CWnd* pParent /* = NULL*/) : CDialogEx(IDD_MFCMODALDEMO_DIALOG, pParent) { m_hIcon = AfxGetApp() -> LoadIcon(IDR_MAINFRAME); }
**步骤 9** - 现在您可以看到默认图标已加载到构造函数中。让我们将其更改为 IDR_MYICON,如下面的代码所示。
CMFCModalDemoDlg::CMFCModalDemoDlg(CWnd* pParent /* = NULL*/) : CDialogEx(IDD_MFCMODALDEMO_DIALOG, pParent) { m_hIcon = AfxGetApp() -> LoadIcon(IDR_ MYICON); }
**步骤 10** - 当以上代码编译并执行时,您将看到新图标显示在对话框上。
菜单
**菜单**允许您以逻辑且易于查找的方式排列命令。使用菜单编辑器,您可以通过直接使用与完成的应用程序中的菜单栏非常相似的菜单栏来创建和编辑菜单。要创建菜单,请按照以下步骤操作:
**步骤 1** - 右键单击您的项目并选择“添加”→“资源”。您将看到“添加资源”对话框。
**步骤 2** - 选择“菜单”并单击“新建”。您将在菜单栏上看到包含“在此处键入”的矩形。
**步骤 3** - 编写一些菜单选项,如“文件”、“编辑”等,如下面的快照所示。
**步骤 4** - 如果您在“资源视图”中展开“菜单”文件夹,您将看到菜单标识符 IDR_MENU1。右键单击此标识符并将其更改为 IDM_MAINMENU。
**步骤 5** - 保存所有更改。
**步骤 6** - 我们需要将此菜单附加到我们的对话框。在“解决方案资源管理器”中展开您的“对话框”文件夹,然后双击对话框标识符。
**步骤 7** - 您将在“属性”中看到“菜单”字段。从下拉列表中选择菜单标识符,如上所示。
**步骤 8** - 运行此应用程序,您将看到以下对话框,其中还包含菜单选项。
工具栏
**工具栏**是 Windows 控件,允许用户通过单击按钮而不是使用菜单来在表单上执行某些操作。
工具栏提供了一组方便的按钮,通过将最容易访问的操作作为按钮来简化用户的工作。
工具栏可以将此类常见操作更靠近用户。
工具栏通常显示在主菜单下方。
它们可以配备按钮,但有时它们的按钮或某些按钮带有标题。
工具栏还可以配备其他类型的控件。
要创建工具栏,请按照以下步骤操作。
**步骤 1** - 右键单击您的项目并选择“添加”→“资源”。您将看到“添加资源”对话框。
**步骤 2** - 选择“工具栏”并单击“新建”。您将看到以下屏幕。
**步骤 3** - 在设计器中设计您的工具栏,如下面的屏幕截图所示,并指定 ID。
**步骤 4** - 在 CMFCModalDemoDlg 类中添加这两个变量。
CToolBar m_wndToolBar; BOOL butD;
**步骤 5** - 以下是 CMFCModalDemoDlg.h 文件中 CMFCModalDemoDlg 的完整实现:
class CMFCModalDemoDlg : public CDialogEx { // Construction public: CMFCModalDemoDlg(CWnd* pParent = NULL); // standard constructor // Dialog Data #ifdef AFX_DESIGN_TIME enum { IDD = IDD_MFCMODALDEMO_DIALOG }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support // Implementation protected: HICON m_hIcon; CToolBar m_wndToolBar; BOOL butD; // Generated message map functions virtual BOOL OnInitDialog(); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: afx_msg void OnBnClickedOk(); };
**步骤 6** - 更新 CMFCModalDemoDlg::OnInitDialog(),如下面的代码所示。
BOOL CMFCModalDemoDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon if (!m_wndToolBar.Create(this) || !m_wndToolBar.LoadToolBar(IDR_TOOLBAR1)) //if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | // WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | // CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || // !m_wndToolBar.LoadToolBar(IDR_TOOLBAR1)) { TRACE0("Failed to Create Dialog Toolbar\n"); EndDialog(IDCANCEL); } butD = TRUE; CRect rcClientOld; // Old Client Rect CRect rcClientNew; // New Client Rect with Tollbar Added // Retrive the Old Client WindowSize // Called to reposition and resize control bars in the client area of a window // The reposQuery FLAG does not really traw the Toolbar. It only does the calculations. // And puts the new ClientRect values in rcClientNew so we can do the rest of the Math. GetClientRect(rcClientOld); RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0, reposQuery, rcClientNew); // All of the Child Windows (Controls) now need to be moved so the Tollbar does not cover them up. // Offest to move all child controls after adding Tollbar CPoint ptOffset(rcClientNew.left - rcClientOld.left, rcClientNew.top - rcClientOld.top); CRect rcChild; CWnd* pwndChild = GetWindow(GW_CHILD); //Handle to the Dialog Controls while (pwndChild) // Cycle through all child controls { pwndChild -> GetWindowRect(rcChild); // Get the child control RECT ScreenToClient(rcChild); // Changes the Child Rect by the values of the claculated offset rcChild.OffsetRect(ptOffset); pwndChild -> MoveWindow(rcChild, FALSE); // Move the Child Control pwndChild = pwndChild -> GetNextWindow(); } CRect rcWindow; // Get the RECT of the Dialog GetWindowRect(rcWindow); // Increase width to new Client Width rcWindow.right += rcClientOld.Width() - rcClientNew.Width(); // Increase height to new Client Height rcWindow.bottom += rcClientOld.Height() - rcClientNew.Height(); // Redraw Window MoveWindow(rcWindow, FALSE); // Now we REALLY Redraw the Toolbar RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0); // TODO: Add extra initialization here return TRUE; // return TRUE unless you set the focus to a control }
**步骤 7** - 运行此应用程序。您将看到以下对话框,其中还包含工具栏。
加速键
**访问键**是一个字母,允许用户通过使用键盘而不是鼠标来更快地执行菜单操作。这通常更快,因为用户不需要将鼠标定位在任何位置,从而减少了执行操作所需的时间。
**步骤 1** - 要创建访问键,请在菜单项左侧键入一个“&”符号。
**步骤 2** - 对所有菜单选项重复此步骤。运行此应用程序并按 Alt。您将看到所有菜单选项的第一个字母都带下划线。
快捷键
快捷键是高级用户用来执行操作的键或键组合,这些操作原本需要通过菜单项来完成。大多数快捷键都是同时按下 Ctrl 键和一个字母键的组合。例如,Ctrl + N、Ctrl + O 或 Ctrl + D。
要创建快捷键,在构成菜单标题的字符串右侧,右键单击菜单项并选择属性。
在“标题”字段中键入 \t 后跟所需的组合,如下所示,用于“新建”菜单选项。对所有菜单选项重复此步骤。
加速器表
加速器表是一个项目的列表,其中表中的每个项目都组合了标识符、快捷键和一个指定加速器键类型的常量数字。就像其他资源一样,可以在 .rc 文件中手动创建加速器表。以下是创建加速器表的步骤。
步骤 1 - 要创建加速器表,请在解决方案资源管理器中右键单击 *.rc 文件。
步骤 2 - 选择“加速器”并单击“新建”。
步骤 3 - 单击 ID 组合框的箭头并选择菜单项。
步骤 4 - 从“修饰符”下拉列表中选择 Ctrl。
步骤 5 - 单击“键”框并为两个菜单选项键入相应的键。
我们还将向测试中添加“新建”菜单项事件处理程序。右键单击“新建”菜单选项。
步骤 6 - 您可以指定类、消息类型和处理程序名称。目前,让我们保持原样,然后单击“添加和编辑”按钮。
步骤 7 - 选择“添加事件处理程序”。
步骤 8 - 您现在将在 CMFCModalDemoDlg.cpp 文件的末尾看到添加的事件。
void CMFCModalDemoDlg::OnFileNew() { // TODO: Add your command handler code here MessageBox(L"File > New menu option"); }
步骤 9 - 现在让我们添加一个消息框,该消息框将显示简单的菜单选项消息。
要在工作中启动加速器表,请添加 HACCEL 变量和 ProcessMessageFilter,如下面的 CMFCModalDemoApp 中所示。
class CMFCModalDemoApp : public CWinApp { public: CMFCModalDemoApp(); // Overrides public: virtual BOOL InitInstance(); HACCEL m_hAccelTable; // Implementation DECLARE_MESSAGE_MAP() virtual BOOL ProcessMessageFilter(int code, LPMSG lpMsg); };
步骤 10 - 加载加速器并在 CMFCModalDemoApp::InitInstance() 中进行以下调用。
m_hAccelTable = LoadAccelerators(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_ACCELERATOR1));
步骤 11 - 这是 ProcessMessageFilter 的实现。
BOOL CMFCModalDemoApp::ProcessMessageFilter(int code, LPMSG lpMsg) { if (code >= 0 && m_pMainWnd && m_hAccelTable) { if (::TranslateAccelerator(m_pMainWnd -> m_hWnd, m_hAccelTable, lpMsg)) return TRUE; } return CWinApp::ProcessMessageFilter(code, lpMsg); }
步骤 12 - 当编译并执行上述代码时,您将看到以下输出。
步骤 13 - 按 Alt 键,然后按 F 键,再按 N 键或 Ctrl + N。您将看到以下消息。
MFC - 属性表
属性表,也称为选项卡对话框,是一个包含属性页的对话框。每个属性页都基于对话框模板资源并包含控件。它在一个页面上封闭,顶部有一个选项卡。选项卡命名页面并指示其用途。用户单击属性表中的选项卡以选择一组控件。
要创建属性页,让我们通过创建一个基于 MFC 的对话框项目来了解一个简单的示例。
创建项目后,我们需要添加一些属性页。
Visual Studio 通过显示“添加资源”对话框、展开“对话框”节点并选择其中一个 IDD_PROPPAGE_X 项,从而轻松创建属性页的资源。
步骤 1 - 在解决方案资源管理器中右键单击您的项目,然后选择“添加”→“资源”。
步骤 2 - 选择 IDD_PROPPAGE_LARGE 并单击“新建”。
步骤 3 - 让我们将此属性页的 ID 和标题分别更改为IDD_PROPPAGE_1 和属性页 1,如上所示。
步骤 4 - 在设计器窗口中右键单击属性页。
步骤 5 - 选择“添加类”选项。
步骤 6 - 输入类名并从基类下拉列表中选择 CPropertyPage。
步骤 7 - 单击“完成”继续。
步骤 8 - 通过遵循上述步骤,再添加一个 ID 为 IDD_PROPPAGE_2、标题为属性页 2 的属性页。
步骤 9 - 您现在可以看到创建了两个属性页。要实现其功能,我们需要一个属性表。
属性表将属性页组合在一起并将其保留为实体。
要创建属性表,请按照以下步骤操作 -
步骤 1 - 右键单击您的项目,然后选择“添加”>“类”菜单选项。
步骤 2 - 从左侧窗格中选择“Visual C++”→“MFC”,从模板窗格中选择“MFC 类”,然后单击“添加”。
步骤 3 - 输入类名并从基类下拉列表中选择 CPropertySheet。
步骤 4 - 单击“完成”继续。
步骤 5 - 要启动此属性表,我们需要对我们的主项目类进行以下更改。
步骤 6 - 在 CMFCPropSheetDemo.cpp 文件中添加以下引用。
#include "MySheet.h" #include "PropPage1.h" #include "PropPage2.h"
步骤 7 - 修改 CMFCPropSheetDemoApp::InitInstance() 方法,如下面的代码所示。
CMySheet mySheet(L"Property Sheet Demo"); CPropPage1 page1; CPropPage2 page2; mySheet.AddPage(&page1); mySheet.AddPage(&page2); m_pMainWnd = &mySheet; INT_PTR nResponse = mySheet.DoModal();
步骤 8 - 这是 CMFCPropSheetDemo.cpp 文件的完整实现。
// MFCPropSheetDemo.cpp : Defines the class behaviors for the application. // #include "stdafx.h" #include "MFCPropSheetDemo.h" #include "MFCPropSheetDemoDlg.h" #include "MySheet.h" #include "PropPage1.h" #include "PropPage2.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CMFCPropSheetDemoApp BEGIN_MESSAGE_MAP(CMFCPropSheetDemoApp, CWinApp) ON_COMMAND(ID_HELP, &CWinApp::OnHelp) END_MESSAGE_MAP() // CMFCPropSheetDemoApp construction CMFCPropSheetDemoApp::CMFCPropSheetDemoApp() { // support Restart Manager m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART; // TODO: add construction code here, // Place all significant initialization in InitInstance } // The one and only CMFCPropSheetDemoApp object CMFCPropSheetDemoApp theApp; // CMFCPropSheetDemoApp initialization BOOL CMFCPropSheetDemoApp::InitInstance() { // InitCommonControlsEx() is required on Windows XP if an application // manifest specifies use of ComCtl32.dll version 6 or later to enable // visual styles. Otherwise, any window creation will fail. INITCOMMONCONTROLSEX InitCtrls; InitCtrls.dwSize = sizeof(InitCtrls); // Set this to include all the common control classes you want to use // in your application. InitCtrls.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&InitCtrls); CWinApp::InitInstance(); AfxEnableControlContainer(); // Create the shell manager, in case the dialog contains // any shell tree view or shell list view controls. CShellManager *pShellManager = new CShellManager; // Activate "Windows Native" visual manager for enabling themes in MFC controls CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows)); // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need // Change the registry key under which our settings are stored // TODO: You should modify this string to be something appropriate // such as the name of your company or organization SetRegistryKey(_T("Local AppWizard-Generated Applications")); CMySheet mySheet(L"Property Sheet Demo"); CPropPage1 page1; CPropPage2 page2; mySheet.AddPage(&page1); mySheet.AddPage(&page2); m_pMainWnd = &mySheet; INT_PTR nResponse = mySheet.DoModal(); if (nResponse == IDOK) { // TODO: Place code here to handle when the dialog is // dismissed with OK }else if (nResponse == IDCANCEL) { // TODO: Place code here to handle when the dialog is // dismissed with Cancel }else if (nResponse == -1) { TRACE(traceAppMsg, 0, "Warning: dialog creation failed, so application is terminating unexpectedly.\n"); TRACE(traceAppMsg, 0, "Warning: if you are using MFC controls on the dialog, you cannot #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS.\n"); } // Delete the shell manager created above. if (pShellManager != NULL) { delete pShellManager; } // Since the dialog has been closed, return FALSE so that we exit the // application, rather than start the application's message pump. return FALSE; }
步骤 9 - 当编译并执行上述代码时,您将看到以下对话框。此对话框包含两个属性页。
MFC - Windows 布局
控件布局非常重要,对于应用程序可用性至关重要。它用于在应用程序中排列一组 GUI 元素。在选择布局时,需要考虑一些重要事项 -
- 子元素的位置。
- 子元素的大小。
添加控件
让我们创建一个新的基于对话框的 MFC 项目 MFCLayoutDemo。
步骤 1 - 创建项目后,您将看到以下屏幕。
步骤 2 - 从对话框中删除 TODO。
步骤 3 - 从工具箱中拖动一些控件,您可以在左侧看到它。
(我们将拖动一个静态文本和一个编辑控件,如下面的快照所示)。
步骤 4 - 将静态文本的标题更改为“名称”。
控件网格
控件网格是指导网格点,可以帮助在设计时定位您正在添加的控件。
要启用控件网格,您需要单击工具栏中的“切换网格”按钮,如下面的快照所示。
控件调整大小
将控件添加到对话框后,它将采用其默认大小或您绘制的大小。为了帮助调整表单或对话框上控件的大小,Visual Studio 提供了一个由黑点组成的视觉网格。
要调整控件的大小,即为其指定特定的宽度或高度,请将鼠标放在其中一个句柄上并将其拖动到所需的方向。
您现在可以使用此点状网格调整控件的大小。
控件位置
您在对话框或表单上定位的控件将采用其给定的位置。大多数情况下,这些位置不实用。您可以将它们移动到您选择的任何位置。
让我们添加更多控件 -
步骤 1 - 要移动控件,请单击并将其拖动到所需的方向,直到到达目标位置。
步骤 2 - 要移动一组控件,请先选择它们。然后将所选内容拖动到所需位置。让我们选择静态文本和编辑控件。
步骤 3 - 将这些选定的控件移动到左侧。
为了帮助定位控件,Visual Studio 提供了带有以下按钮的“对话框”工具栏。
步骤 1 - 让我们通过选择所有这些控件将复选框和静态文本控件对齐到左侧。
步骤 2 - 选择“格式”→“对齐”→“左对齐”。
步骤 3 - 您现在可以看到所有这些控件都对齐到左侧。
选项卡顺序
您添加到表单或对话框的控件按添加顺序排列。无论您将新控件放置在哪个部分或区域,添加控件时,它都会按顺序放置在现有控件的末尾。如果您不修复它,用户将难以导航控件。控件导航的顺序也称为选项卡顺序。
要更改选项卡,您可以使用“格式”→“选项卡顺序”菜单选项,也可以使用 Ctrl + D 快捷键。让我们按 Ctrl + D。
您现在可以看到将所有这些控件添加到此对话框的顺序。要更改控件的顺序或序列,请按您希望导航的顺序依次单击所有控件。
在此示例中,我们将首先单击复选框,然后单击“名称”和“地址”编辑控件。然后单击“确定”和“取消”,如下面的快照所示。
让我们运行此应用程序,您将看到以下输出。
MFC - 控件管理
在 MFC 应用程序中,在将控件可视化地添加到应用程序后,如果要在代码中引用它,则可以根据该控件或与其关联的控件声明一个变量。MFC 库允许您为应用程序中使用的一些控件声明两种类型的变量:值或控件变量。
一个变量用于存储在控件中的信息,也称为控件变量/实例。
另一个变量称为控件值变量。用户可以使用此变量对该控件执行某种操作。
控件变量/实例
控件变量是基于管理控件的类的变量。例如,按钮控件基于 CButton 类。
要了解在实际编程中这些概念,让我们创建一个基于 MFC 对话框的项目 MFCControlManagement。
创建项目后,您将在设计器窗口中看到以下对话框。
步骤 1 - 删除 TODO 行,并拖动一个复选框和一个编辑控件,如下面的快照所示。将复选框的标题更改为“启用控件”。
步骤 2 - 右键单击复选框。
步骤 3 - 选择“添加变量”。
步骤 4 - 您现在可以看到“添加成员变量向导”。
您可以在此对话框上选择不同的选项。对于复选框,变量类型为 CButton。它在此对话框中默认选中。
同样,控件 ID 也已默认选中,现在我们需要在“类别”组合框中选择“控件”,在“变量名”编辑框中键入 m_enableDisableCheck,然后单击“完成”。
步骤 5 - 同样,使用如下面的快照所示的设置添加编辑控件的控件变量。
观察对话框类的头文件。您现在可以看到已添加了新变量。
CButton m_enableDisableCheck; CEdit m_myEditControl;
控件值变量
您可以为控件声明的另一种类型的变量是值变量。并非所有控件都提供值变量。
值变量必须能够处理其旨在引用的控件中存储的值的类型。
例如,由于文本控件用于处理文本,因此您可以为其声明一个基于文本的数据类型。这通常是一个 CString 变量。
让我们看看复选框和编辑控件的这种变量类型。
步骤 1 − 右键单击复选框,然后选择“添加变量”。
步骤 2 − 变量类型为 BOOL。从“类别”下拉列表中选择“值”。
步骤 3 − 单击“完成”继续。
步骤 4 − 同样,使用以下快照中所示的设置,为编辑控件添加值变量。
步骤 5 − 在变量类型中键入 CString,在变量名称字段中键入 m_editControlVal。
步骤 6 − 现在您可以在头文件中看到这些添加的变量。
bool m_enableDisableVal; CString m_editControlVal;
控件事件处理程序
将控件添加到应用程序后,无论您是通过可视化方式添加还是动态创建,您还需要决定如何处理用户可能对控件执行的操作。
对于已与类关联的项目对话框,在创建事件处理程序时,您可以利用一些快捷方式。
您可以快速创建默认控件通知事件或任何适用的 Windows 消息的处理程序。
让我们看看我们在其中为复选框添加事件处理程序的相同示例。
步骤 1 − 右键单击要处理通知事件的控件。
步骤 2 − 在快捷菜单上,单击“添加事件处理程序”以显示“事件处理程序向导”。
步骤 3 − 在“消息类型”框中选择要添加到“类”列表框中所选类的事件。
步骤 4 − 接受“函数处理程序名称”框中的默认名称,或提供您选择的名称。
步骤 5 − 单击“添加并编辑”以添加事件处理程序。
步骤 6 − 现在您可以在 CMFCControlManagementDlg.cpp 文件末尾看到添加了以下事件。
void CMFCControlManagementDlg::OnBnClickedCheck1() { // TODO: Add your control notification handler code here }
控件管理
到目前为止,我们已经了解了如何将控件添加到应用程序。现在,我们将了解如何根据用户需求管理这些控件。我们可以在特定的事件处理程序中使用控件变量/实例。
步骤 1 − 让我们看看以下示例。在这里,我们将根据复选框的选中/未选中状态启用/禁用编辑控件。
步骤 2 − 我们现在添加了复选框单击事件处理程序。以下是实现方法:
void CMFCControlManagementDlg::OnBnClickedCheck1() { // TODO: Add your control notification handler code here UpdateData(TRUE); if (m_enableDisableVal) m_myEditControl.EnableWindow(TRUE); else m_myEditControl.EnableWindow(FALSE); }
步骤 3 − 创建对话框时,我们需要将以下代码添加到 CMFCControlManagementDlg::OnInitDialog() 中。这将管理这些控件。
UpdateData(TRUE); if (m_enableDisableVal) m_myEditControl.EnableWindow(TRUE); else m_myEditControl.EnableWindow(FALSE);
步骤 4 − 以下是 CMFCControlManagementDlg.cpp 文件的完整实现。
// MFCControlManagementDlg.cpp : implementation file // #include "stdafx.h" #include "MFCControlManagement.h" #include "MFCControlManagementDlg.h" #include "afxdialogex.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CAboutDlg dialog used for App About class CAboutDlg : public CDialogEx { public: CAboutDlg(); // Dialog Data #ifdef AFX_DESIGN_TIME enum { IDD = IDD_ABOUTBOX }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support // Implementation protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) END_MESSAGE_MAP() // CMFCControlManagementDlg dialog CMFCControlManagementDlg::CMFCControlManagementDlg(CWnd* pParent /* = NULL*/) :CDialogEx(IDD_MFCCONTROLMANAGEMENT_DIALOG, pParent) , m_enableDisableVal(FALSE) , m_editControlVal(_T("")) { m_hIcon = AfxGetApp()&rarr LoadIcon(IDR_MAINFRAME); } void CMFCControlManagementDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_CHECK1, m_enableDisableCheck); DDX_Control(pDX, IDC_EDIT1, m_myEditControl); DDX_Check(pDX, IDC_CHECK1, m_enableDisableVal); DDX_Text(pDX, IDC_EDIT1, m_editControlVal); } BEGIN_MESSAGE_MAP(CMFCControlManagementDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_CHECK1, &CMFCControlManagementDlg::OnBnClickedCheck1) END_MESSAGE_MAP() // CMFCControlManagementDlg message handlers BOOL CMFCControlManagementDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu → AppendMenu(MF_SEPARATOR); pSysMenu → AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here UpdateData(TRUE); if (m_enableDisableVal) m_myEditControl.EnableWindow(TRUE); else m_myEditControl.EnableWindow(FALSE); return TRUE; // return TRUE unless you set the focus to a control } void CMFCControlManagementDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); }else { CDialogEx::OnSysCommand(nID, lParam); } } // If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void CMFCControlManagementDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); }else { CDialogEx::OnPaint(); } } // The system calls this function to obtain the cursor to display while the user drags // the minimized window. HCURSOR CMFCControlManagementDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } void CMFCControlManagementDlg::OnBnClickedCheck1() { // TODO: Add your control notification handler code here UpdateData(TRUE); if (m_enableDisableVal) m_myEditControl.EnableWindow(TRUE); else m_myEditControl.EnableWindow(FALSE); }
步骤 5 − 编译并执行上述代码后,您将看到以下输出。复选框默认情况下未选中。这也将禁用编辑控件。
步骤 6 − 选中“启用控件”复选框。这将自动启用编辑控件。
MFC - Windows 控件
Windows 控件是用户可以与之交互以输入或操作数据的对象。它们通常出现在对话框或工具栏中。有各种类型的控件:
基于文本的控件用于向用户显示文本或向用户请求文本。
基于列表的控件显示项目列表。
基于进度的控件用于显示操作的进度。
静态控件可用于显示颜色、图片或不经常适合上述类别的内容。
序号 | 控件和描述 |
---|---|
1 | 静态控件
静态控件是一个向用户显示信息的对象,无需用户的直接干预。它可以用来显示颜色、几何图形或图片,例如图标、位图或动画。 |
2 | 动画控件
动画控件是一个窗口,用于显示 AVI 格式的音频剪辑。AVI 剪辑是一系列位图帧,就像电影一样。动画控件只能播放简单的 AVI 剪辑,并且不支持声音。它由CAnimateCtrl类表示。 |
3 | 按钮
按钮是用户单击以启动操作的对象。按钮控件由CButton 类表示。 |
4 | 位图按钮
位图按钮在其表面上显示图片或图片和文本。这通常是为了使按钮更明确。位图按钮是使用CBitmapButton 类创建的,该类派生自 CButton。 |
5 | 命令按钮
命令按钮是普通按钮的增强版。它在左侧显示一个绿色箭头图标,然后是常规大小的标题。在主标题下方,它可以显示另一个较小的标题,作为提示提供更多信息。 |
6 | 静态文本
静态控件显示文本字符串、框、矩形、图标、光标、位图或增强型图元文件。它由CStatic 类表示。它可以用于标记、框定或分隔其他控件。静态控件通常不接收输入也不提供输出。 |
7 | 列表框
列表框显示项目列表,例如用户可以查看和选择的的文件名。列表框由CListBox 类表示。在单选列表框中,用户只能选择一个项目。在多选列表框中,可以选择一系列项目。当用户选择一个项目时,该项目会被突出显示,并且列表框会向父窗口发送通知消息。 |
8 | 组合框
组合框由列表框与静态控件或编辑控件组合而成。它由CComboBox 类表示。控件的列表框部分可以始终显示,也可以仅在用户选择控件旁边的下拉箭头时下拉。 |
9 | 单选按钮
单选按钮是一个控件,显示为一个圆圈周围的点。实际上,单选按钮伴随着一个或多个其他单选按钮,这些按钮作为一组显示并运行。 |
10 | 复选框
复选框是一个 Windows 控件,允许用户将项目的真值设置为真或假。 |
11 | 图像列表
图像列表是相同大小的图像的集合,每个图像都可以通过其基于零的索引来引用。图像列表用于有效地管理大量图标或位图。图像列表由CImageList 类表示。 |
12 | 编辑框
编辑框是用户可以在其中输入文本的矩形子窗口。它由CEdit 类表示。 |
13 | 富文本编辑
富文本编辑控件是一个窗口,用户可以在其中输入和编辑文本。文本可以分配字符和段落格式,并且可以包含嵌入的 OLE 对象。它由CRichEditCtrl 类表示。 |
14 | 组框
组框是一个静态控件,用于设置控件的可视或编程组。该控件是一个矩形,将其他控件组合在一起。 |
15 | 微调按钮
微调按钮控件(也称为上下控件)是一对箭头按钮,用户可以单击这些按钮来增加或减少值,例如滚动位置或伴随控件中显示的数字。它由CSpinButtonCtrl 类表示。 |
16 | 管理上下控件
它管理上下控件。 |
17 | 进度控件
进度条控件是一个窗口,应用程序可以使用它来指示冗长操作的进度。它由一个矩形组成,随着操作的进行,矩形会从左到右逐渐填充系统高亮颜色。它由CProgressCtrl 类表示。 |
18 | 进度条
进度条是一个窗口,应用程序可以使用它来指示操作的进度。 |
19 | 定时器
定时器是一个非空间对象,它使用来自计算机或应用程序的重复时间间隔。为了工作,每个时间间隔,控件都会向操作系统发送消息。与大多数其他控件不同,MFC 定时器既没有按钮来表示它,也没有类。要创建定时器,只需调用 CWnd::SetTimer() 方法即可。此函数调用为您的应用程序创建了一个定时器。与其他控件一样,定时器使用标识符。 |
20 | 日期和时间选择器
日期和时间选择器控件 (CDateTimeCtrl) 实现了一种直观且易于识别的输入或选择特定日期的方法。控件的主要界面在功能上类似于组合框。但是,如果用户展开控件,则会显示月历控件(默认情况下),允许用户指定特定日期。选择日期后,月历控件会自动消失。 |
21 | 图片
如果您需要为应用程序显示图片,Visual C++ 提供了一个专门的控件用于此目的。 |
22 | 图像编辑器
图像编辑器具有一套广泛的创建和编辑图像的工具,以及帮助您创建工具栏位图的功能。除了位图、图标和光标之外,您还可以使用“图像”菜单上的命令和“图像编辑器”工具栏上的工具编辑 GIF 或 JPEG 格式的图像。 |
23 | 滑块控件
滑块控件(也称为轨迹条)是一个包含滑块和可选刻度的窗口。当用户使用鼠标或方向键移动滑块时,控件会发送通知消息以指示更改。滑块有两种类型:水平和垂直。它由CSliderCtrl 类表示。 |
24 | 滚动条
滚动条是一个图形控件元素,可以通过单击箭头在两个方向上沿着控件滚动连续的文本、图片或其他任何内容。此控件可以采用两个方向之一:水平或垂直。它由CScrollBar类表示。 |
25 | 树形控件
树形视图控件是一个窗口,用于显示项目的层次列表,例如文档中的标题、索引中的条目或磁盘上的文件和目录。每个项目都包含一个标签和一个可选的位图图像,并且每个项目都可以有一系列与其关联的子项目。通过单击一个项目,用户可以展开和折叠关联的子项目列表。它由CTreeCtrl类表示。 |
26 | 列表控件
封装列表视图控件的功能,列表视图控件显示项目的集合,每个项目都包含一个图标(来自图像列表)和一个标签。它由CListCtrl类表示。列表控件包括使用四种视图之一来显示项目列表。 |
MFC - 消息和事件
一个应用程序是由各种对象组成的。大多数情况下,计算机上会运行多个应用程序,并且操作系统不断被要求执行一些任务。由于可能会有很多无法预测的请求出现,因此操作系统将决定权留给对象,由它们指定想要什么,何时想要,以及期望什么行为或结果。
概述
Microsoft Windows 操作系统无法预测一个对象需要处理什么样的请求,以及另一个对象需要什么样的任务。
为了管理所有这些任务和请求,对象会发送消息。
每个对象都有责任决定发送什么消息以及何时发送。
为了发送消息,控件必须创建一个事件。
为了区分两者,消息的名称通常以 WM_ 开头,代表窗口消息。
事件的名称通常以 On 开头,表示一个动作。
事件是发送消息的动作。
消息映射
由于 Windows 是一个面向消息的操作系统,因此 Windows 环境的大部分编程都涉及消息处理。每次发生诸如击键或鼠标点击之类的事件时,都会向应用程序发送一条消息,然后应用程序必须处理该事件。
为了让编译器管理消息,它们应该包含在类定义中。
DECLARE_MESSAGE_MAP 宏应在类定义的末尾提供,如下面的代码所示。
class CMainFrame : public CFrameWnd { public: CMainFrame(); protected: DECLARE_MESSAGE_MAP() };
实际的消息应该列在 DECLARE_MESSAGE_MAP 行的正上方。
要实现消息,您需要创建一个程序正在使用的消息表。
此表使用两个分隔宏;
它以 BEGIN_MESSAGE_MAP 开头,以 END_MESSAGE_MAP 宏结束。
BEGIN_MESSAGE_MAP 宏需要两个参数,即类的名称和派生类所来自的 MFC 类,如下面的代码所示。
#include <afxwin.h> class CMainFrame : public CFrameWnd { public: CMainFrame(); protected: DECLARE_MESSAGE_MAP() }; CMainFrame::CMainFrame() { // Create the window's frame Create(NULL, L"MFC Messages Demo", WS_OVERLAPPEDWINDOW, CRect(120, 100, 700, 480), NULL); } class CMessagesApp : public CWinApp { public: BOOL InitInstance(); }; BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) END_MESSAGE_MAP() BOOL CMessagesApp::InitInstance(){ m_pMainWnd = new CMainFrame; m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); return TRUE; } CMessagesApp theApp;
让我们通过创建一个新的 Win32 项目来了解一个简单的示例。
步骤 1 - 要创建 MFC 项目,请右键单击该项目并选择“属性”。
步骤 2 - 在左侧部分,单击“配置属性”→“常规”。
步骤 3 - 在“项目默认值”部分中选择“在共享 DLL 中使用 MFC”选项,然后单击“确定”。
步骤 4 - 我们需要添加一个新的源文件。
步骤 5 - 右键单击您的项目,然后选择“添加”→“新建项”。
步骤 6 - 在“模板”部分中,单击“C++ 文件 (.cpp)”。
步骤 7 - 单击“添加”继续。
步骤 8 - 现在,在 *.cpp 文件中添加以下代码。
#include <afxwin.h> class CMainFrame : public CFrameWnd { public: CMainFrame(); protected: DECLARE_MESSAGE_MAP() }; CMainFrame::CMainFrame() { // Create the window's frame Create(NULL, L"MFC Messages Demo", WS_OVERLAPPEDWINDOW, CRect(120, 100, 700, 480), NULL); } class CMessagesApp : public CWinApp { public: BOOL InitInstance(); }; BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) END_MESSAGE_MAP() BOOL CMessagesApp::InitInstance() { m_pMainWnd = new CMainFrame; m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); return TRUE; } CMessagesApp theApp;
Windows 消息
有不同类型的 Windows 消息,例如创建窗口、显示窗口等。以下是一些常用的 Windows 消息。
让我们来看一个简单的窗口创建示例。
WM_CREATE - 当一个称为窗口的对象被创建时,创建对象的框架会发送一条标识为 ON_WM_CREATE 的消息。
步骤 1 - 要创建 ON_WM_CREATE,请在 DECLARE_MESSAGE_MAP() 之前添加 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);,如下所示。
class CMainFrame : public CFrameWnd { public: CMainFrame(); protected: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); DECLARE_MESSAGE_MAP() };
步骤 2 - 在 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) 之后和 END_MESSAGE_MAP() 之前添加 ON_WM_CREATE()。
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_WM_CREATE() END_MESSAGE_MAP()
步骤 3 - 这是 OnCreate() 的实现。
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { // Call the base class to create the window if (CFrameWnd::OnCreate(lpCreateStruct) == 0) { // If the window was successfully created, let the user know MessageBox(L"The window has been created!!!"); // Since the window was successfully created, return 0 return 0; } // Otherwise, return -1 return -1; }
步骤 4 - 现在您的 *.cpp 文件将如下面的代码所示。
#include <afxwin.h> class CMainFrame : public CFrameWnd { public: CMainFrame(); protected: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); DECLARE_MESSAGE_MAP() }; CMainFrame::CMainFrame() { // Create the window's frame Create(NULL, L"MFC Messages Demo", WS_OVERLAPPEDWINDOW, CRect(120, 100, 700, 480), NULL); } class CMessagesApp : public CWinApp { public: BOOL InitInstance(); }; BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_WM_CREATE() END_MESSAGE_MAP() int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { // Call the base class to create the window if (CFrameWnd::OnCreate(lpCreateStruct) == 0) { // If the window was successfully created, let the user know MessageBox(L"The window has been created!!!"); // Since the window was successfully created, return 0 return 0; } // Otherwise, return -1 return -1; } BOOL CMessagesApp::InitInstance() { m_pMainWnd = new CMainFrame; m_pMainWnd -> ShowWindow(SW_SHOW); m_pMainWnd -> UpdateWindow(); return TRUE; } CMessagesApp theApp;
步骤 5 - 当上述代码编译并执行时,您将看到以下输出。
步骤 6 - 当您单击“确定”时,它将显示主窗口。
命令消息
图形应用程序的主要功能之一是呈现 Windows 控件和资源,允许用户与机器交互。我们将学习的控件示例包括按钮、列表框、组合框等。
我们在上一课中介绍的一种资源是菜单。当用户单击此类控件和资源时,它们可以启动自己的消息。来自 Windows 控件或资源的消息称为命令消息。
让我们来看一个简单的命令消息示例。
为了使应用程序能够创建新的文档,CWinApp 类提供了 OnFileNew() 方法。
afx_msg void OnFileNew(); BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_COMMAND(ID_FILE_NEW, CMainFrame::OnFileNew) END_MESSAGE_MAP()
这是方法定义 -
void CMainFrame::OnFileNew() { // Create New file }
键盘消息
键盘是连接到计算机的硬件对象。默认情况下,它用于在控件上输入可识别的符号、字母和其他字符。键盘上的每个键都显示一个符号、一个字母或它们的组合,以指示该键可以用于什么。用户通常按下某个键,该键会向程序发送信号。
每个键都有一个操作系统可以识别的代码。此代码称为虚拟键码。
按下某个键会导致WM_KEYDOWN或WM_SYSKEYDOWN消息被放置到线程消息中。这可以定义如下:
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
让我们来看一个简单的例子。
步骤1 - 这是消息。
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_WM_CREATE() ON_WM_KEYDOWN() END_MESSAGE_MAP()
步骤2 - 这是OnKeyDown()的实现。
void CMainFrame::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { switch (nChar) { case VK_RETURN: MessageBox(L"You pressed Enter"); break; case VK_F1: MessageBox(L"Help is not available at the moment"); break; case VK_DELETE: MessageBox(L"Can't Delete This"); break; default: MessageBox(L"Whatever"); } }
步骤3 - 当以上代码编译并执行时,您将看到以下输出。
步骤4 - 当您按下Enter键时,它将显示以下消息。
鼠标消息
鼠标是另一个连接到计算机的对象,允许用户与机器交互。
如果按下左鼠标按钮,则会发送ON_WM_LBUTTONDOWN消息。此消息的语法如下:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point)
如果按下右鼠标按钮,则会发送ON_WM_RBUTTONDOWN消息。其语法如下:
afx_msg void OnRButtonDown(UINT nFlags, CPoint point)
同样,如果释放左鼠标,则会发送ON_WM_LBUTTONUP消息。其语法如下:
afx_msg void OnLButtonUp(UINT nFlags, CPoint point)
如果释放右鼠标,则会发送ON_WM_TBUTTONUP消息。其语法如下:
afx_msg void OnRButtonUp(UINT nFlags, CPoint point)
让我们来看一个简单的例子。
步骤1 - 在CMainFrame类定义中添加以下两个函数,如以下代码所示。
class CMainFrame : public CFrameWnd { public: CMainFrame(); protected: afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnRButtonUp(UINT nFlags, CPoint point); DECLARE_MESSAGE_MAP() };
步骤2 - 添加以下两个消息映射。
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_WM_KEYDOWN() ON_WM_LBUTTONDOWN() ON_WM_RBUTTONUP() END_MESSAGE_MAP()
步骤3 - 这是函数定义。
void CMainFrame::OnLButtonDown(UINT nFlags, CPoint point) { CString MsgCoord; MsgCoord.Format(L"Left Button at P(%d, %d)", point.x, point.y); MessageBox(MsgCoord); } void CMainFrame::OnRButtonUp(UINT nFlags, CPoint point) { MessageBox(L"Right Mouse Button Up"); }
步骤4 - 当您运行此应用程序时,您将看到以下输出。
步骤5 - 当您单击“确定”时,您将看到以下消息。
步骤6 - 在此窗口上右键单击。现在,当您释放鼠标右键时,它将显示以下消息。
MFC - ActiveX控件
ActiveX控件容器是一个父程序,它为ActiveX(以前称为OLE)控件提供运行环境。
ActiveX控件是使用Microsoft ActiveX技术的控件。
ActiveX不是一种编程语言,而是一组关于应用程序如何共享信息的规则。
程序员可以使用多种语言开发ActiveX控件,包括C、C++、Visual Basic和Java。
您可以创建能够包含ActiveX控件的应用程序,无论是否使用MFC,但使用MFC更容易。
让我们来看一个在基于MFC对话框的应用程序中添加ActiveX控件的简单示例。
步骤1 - 在设计器窗口中右键单击对话框,然后选择“插入ActiveX控件”。
步骤2 - 选择“Microsoft图片剪辑控件”,然后单击“确定”。
步骤3 - 调整图片控件的大小,并在“属性”窗口中单击“图片”字段。
步骤4 - 浏览包含图片的文件夹。选择任何图片。
步骤5 - 当您运行此应用程序时,您将看到以下输出。
让我们来看另一个简单的例子。
步骤1 - 在设计器窗口中右键单击对话框。
步骤2 - 选择“插入ActiveX控件”。
步骤3 - 选择“Microsoft进度条控件6.0”,单击“确定”。
步骤4 - 选择进度条,并在“属性”窗口中将其“方向”设置为“1 - ccOrientationVertical”。
步骤5 - 为进度条添加控件变量。
步骤6 - 在OnInitDialog()中添加以下代码
m_progBarCtrl.SetScrollRange(0,100,TRUE); m_progBarCtrl.put_Value(53);
步骤7 - 当您再次运行此应用程序时,您将看到进度条也以垂直方向显示。
MFC - 文件系统
在本章中,我们将讨论文件系统的各个组成部分。
驱动器
驱动器是连接到计算机的物理设备,以便存储信息。逻辑磁盘、逻辑卷或虚拟磁盘(简称VD或vdisk)是一种虚拟设备,它在计算机系统中的一个或多个物理磁盘驱动器上提供可用存储容量的区域。驱动器可以是硬盘、CD ROM、DVD ROM、闪存(USB)驱动器、存储卡等。
您想要执行的主要操作之一是获取计算机上的驱动器列表。
让我们通过创建一个新的基于MFC对话框的应用程序来看一个简单的例子。
步骤1 - 从工具箱中拖动一个按钮,将其“标题”更改为“获取驱动器信息”。
步骤2 - 删除静态控件(TODO行)的“标题”,并将它的ID更改为IDC_STATIC_TEXT。
步骤3 - 右键单击按钮,然后选择“添加事件处理程序”。
步骤4 - 选择BN_CLICKED消息类型,然后单击“添加和编辑”按钮。
步骤5 - 为静态文本控件添加值变量m_strDrives。
为了支持计算机上的驱动器,Win32库提供了Microsoft Window的GetLogicalDrives()函数,该函数将检索当前计算机上所有驱动器的列表。
步骤6 - 当以上代码编译并执行时,您将看到以下输出。
步骤7 - 当您单击按钮时,您可以看到计算机上的所有驱动器。
目录
在计算中,目录是文件系统编目结构,其中包含对其他计算机文件的引用,以及可能的其他目录。目录是物理位置。它可以处理驱动器上不可用的操作。
让我们通过创建一个新的基于MFC对话框的应用程序来看一个简单的例子
步骤1 - 从工具箱中拖动三个按钮。将其“标题”更改为“创建目录”、“删除目录”和“移动目录”。
步骤2 - 将这些按钮的ID更改为IDC_BUTTON_CREATE、IDC_BUTTON_DELETE和IDC_BUTTON_MOVE。
步骤3 - 删除TODO行。
步骤4 - 为每个按钮添加事件处理程序。
步骤5 - 要创建目录,可以调用Win32库的CreateDirectory()方法。
步骤6 - 以下是创建按钮事件处理程序的实现,在其中我们将创建一个目录,然后创建另外两个子目录。
void CMFCDirectoriesDemoDlg::OnBnClickedButtonCreate() { // TODO: Add your control notification handler code here SECURITY_ATTRIBUTES saPermissions; saPermissions.nLength = sizeof(SECURITY_ATTRIBUTES); saPermissions.lpSecurityDescriptor = NULL; saPermissions.bInheritHandle = TRUE; if (CreateDirectory(L"D:\\MFCDirectoryDEMO", &saPermissions) == TRUE) AfxMessageBox(L"The directory was created."); CreateDirectory(L"D:\\MFCDirectoryDEMO\\Dir1", NULL); CreateDirectory(L"D:\\MFCDirectoryDEMO\\Dir2", NULL); }
步骤7 - 要删除目录,可以调用Win32库的RemoveDirectory()函数。以下是删除按钮事件处理程序的实现。
void CMFCDirectoriesDemoDlg::OnBnClickedButtonDelete() { // TODO: Add your control notification handler code here if (RemoveDirectory(L"D:\\MFCDirectoryDEMO\\Dir1") == TRUE) AfxMessageBox(L"The directory has been deleted"); }
步骤8 - 如果要移动目录,也可以调用相同的MoveFile()函数。以下是移动按钮事件处理程序的实现,在其中我们将首先创建新目录,然后将Dir2移动到该目录。
void CMFCDirectoriesDemoDlg::OnBnClickedButtonMove() { // TODO: Add your control notification handler code here CreateDirectory(L"D:\\MFCDirectory", NULL); if (MoveFile(L"D:\\MFCDirectoryDEMO\\Dir1", L"D:\\MFCDirectory\\Dir1") == TRUE) AfxMessageBox(L"The directory has been moved"); }
步骤9 - 当以上代码编译并执行时,您将看到以下输出。
步骤10 - 当您单击“创建目录”按钮时,它将创建这些目录。
步骤11 - 当您单击“删除目录”按钮时,它将删除Dir1。
文件处理
MFC应用程序中的大多数文件处理都是与名为CArchive的类结合执行的。CArchive类充当应用程序与用于存储数据或使其可用的介质之间的中继。它允许您以永久二进制形式(通常是磁盘存储)保存复杂的网络对象,这些对象在这些对象被删除后仍然存在。
以下是CArchive类中的方法列表:
以下是用于存储和检索数据的运算符列表
序号 | 名称 & 描述 |
---|---|
1 | 运算符<< 将对象和基本类型存储到存档中。 |
2 | 运算符>> 从存档中加载对象和基本类型。 |
让我们通过创建一个新的基于MFC对话框的应用程序来看一个简单的例子。
步骤1 - 拖动一个编辑控件和两个按钮,如下面的快照所示。
步骤2 - 为编辑控件添加控件变量m_editCtrl和值变量m_strEdit。
步骤3 - 为“打开”和“保存”按钮添加单击事件处理程序。
步骤4 - 以下是事件处理程序的实现。
void CMFCFileProcessingDlg::OnBnClickedButtonOpen() { // TODO: Add your control notification handler code here UpdateData(TRUE); CFile file; file.Open(L"ArchiveText.rpr", CFile::modeRead); if(file) { CArchive ar(&file, CArchive::load); ar >> m_strEdit; ar.Close(); file.Close(); } UpdateData(FALSE); } void CMFCFileProcessingDlg::OnBnClickedButtonSave() { // TODO: Add your control notification handler code here UpdateData(TRUE); if (m_strEdit.GetLength() == 0) { AfxMessageBox(L"You must enter the name of the text."); return; } CFile file; file.Open(L"ArchiveText.rpr", CFile::modeCreate | CFile::modeWrite); CArchive ar(&file, CArchive::store); ar << m_strEdit; ar.Close(); file.Close(); }
步骤 5 - 当上述代码编译并执行时,您将看到以下输出。
步骤6 - 输入一些内容并单击“保存”。它将以二进制格式保存数据。
步骤7 - 从编辑控件中删除文本。当您单击“打开”时,观察相同的文本是否再次加载。
MFC - 标准 I/O
MFC库提供了自己的文件处理版本。这是通过名为CStdioFile的类完成的。CStdioFile类派生自CFile。它可以处理Unicode文本文件以及普通的多字节文本文件的读取和写入。
以下是可初始化CStdioFile对象的构造函数列表:
CStdioFile(); CStdioFile(CAtlTransactionManager* pTM); CStdioFile(FILE* pOpenStream); CStdioFile(LPCTSTR lpszFileName, UINT nOpenFlags); CStdioFile(LPCTSTR lpszFileName, UINT nOpenFlags, CAtlTransactionManager* pTM);
以下是CStdioFile中的方法列表:
序号 | 名称 & 描述 |
---|---|
1 | 打开 重载。Open设计用于与默认的CStdioFile构造函数一起使用(覆盖CFile::Open)。 |
2 | 读取字符串 读取一行文本。 |
3 | 查找 定位当前文件指针。 |
4 | 写入字符串 写入一行文本。 |
让我们再次通过创建一个新的基于MFC对话框的应用程序来查看一个简单的示例。
步骤1 - 拖动一个编辑控件和两个按钮,如下面的快照所示。
步骤2 - 为编辑控件添加值变量m_strEditCtrl。
步骤3 - 为“打开”和“保存”按钮添加单击事件处理程序。
步骤4 - 以下是事件处理程序的实现。
void CMFCStandardIODlg::OnBnClickedButtonOpen() { // TODO: Add your control notification handler code here UpdateData(TRUE); CStdioFile file; file.Open(L"D:\\MFCDirectoryDEMO\\test.txt", CFile::modeRead | CFile::typeText); file.ReadString(m_strEditCtrl); file.Close(); UpdateData(FALSE); } void CMFCStandardIODlg::OnBnClickedButtonSave() { // TODO: Add your control notification handler code here UpdateData(TRUE); CStdioFile file; if (m_strEditCtrl.GetLength() == 0) { AfxMessageBox(L"You must specify the text."); return; } file.Open(L"D:\\MFCDirectoryDEMO\\test.txt", CFile::modeCreate | CFile::modeWrite | CFile::typeText); file.WriteString(m_strEditCtrl); file.Close(); }
步骤 5 - 当上述代码编译并执行时,您将看到以下输出。
步骤6 - 输入一些内容并单击“保存”。它将数据保存到*.txt文件中。
步骤7 - 如果您查看文件的位置,您将看到它包含test.txt文件。
步骤8 - 现在,关闭应用程序。运行相同的应用程序。当您单击“打开”时,相同的文本将再次加载。
步骤9 - 它首先打开文件,读取文件,然后更新编辑控件。
MFC - 文档/视图
文档/视图架构是用于创建基于Microsoft Foundation Classes库的应用程序的基础。它允许您区分构成计算机程序的不同部分,包括用户在应用程序中看到的内容以及用户将要处理的文档。这是通过结合在一起作为整体工作的单独类来完成的。
构成文档/视图架构的部分是框架、一个或多个文档以及视图。这些实体组合在一起构成一个可用的应用程序。
查看
视图是用户在其上工作以完成其工作的平台。为了让用户在应用程序上执行任何操作,您必须提供一个视图,该视图是一个基于CView类的对象。您可以直接使用派生自CView的类之一,也可以从CView或其子类之一派生自己的自定义类。
文件
文档类似于一个桶。对于计算机应用程序,文档保存用户的数据。要创建此架构的文档部分,您必须从CDocument类派生一个对象。
框架
顾名思义,框架是构建块、结构和项目边界的组合。框架为窗口提供“物理”存在。它还定义了对象相对于Windows桌面的位置。
单文档界面 (SDI)
表达式单文档界面或SDI指的是只能向用户呈现一个视图的文档。这意味着应用程序一次不能显示多个文档。如果您想查看当前应用程序的另一种类型的文档,则必须创建应用程序的另一个实例。记事本和写字板是SDI应用程序的示例。
让我们通过创建一个新的基于MFC对话框的应用程序来查看单文档界面或SDI的简单示例。
步骤1 - 让我们创建一个新的MFC应用程序MFCSDIDemo,并使用以下设置。
步骤2 - 从应用程序类型中选择“单文档”,从项目样式中选择“MFC标准”。
步骤3 - 单击“完成”继续。
步骤4 - 项目创建完成后,运行应用程序,您将看到以下输出。
多文档界面 (MDI)
如果用户可以在应用程序中打开多个文档而无需关闭它,则该应用程序被称为多文档界面或MDI。为了提供此功能,应用程序提供了一个父框架,充当计算机程序的主框架。在此框架内,应用程序允许创建具有单独框架的视图,使每个视图与其他视图区分开来。
让我们通过创建一个新的基于MFC对话框的应用程序来查看多文档界面或MDI的简单示例。
步骤1 - 让我们创建一个新的MFC应用程序MFCMDIDemo,并使用以下设置。
步骤2 - 从应用程序类型中选择“多文档”,从项目样式中选择“MFC标准”。
步骤3 - 单击“完成”继续。
步骤4 - 项目创建完成后,运行应用程序,您将看到以下输出。
步骤5 - 当您单击“文件”→“新建”菜单选项时,它将创建另一个子窗口,如下面的快照所示。
步骤6 - 在多文档界面 (MDI) 应用程序中,每个应用程序都有一个主框架。在这种情况下,一个CMDIFrameWnd,以及每个文档一个派生自CMDIChildWnd的子框架。
MFC - 字符串
字符串是表示字符序列的对象。C风格字符字符串起源于C语言,并在C++中继续得到支持。
此字符串实际上是一个以空字符'\0'结尾的一维字符数组。
空终止字符串包含构成字符串的字符,后跟一个空字符。
以下是字符数组的简单示例。
char word[12] = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0' };
以下是另一种表示方式。
char word[] = "Hello, World";
Microsoft Foundation Class (MFC) 库提供了一个名为CString的类来操作字符串。以下是CString的一些重要特性。
CString没有基类。
CString对象包含一个可变长度的字符序列。
CString使用类似于Basic的语法提供函数和运算符。
连接和比较运算符以及简化的内存管理使CString对象比普通字符数组更容易使用。
以下是CString的构造函数。
序号 | 方法和描述 |
---|---|
1 | CString 以各种方式构造CString对象 |
以下是数组方法列表:
序号 | 方法和描述 |
---|---|
1 | GetLength 返回CString对象中的字符数。 |
2 | IsEmpty 测试CString对象是否不包含任何字符。 |
3 | 清空 强制字符串长度为0。 |
4 | GetAt 返回指定位置的字符。 |
5 | SetAt 在指定位置设置字符。 |
以下是比较方法列表:
序号 | 方法和描述 |
---|---|
1 | 比较 比较两个字符串(区分大小写)。 |
2 | CompareNoCase 比较两个字符串(不区分大小写)。 |
以下是提取方法列表:
序号 | 方法和描述 |
---|---|
1 | 中间 提取字符串的中间部分(如Basic MID$函数)。 |
2 | 左边 提取字符串的左侧部分(如Basic LEFT$函数)。 |
3 | 右边 提取字符串的右侧部分(如Basic RIGHT$函数)。 |
4 | SpanIncluding 从字符串中提取字符,这些字符位于给定的字符集中。 |
5 | SpanExcluding 从字符串中提取不在给定字符集中的字符。 |
以下是转换方法列表。
序号 | 方法和描述 |
---|---|
1 | MakeUpper 将此字符串中的所有字符转换为大写字符。 |
2 | MakeLower 将此字符串中的所有字符转换为小写字符。 |
3 | MakeReverse 反转此字符串中的字符。 |
4 | 格式 像sprintf一样格式化字符串。 |
5 | TrimLeft 从字符串中修剪前导空白字符。 |
6 | TrimRight 从字符串中修剪尾随空白字符。 |
以下是搜索方法列表。
序号 | 方法和描述 |
---|---|
1 | 查找 在较大的字符串内查找字符或子字符串。 |
2 | ReverseFind 在较大的字符串内查找字符;从结尾开始。 |
3 | FindOneOf 从集合中查找第一个匹配的字符。 |
以下是缓冲区访问方法列表。
序号 | 方法和描述 |
---|---|
1 | GetBuffer 返回指向CString中字符的指针。 |
2 | GetBufferSetLength 返回指向CString中字符的指针,截断到指定的长度。 |
3 | ReleaseBuffer 释放GetBuffer返回的缓冲区的控制权 |
4 | FreeExtra 通过释放之前分配给字符串的任何额外内存来删除此字符串对象的任何开销。 |
5 | LockBuffer 禁用引用计数并保护缓冲区中的字符串。 |
6 | UnlockBuffer 启用引用计数并释放缓冲区中的字符串。 |
以下是Windows特定方法列表。
序号 | 方法和描述 |
---|---|
1 | AllocSysString 从CString数据分配BSTR。 |
2 | SetSysString 使用来自CString对象的数据设置现有的BSTR对象。 |
3 | LoadString 从Windows CE资源加载现有的CString对象。 |
以下是CString对象的各种操作:
创建字符串
您可以通过使用字符串文字或创建CString类的实例来创建字符串。
BOOL CMFCStringDemoDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon CString string1 = _T("This is a string1"); CString string2("This is a string2"); m_strText.Append(string1 + L"\n"); m_strText.Append(string2); UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
编译并执行上述代码后,您将看到以下输出。
空字符串
您可以通过使用空字符串文字或使用CString::Empty()方法来创建空字符串。您还可以使用布尔属性isEmpty来检查字符串是否为空。
BOOL CMFCStringDemoDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon CString string1 = _T(""); CString string2; string2.Empty(); if(string1.IsEmpty()) m_strText.Append(L"String1 is empty\n"); else m_strText.Append(string1 + L"\n"); if(string2.IsEmpty()) m_strText.Append(L"String2 is empty"); else m_strText.Append(string2); UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
编译并执行上述代码后,您将看到以下输出。
字符串连接
要连接两个或多个字符串,您可以使用+运算符连接两个字符串或使用CString::Append()方法。
BOOL CMFCStringDemoDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon //To concatenate two CString objects CString s1 = _T("This "); // Cascading concatenation s1 += _T("is a "); CString s2 = _T("test"); CString message = s1; message.Append(_T("big ") + s2); // Message contains "This is a big test". m_strText = L"message: " + message; UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
编译并执行上述代码后,您将看到以下输出。
字符串长度
要查找字符串的长度,您可以使用CString::GetLength()方法,该方法返回CString对象中的字符数。
BOOL CMFCStringDemoDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon CString string1 = _T("This is string 1"); int length = string1.GetLength(); CString strLen; strLen.Format(L"\nString1 contains %d characters", length); m_strText = string1 + strLen; UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
编译并执行上述代码后,您将看到以下输出。
字符串比较
要比较两个字符串变量,您可以使用==运算符
BOOL CMFCStringDemoDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon CString string1 = _T("Hello"); CString string2 = _T("World"); CString string3 = _T("MFC Tutorial"); CString string4 = _T("MFC Tutorial"); if (string1 == string2) m_strText = "string1 and string1 are same\n"; else m_strText = "string1 and string1 are not same\n"; if (string3 == string4) m_strText += "string3 and string4 are same"; else m_strText += "string3 and string4 are not same"; UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
编译并执行上述代码后,您将看到以下输出。
MFC - CArray
CArray 是一种集合,最适合用于以随机或非顺序方式访问的数据。CArray 类支持类似于 C 数组的数组,但可以根据需要动态缩小和增长。
数组索引始终从位置 0 开始。
您可以决定是修复上限还是在添加超过当前边界的元素时启用数组扩展。
即使某些元素为空,内存也会连续分配到上限。
以下是 CArray 对象的不同操作:
创建 CArray 对象
要创建 CArray 值或对象的集合,您必须首先确定集合的值类型。您可以使用现有的基本数据类型,例如 int、CString、double 等,如下所示:
CArray<CString, CString>strArray;
添加项目
要添加项目,可以使用 CArray::Add() 函数。它在数组末尾添加一个项目。在 OnInitDialog() 中,创建 CArray 对象并添加三个名称,如下面的代码所示。
CArray<CString, CString>strArray; //Add names to CArray strArray.Add(L"Ali"); strArray.Add(L"Ahmed"); strArray.Add(L"Mark");
检索项目
要检索任何项目,可以使用 CArray::GetAt() 函数。此函数采用一个整数参数作为数组的索引。
步骤 1 - 让我们看一个简单的示例,它将检索所有名称。
//Retrive names from CArray for (int i = 0; i < strArray.GetSize(); i++) { m_strText.Append(strArray.GetAt(i) + L"\n"); }
步骤 2 - 以下是 CMFCCArrayDlg::OnInitDialog() 的完整实现
BOOL CMFCCArrayDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here CArray<CString, CString>strArray; //Add names to CArray strArray.Add(L"Ali"); strArray.Add(L"Ahmed"); strArray.Add(L"Mark"); //Retrive names from CArray for (int i = 0; i < strArray.GetSize(); i++) { m_strText.Append(strArray.GetAt(i) + L"\n"); } UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
步骤3 - 当以上代码编译并执行时,您将看到以下输出。
在中间添加项目
要在数组中间添加项目,可以使用 CArray::.InsertAt() 函数。它采用两个参数 - 第一个参数是索引,第二个参数是值。
让我们在索引 1 处插入一个新项目,如下面的代码所示。
BOOL CMFCCArrayDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here CArray<CString, CString>strArray; //Add names to CArray strArray.Add(L"Ali"); strArray.Add(L"Ahmed"); strArray.Add(L"Mark"); strArray.InsertAt(1, L"Allan"); //Retrive names from CArray for (int i = 0; i < strArray.GetSize(); i++) { m_strText.Append(strArray.GetAt(i) + L"\n"); } UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
编译并执行上述代码后,您将看到以下输出。您现在可以看到 Allan 作为第二个索引添加的名称。
更新项目值
要更新数组中间的项目,可以使用 CArray::.SetAt() 函数。它采用两个参数 - 第一个参数是索引,第二个参数是值。
让我们更新数组中的第三个元素,如下面的代码所示。
BOOL CMFCCArrayDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here CArray<CString, CString>strArray; //Add names to CArray strArray.Add(L"Ali"); strArray.Add(L"Ahmed"); strArray.Add(L"Mark"); strArray.InsertAt(1, L"Allan"); strArray.SetAt(2, L"Salman"); //Retrive names from CArray for (int i = 0; i < strArray.GetSize(); i++) { m_strText.Append(strArray.GetAt(i) + L"\n"); } UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
编译并执行上述代码后,您将看到以下输出。您现在可以看到第三个元素的值已更新。
复制数组
要将整个数组复制到另一个 CArray 对象,可以使用 CArray::Copy() 函数。
步骤 1 - 让我们创建另一个数组并将第一个数组中的所有元素复制到该数组中,如下面的代码所示。
BOOL CMFCCArrayDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu→AppendMenu(MF_SEPARATOR); pSysMenu→AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here CArray<CString, CString>strArray; //Add names to CArray strArray.Add(L"Ali"); strArray.Add(L"Ahmed"); strArray.Add(L"Mark"); strArray.InsertAt(1, L"Allan"); strArray.SetAt(2, L"Salman"); CArray<CString, CString>strArray2; strArray2.Copy(strArray); //Retrive names from CArray for (int i = 0; i < strArray2.GetSize(); i++) { m_strText.Append(strArray2.GetAt(i) + L"\n"); } UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
您现在可以看到我们已从第二个数组中检索了元素,并且输出相同,因为我们使用了复制函数。
删除项目
要删除任何特定项目,可以使用 CArray::RemoveAt() 函数。要从列表中删除所有元素,可以使用 CArray::RemoveAll() 函数。
让我们从数组中删除第二个元素。
BOOL CMFCCArrayDlg::OnInitDialog() { CDialogEx::OnInitDialog(); SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here CArray<CString, CString>strArray; //Add names to CArray strArray.Add(L"Ali"); strArray.Add(L"Ahmed"); strArray.Add(L"Mark"); strArray.InsertAt(1, L"Allan"); strArray.SetAt(2, L"Salman"); CArray<CString, CString>strArray2; strArray2.Copy(strArray); strArray2.RemoveAt(1); //Retrive names from CArray for (int i = 0; i < strArray2.GetSize(); i++) { m_strText.Append(strArray2.GetAt(i) + L"\n"); } UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
编译并执行上述代码后,您将看到以下输出。您现在可以看到 Allan 的名称不再是数组的一部分。
MFC - 链接列表
链接列表是一种线性数据结构,其中每个元素都是一个单独的对象。列表的每个元素(我们将其称为节点)包含两个项目 - 数据和对下一个节点的引用。最后一个节点对 null 的引用。
链接列表是一种数据结构,由一组节点组成,这些节点共同表示一个序列。它是一种使用结构存储数据的方式,以便程序员可以在需要时自动创建一个新的存储数据的地方。它的一些主要特征是:
链接列表是一系列包含项目的链接。
每个链接都包含到另一个链接的连接。
列表中的每个项目都称为节点。
如果列表至少包含一个节点,则一个新节点将被定位为列表中的最后一个元素。
如果列表只有一个节点,则该节点表示第一个和最后一个项目。
链接列表有两种类型:
单向链接列表
单向链接列表是一种数据结构。在单向链接列表中,列表中的每个节点都存储节点的内容以及指向列表中下一个节点的指针或引用。
双向链接列表
双向链接列表是一种链接数据结构,由一组称为节点的顺序链接记录组成。每个节点包含两个字段,它们分别引用序列中前一个节点和下一个节点。
CList 类
MFC 提供了一个类CList,它是一个模板链接列表实现,并且可以完美运行。CList 列表的行为类似于双向链接列表。类型为 POSITION 的变量是列表的关键。您可以使用 POSITION 变量作为迭代器顺序遍历列表,并作为书签来保存位置。
以下是 CList 对象的不同操作:
创建 CList 对象
要创建 CList 值或对象的集合,您必须首先确定集合的值类型。您可以使用现有的基本数据类型,例如 int、CString、double 等,如下面的代码所示。
CList<double, double>m_list;
添加项目
要添加项目,可以使用 CList::AddTail() 函数。它在列表末尾添加一个项目。要将元素添加到列表的开头,可以使用 CList::AddHead() 函数。在 OnInitDialog() CList 中,创建对象并添加四个值,如下面的代码所示。
CList<double, double>m_list; //Add items to the list m_list.AddTail(100.75); m_list.AddTail(85.26); m_list.AddTail(95.78); m_list.AddTail(90.1);
检索项目
类型为 POSITION 的变量是列表的关键。您可以使用 POSITION 变量作为迭代器顺序遍历列表。
步骤 1 - 要从列表中检索元素,我们可以使用以下代码,它将检索所有值。
//iterate the list POSITION pos = m_list.GetHeadPosition(); while (pos) { double nData = m_list.GetNext(pos); CString strVal; strVal.Format(L"%.2f\n", nData); m_strText.Append(strVal); }
步骤 2 - 以下是完整的 CMFCCListDemoDlg::OnInitDialog() 函数。
BOOL CMFCCListDemoDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here CList<double, double>m_list; //Add items to the list m_list.AddTail(100.75); m_list.AddTail(85.26); m_list.AddTail(95.78); m_list.AddTail(90.1); //iterate the list POSITION pos = m_list.GetHeadPosition(); while (pos) { double nData = m_list.GetNext(pos); CString strVal; strVal.Format(L"%.f\n", nData); m_strText.Append(strVal); } UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
步骤3 - 当以上代码编译并执行时,您将看到以下输出。
在中间添加项目
要在列表中间添加项目,可以使用 CList::.InsertAfter() 和 CList::.InsertBefore() 函数。它采用两个参数 - 第一个参数是位置(可以在哪里添加),第二个参数是值。
步骤 1 - 让我们插入一个新项目,如下面的代码所示。
BOOL CMFCCListDemoDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here CList<double, double>m_list; //Add items to the list m_list.AddTail(100.75); m_list.AddTail(85.26); m_list.AddTail(95.78); m_list.AddTail(90.1); POSITION position = m_list.Find(85.26); m_list.InsertBefore(position, 200.0); m_list.InsertAfter(position, 300.0); //iterate the list POSITION pos = m_list.GetHeadPosition(); while (pos) { double nData = m_list.GetNext(pos); CString strVal; strVal.Format(L"%.2f\n", nData); m_strText.Append(strVal); } UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
步骤 2 - 您现在可以看到我们首先检索了值 85.26 的位置,然后在该值之前和之后插入了一个元素。
步骤3 - 当以上代码编译并执行时,您将看到以下输出。
更新项目值
要更新数组中间的项目,可以使用 CArray::.SetAt() 函数。它采用两个参数 - 第一个参数是位置,第二个参数是值。
让我们将列表中的 300.00 更新为 400,如下面的代码所示。
BOOL CMFCCListDemoDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here CList<double, double>m_list; //Add items to the list m_list.AddTail(100.75); m_list.AddTail(85.26); m_list.AddTail(95.78); m_list.AddTail(90.1); POSITION position = m_list.Find(85.26); m_list.InsertBefore(position, 200.0); m_list.InsertAfter(position, 300.0); position = m_list.Find(300.00); m_list.SetAt(position, 400.00); //iterate the list POSITION pos = m_list.GetHeadPosition(); while (pos) { double nData = m_list.GetNext(pos); CString strVal; strVal.Format(L"%.2f\n", nData); m_strText.Append(strVal); } UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
编译并执行上述代码后,您将看到以下输出。您现在可以看到 300.00 的值已更新为 400.00。
删除项目
要删除任何特定项目,可以使用 CList::RemoveAt() 函数。要从列表中删除所有元素,可以使用 CList::RemoveAll() 函数。
让我们删除值为 95.78 的元素。
BOOL CMFCCListDemoDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here CList<double, double>m_list; //Add items to the list m_list.AddTail(100.75); m_list.AddTail(85.26); m_list.AddTail(95.78); m_list.AddTail(90.1); POSITION position = m_list.Find(85.26); m_list.InsertBefore(position, 200.0); m_list.InsertAfter(position, 300.0); position = m_list.Find(300.00); m_list.SetAt(position, 400.00); position = m_list.Find(95.78); m_list.RemoveAt(position); //iterate the list POSITION pos = m_list.GetHeadPosition(); while (pos) { double nData = m_list.GetNext(pos); CString strVal; strVal.Format(L"%.2f\n", nData); m_strText.Append(strVal); } UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
编译并执行上述代码后,您将看到以下输出。您现在可以看到 95.78 的值不再是列表的一部分。
MFC - 数据库类
数据库是信息的集合,这些信息经过组织,以便可以轻松地访问、管理和更新。基于 ODBC 的 MFC 数据库类旨在提供对任何可使用 ODBC 驱动程序访问的数据库的访问。由于这些类使用 ODBC,因此您的应用程序可以访问许多不同数据格式和不同本地/远程配置中的数据。
您不必编写专门的代码来处理不同的数据库管理系统 (DBMS)。只要您的用户拥有他们要访问的数据的相应 ODBC 驱动程序,他们就可以使用您的程序来操作存储在那里的表中的数据。数据源是由某些数据库管理系统 (DBMS) 托管的数据的特定实例。示例包括 Microsoft SQL Server、Microsoft Access 等。
CDatabase
MFC 提供了一个类CDatabase,它表示与数据源的连接,您可以通过它来操作数据源。您的应用程序可以同时激活一个或多个 CDatabase 对象。
让我们通过创建一个新的基于MFC对话框的应用程序来看一个简单的例子。
步骤1 - 将TODO行的标题更改为从数据库检索数据,并拖动一个按钮和一个列表控件,如下面的快照所示。
步骤2 - 为按钮添加单击事件处理程序,并为列表控件添加控制变量m_ListControl。
步骤3 - 我们有一个简单的数据库,其中包含一个Employees表和一些记录,如下面的快照所示。
步骤4 - 我们需要包含以下头文件,以便可以使用CDatabase类。
#include "odbcinst.h" #include "afxdb.h"
插入查询
SQL INSERT INTO语句用于向数据库中的表添加新的数据行。
步骤1 - 要添加新记录,我们将使用CDatabase类的ExecuteSQL()函数,如下面的代码所示。
CDatabase database; CString SqlString; CString strID, strName, strAge; CString sDriver = L"MICROSOFT ACCESS DRIVER (*.mdb)"; CString sDsn; CString sFile = L"D:\\Test.mdb"; // You must change above path if it's different int iRec = 0; // Build ODBC connection string sDsn.Format(L"ODBC;DRIVER={%s};DSN='';DBQ=%s", sDriver, sFile); TRY { // Open the database database.Open(NULL,false,false,sDsn); SqlString = "INSERT INTO Employees (ID,Name,age) VALUES (5,'Sanjay',69)"; database.ExecuteSQL(SqlString); // Close the database database.Close(); }CATCH(CDBException, e) { // If a database exception occured, show error msg AfxMessageBox(L"Database error: " + e→m_strError); } END_CATCH;
步骤2 - 当上述代码编译并执行时,您将看到数据库中添加了一条新记录。
检索记录
要在MFC应用程序中检索上述表,我们将在按钮事件处理程序中实现与数据库相关的操作,如下面的步骤所示。
步骤1 - 要使用CDatabase,请构造一个CDatabase对象并调用其Open()函数。这将打开连接。
步骤2 - 为操作连接的数据源构造CRecordset对象,并将记录集构造函数传递给您的CDatabase对象。
步骤3 - 使用完连接后,调用Close函数并销毁CDatabase对象。
void CMFCDatabaseDemoDlg::OnBnClickedButtonRead() { // TODO: Add your control notification handler code here CDatabase database; CString SqlString; CString strID, strName, strAge; CString sDriver = "MICROSOFT ACCESS DRIVER (*.mdb)"; CString sFile = L"D:\\Test.mdb"; // You must change above path if it's different int iRec = 0; // Build ODBC connection string sDsn.Format("ODBC;DRIVER={%s};DSN='';DBQ=%s",sDriver,sFile); TRY { // Open the database database.Open(NULL,false,false,sDsn); // Allocate the recordset CRecordset recset( &database ); // Build the SQL statement SqlString = "SELECT ID, Name, Age " "FROM Employees"; // Execute the query recset.Open(CRecordset::forwardOnly,SqlString,CRecordset::readOnly); // Reset List control if there is any data ResetListControl(); // populate Grids ListView_SetExtendedListViewStyle(m_ListControl,LVS_EX_GRIDLINES); // Column width and heading m_ListControl.InsertColumn(0,"Emp ID",LVCFMT_LEFT,-1,0); m_ListControl.InsertColumn(1,"Name",LVCFMT_LEFT,-1,1); m_ListControl.InsertColumn(2, "Age", LVCFMT_LEFT, -1, 1); m_ListControl.SetColumnWidth(0, 120); m_ListControl.SetColumnWidth(1, 200); m_ListControl.SetColumnWidth(2, 200); // Loop through each record while( !recset.IsEOF() ) { // Copy each column into a variable recset.GetFieldValue("ID",strID); recset.GetFieldValue("Name",strName); recset.GetFieldValue("Age", strAge); // Insert values into the list control iRec = m_ListControl.InsertItem(0,strID,0); m_ListControl.SetItemText(0,1,strName); m_ListControl.SetItemText(0, 2, strAge); // goto next record recset.MoveNext(); } // Close the database database.Close(); }CATCH(CDBException, e) { // If a database exception occured, show error msg AfxMessageBox("Database error: "+e→m_strError); } END_CATCH; } // Reset List control void CMFCDatabaseDemoDlg::ResetListControl() { m_ListControl.DeleteAllItems(); int iNbrOfColumns; CHeaderCtrl* pHeader = (CHeaderCtrl*)m_ListControl.GetDlgItem(0); if (pHeader) { iNbrOfColumns = pHeader→GetItemCount(); } for (int i = iNbrOfColumns; i >= 0; i--) { m_ListControl.DeleteColumn(i); } }
步骤4 - 这是头文件。
// MFCDatabaseDemoDlg.h : header file // #pragma once #include "afxcmn.h" // CMFCDatabaseDemoDlg dialog class CMFCDatabaseDemoDlg : public CDialogEx { // Construction public: CMFCDatabaseDemoDlg(CWnd* pParent = NULL); // standard constructor // Dialog Data #ifdef AFX_DESIGN_TIME enum { IDD = IDD_MFCDATABASEDEMO_DIALOG }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support void ResetListControl(); // Implementation protected: HICON m_hIcon; // Generated message map functions virtual BOOL OnInitDialog(); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: CListCtrl m_ListControl; afx_msg void OnBnClickedButtonRead(); };
步骤 5 - 当上述代码编译并执行时,您将看到以下输出。
步骤6 - 按下读取按钮以执行数据库操作。它将检索Employees表。
更新记录
SQL UPDATE查询用于修改表中现有的记录。您可以将WHERE子句与UPDATE查询一起使用来更新选定的行,否则所有行都将受到影响。
步骤1 - 让我们通过更新ID等于5的Age来了解一个简单的示例。
SqlString = L"UPDATE Employees SET Age = 59 WHERE ID = 5;"; database.ExecuteSQL(SqlString);
步骤2 - 这是按钮单击事件的完整代码。
void CMFCDatabaseDemoDlg::OnBnClickedButtonRead() { // TODO: Add your control notification handler code here CDatabase database; CString SqlString; CString strID, strName, strAge; CString sDriver = L"MICROSOFT ACCESS DRIVER (*.mdb)"; CString sDsn; CString sFile = L"C:\\Users\\Muhammad.Waqas\\Downloads\\Compressed\\ReadDB_demo\\Test.mdb"; // You must change above path if it's different int iRec = 0; // Build ODBC connection string sDsn.Format(L"ODBC;DRIVER={%s};DSN='';DBQ=%s", sDriver, sFile); TRY { // Open the database database.Open(NULL,false,false,sDsn); // Allocate the recordset CRecordset recset(&database); SqlString = L"UPDATE Employees SET Age = 59 WHERE ID = 5;"; database.ExecuteSQL(SqlString); SqlString = "SELECT ID, Name, Age FROM Employees"; // Build the SQL statement SqlString = "SELECT ID, Name, Age FROM Employees"; // Execute the query recset.Open(CRecordset::forwardOnly,SqlString,CRecordset::readOnly); // Reset List control if there is any data ResetListControl(); // populate Grids ListView_SetExtendedListViewStyle(m_listCtrl,LVS_EX_GRIDLINES); // Column width and heading m_listCtrl.InsertColumn(0,L"Emp ID",LVCFMT_LEFT,-1,0); m_listCtrl.InsertColumn(1,L"Name",LVCFMT_LEFT,-1,1); m_listCtrl.InsertColumn(2, L"Age", LVCFMT_LEFT, -1, 1); m_listCtrl.SetColumnWidth(0, 120); m_listCtrl.SetColumnWidth(1, 200); m_listCtrl.SetColumnWidth(2, 200); // Loop through each record while (!recset.IsEOF()) { // Copy each column into a variable recset.GetFieldValue(L"ID",strID); recset.GetFieldValue(L"Name",strName); recset.GetFieldValue(L"Age", strAge); // Insert values into the list control iRec = m_listCtrl.InsertItem(0,strID,0); m_listCtrl.SetItemText(0,1,strName); m_listCtrl.SetItemText(0, 2, strAge); // goto next record recset.MoveNext(); } // Close the database database.Close(); }CATCH(CDBException, e) { // If a database exception occured, show error msg AfxMessageBox(L"Database error: " + e→m_strError); } END_CATCH; }
步骤3 - 当以上代码编译并执行时,您将看到以下输出。
步骤4 - 按下读取按钮以执行数据库操作。它将检索以下Employees表。
步骤5 - 您现在可以看到年龄已从69更新为59。
删除记录
SQL DELETE查询用于从表中删除现有记录。您可以将WHERE子句与DELETE查询一起使用来删除选定的行,否则所有记录都将被删除。
步骤1 - 让我们通过删除ID等于3的记录来了解一个简单的示例。
SqlString = L"DELETE FROM Employees WHERE ID = 3;"; database.ExecuteSQL(SqlString);
步骤2 - 这是按钮单击事件的完整代码。
void CMFCDatabaseDemoDlg::OnBnClickedButtonRead() { // TODO: Add your control notification handler code here CDatabase database; CString SqlString; CString strID, strName, strAge; CString sDriver = L"MICROSOFT ACCESS DRIVER (*.mdb)"; CString sDsn; CString sFile = L"C:\\Users\\Muhammad.Waqas\\Downloads\\Compressed\\ReadDB_demo\\Test.mdb"; // You must change above path if it's different int iRec = 0; // Build ODBC connection string sDsn.Format(L"ODBC;DRIVER={%s};DSN='';DBQ=%s", sDriver, sFile); TRY { // Open the database database.Open(NULL,false,false,sDsn); // Allocate the recordset CRecordset recset(&database); SqlString = L"DELETE FROM Employees WHERE ID = 3;"; database.ExecuteSQL(SqlString); SqlString = "SELECT ID, Name, Age FROM Employees"; // Build the SQL statement SqlString = "SELECT ID, Name, Age FROM Employees"; // Execute the query recset.Open(CRecordset::forwardOnly,SqlString,CRecordset::readOnly); // Reset List control if there is any data ResetListControl(); // populate Grids ListView_SetExtendedListViewStyle(m_listCtrl,LVS_EX_GRIDLINES); // Column width and heading m_listCtrl.InsertColumn(0,L"Emp ID",LVCFMT_LEFT,-1,0); m_listCtrl.InsertColumn(1,L"Name",LVCFMT_LEFT,-1,1); m_listCtrl.InsertColumn(2, L"Age", LVCFMT_LEFT, -1, 1); m_listCtrl.SetColumnWidth(0, 120); m_listCtrl.SetColumnWidth(1, 200); m_listCtrl.SetColumnWidth(2, 200); // Loop through each record while (!recset.IsEOF()) { // Copy each column into a variable recset.GetFieldValue(L"ID",strID); recset.GetFieldValue(L"Name",strName); recset.GetFieldValue(L"Age", strAge); // Insert values into the list control iRec = m_listCtrl.InsertItem(0,strID,0); m_listCtrl.SetItemText(0,1,strName); m_listCtrl.SetItemText(0, 2, strAge); // goto next record recset.MoveNext(); } // Close the database database.Close(); }CATCH(CDBException, e) { // If a database exception occured, show error msg AfxMessageBox(L"Database error: " + e→m_strError); } END_CATCH; }
步骤3 - 当以上代码编译并执行时,您将看到以下输出。
步骤4 - 按下读取按钮以执行数据库操作。它将检索Employees表。
MFC - 序列化
序列化是将对象写入或读取到持久性存储介质(如磁盘文件)的过程。序列化非常适合需要在程序执行期间或之后维护结构化数据(如C++类或结构)状态的情况。
在执行文件处理时,值通常是基本类型(char、short、int、float或double)。同样,我们可以一次一个地单独保存许多值。此技术不包括从(作为…的变量)类创建的对象。
MFC库对序列化提供了高级支持。它从CObject类开始,该类是大多数MFC类的祖先,并配备了Serialize()成员函数。
让我们通过创建一个新的MFC项目来了解一个简单的示例。
步骤1 - 删除TODO行并设计您的对话框,如下面的快照所示。
步骤2 - 为所有编辑控件添加值变量。对于提到的Emp ID和Age,值类型为整数,如下面的快照所示。
步骤3 - 为这两个按钮添加事件处理程序。
步骤4 - 现在让我们添加一个简单的Employee类,我们需要对其进行序列化。这是头文件中Employee类的声明。
class CEmployee : public CObject { public: int empID; CString empName; int age; CEmployee(void); ~CEmployee(void); private: public: void Serialize(CArchive& ar); DECLARE_SERIAL(CEmployee); };
步骤5 - 这是源(*.cpp)文件中Employee类的定义。
IMPLEMENT_SERIAL(CEmployee, CObject, 0) CEmployee::CEmployee(void) { } CEmployee::~CEmployee(void) { } void CEmployee::Serialize(CArchive& ar) { CObject::Serialize(ar); if (ar.IsStoring()) ar << empID << empName << age; else ar >> empID >> empName >> age; }
步骤6 - 这是保存按钮事件处理程序的实现。
void CMFCSerializationDlg::OnBnClickedButtonSave() { // TODO: Add your control notification handler code here UpdateData(TRUE); CEmployee employee; CFile file; file.Open(L"EmployeeInfo.hse", CFile::modeCreate | CFile::modeWrite); CArchive ar(&file, CArchive::store); employee.empID = m_id; employee.empName = m_strName; employee.age = m_age; employee.Serialize(ar); ar.Close(); }
步骤7 - 这是打开按钮事件处理程序的实现。
void CMFCSerializationDlg::OnBnClickedButtonOpen() { // TODO: Add your control notification handler code here UpdateData(TRUE); CFile file; file.Open(L"EmployeeInfo.hse", CFile::modeRead); CArchive ar(&file, CArchive::load); CEmployee employee; employee.Serialize(ar); m_id = employee.empID; m_strName = employee.empName; m_age = employee.age; ar.Close(); file.Close(); UpdateData(FALSE); }
步骤8 - 当上述代码编译并执行时,您将看到以下输出。
步骤9 - 在所有字段中输入信息,然后单击保存并关闭此程序。
步骤10 - 它将保存数据。再次运行应用程序并单击打开。它将加载Employee信息。
MFC - 多线程
Microsoft Foundation Class (MFC)库提供对多线程应用程序的支持。线程是进程中的执行路径。当您启动记事本时,操作系统会创建一个进程并开始执行该进程的主线程。当此线程终止时,进程也将终止。
如果需要,您可以在应用程序中创建其他线程。MFC应用程序中的所有线程都由CWinThread对象表示。在大多数情况下,您甚至不必显式创建这些对象;而是调用框架辅助函数AfxBeginThread,它将为您创建CWinThread对象。
让我们通过创建一个新的基于MFC对话框的应用程序来看一个简单的例子。
步骤1 - 将静态控件的标题和ID分别更改为单击“启动线程”按钮和IDC_STATIC_TEXT。
步骤2 - 拖动两个按钮并为这些按钮添加单击事件处理程序。
步骤3 - 为静态文本控件添加控制变量。
步骤4 - 现在在CMFCMultithreadingDlg.cpp文件的开头添加以下三个全局变量。
int currValue; int maxValue; BOOL stopNow;
步骤5 - 在CMFCMultithreadingDlg类中添加WM_TIMER消息。
这是OnTimer()的实现
void CMFCMultithreadingDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: Add your message handler code here and/or call default CString sStatusMsg; sStatusMsg.Format(L"Running: %d", currValue); m_ctrlStatus.SetWindowText(sStatusMsg); CDialogEx::OnTimer(nIDEvent); }
步骤6 - 现在添加一个示例函数,用于在CMFCMultithreadingDlg类中的AfxBeginThread中使用。
UINT MyThreadProc(LPVOID Param) { while (!stopNow && (currValue < maxValue)) { currValue++; Sleep(50); // would do some work here } return TRUE; }
步骤7 - 这是“启动线程”按钮的事件处理程序的实现,它将启动线程。
void CMFCMultithreadingDlg::OnBnClickedButtonStart() { // TODO: Add your control notification handler code here currValue = 0; maxValue = 5000; stopNow = 0; m_ctrlStatus.SetWindowText(L"Starting..."); SetTimer(1234, 333, 0); // 3 times per second AfxBeginThread(MyThreadProc, 0); // <<== START THE THREAD }
步骤8 - 这是“停止线程”按钮的事件处理程序的实现,它将停止线程。
void CMFCMultithreadingDlg::OnBnClickedButtonStop() { // TODO: Add your control notification handler code here stopNow = TRUE; KillTimer(1234); m_ctrlStatus.SetWindowText(L"Stopped"); }
步骤9 - 这是完整的源文件。
// MFCMultithreadingDlg.cpp : implementation file // #include "stdafx.h" #include "MFCMultithreading.h" #include "MFCMultithreadingDlg.h" #include "afxdialogex.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CMFCMultithreadingDlg dialog int currValue; int maxValue; BOOL stopNow; CMFCMultithreadingDlg::CMFCMultithreadingDlg(CWnd* pParent /* = NULL*/) : CDialogEx(IDD_MFCMULTITHREADING_DIALOG, pParent) { m_hIcon = AfxGetApp() -> LoadIcon(IDR_MAINFRAME); } void CMFCMultithreadingDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_STATIC_TEXT, m_ctrlStatus); } BEGIN_MESSAGE_MAP(CMFCMultithreadingDlg, CDialogEx) ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BUTTON_START, &CMFCMultithreadingDlg::OnBnClickedButtonStart) ON_WM_TIMER() ON_BN_CLICKED(IDC_BUTTON_STOP, &CMFCMultithreadingDlg::OnBnClickedButtonStop) END_MESSAGE_MAP() // CMFCMultithreadingDlg message handlers BOOL CMFCMultithreadingDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here return TRUE; // return TRUE unless you set the focus to a control } // If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void CMFCMultithreadingDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); }else { CDialogEx::OnPaint(); } } // The system calls this function to obtain the cursor to display while the user drags // the minimized window. HCURSOR CMFCMultithreadingDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } UINT /*CThreadDlg::*/MyThreadProc(LPVOID Param) //Sample function for using in AfxBeginThread { while (!stopNow && (currValue < maxValue)) { currValue++; Sleep(50); // would do some work here } return TRUE; } void CMFCMultithreadingDlg::OnBnClickedButtonStart() { // TODO: Add your control notification handler code here currValue = 0; maxValue = 5000; stopNow = 0; m_ctrlStatus.SetWindowText(L"Starting..."); SetTimer(1234, 333, 0); // 3 times per second AfxBeginThread(MyThreadProc, 0); // <<== START THE THREAD } void CMFCMultithreadingDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: Add your message handler code here and/or call default CString sStatusMsg; sStatusMsg.Format(L"Running: %d", currValue); m_ctrlStatus.SetWindowText(sStatusMsg); CDialogEx::OnTimer(nIDEvent); } void CMFCMultithreadingDlg::OnBnClickedButtonStop() { // TODO: Add your control notification handler code here stopNow = TRUE; KillTimer(1234); m_ctrlStatus.SetWindowText(L"Stopped"); }
步骤10 - 当上述代码编译并执行时,您将看到以下输出。
步骤11 - 现在单击“启动线程”按钮。
步骤12 - 单击“停止线程”按钮。它将停止线程。
MFC - 互联网编程
Microsoft提供了许多用于编程客户端和服务器应用程序的API。许多新的应用程序正在为Internet编写,并且随着技术、浏览器功能和安全选项的变化,将编写新的应用程序类型。您的自定义应用程序可以检索信息并在Internet上提供数据。
MFC提供了一个CSocket类,用于使用Windows Sockets编写网络通信程序。
以下是CSocket类中的方法列表。
序号 | 名称 & 描述 |
---|---|
1 | Attach 将SOCKET句柄附加到CSocket对象。 |
2 | CancelBlockingCall 取消当前正在进行的阻塞调用。 |
3 | Create 创建套接字。 |
4 | FromHandle 给定SOCKET句柄,返回指向CSocket对象的指针。 |
5 | IsBlocking 确定是否正在进行阻塞调用。 |
让我们通过创建一个MFS SDI应用程序来了解一个简单的示例。
步骤1 - 在名称字段中输入MFCServer,然后单击确定。
步骤2 - 在“高级功能”选项卡上,选中“Windows套接字”选项。
步骤3 - 创建项目后,添加一个新的MFC类CServerSocket。
步骤4 - 选择CSocket作为基类,然后单击“完成”。
步骤5 - 添加更多MFC类CReceivingSocket。
步骤6 - CRecevingSocket将接收来自客户端的传入消息。
在CMFCServerApp中,头文件包含以下文件 -
#include "ServerSocket.h" #include "MFCServerView.h"
步骤7 - 在CMFCServerApp类中添加以下两个类变量。
CServerSocket m_serverSocket; CMFCServerView m_pServerView;
步骤8 - 在CMFCServerApp::InitInstance()方法中,创建套接字并指定端口,然后调用Listen方法,如下所示。
m_serverSocket.Create(6666); m_serverSocket.Listen();
步骤9 - 在CMFCServerView头文件中包含以下头文件。
#include "MFCServerDoc.h"
步骤10 - 覆盖Socket类中的OnAccept函数。
步骤11 - 在类视图中选择CServerSocket,并在“属性”窗口中选择突出显示的图标。现在,添加OnAccept。这是OnAccept函数的实现。
void CServerSocket::OnAccept(int nErrorCode) { // TODO: Add your specialized code here and/or call the base class AfxMessageBox(L"Connection accepted"); CSocket::OnAccept(nErrorCode); }
步骤12 - 添加OnReceive()函数。
void CServerSocket::OnReceive(int nErrorCode) { // TODO: Add your specialized code here and/or call the base class AfxMessageBox(L"Data Received"); CSocket::OnReceive(nErrorCode); }
步骤13 - 在CReceivingSocket类中添加OnReceive()函数。
右键单击解决方案资源管理器中的CMFCServerView类,然后选择添加→添加函数。
步骤14 - 输入上述信息,然后单击“完成”。
步骤15 - 在CMFCServerView头文件中添加以下CStringArray变量。
CStringArray m_msgArray;
步骤16 - 这是AddMsg()函数的实现。
void CMFCServerView::AddMsg(CString message) { m_msgArray.Add(message); Invalidate(); }
步骤17 - 更新构造函数,如下面的代码所示。
CMFCServerView::CMFCServerView() { ((CMFCServerApp*)AfxGetApp()) -> m_pServerView = this; }
步骤18 - 这是OnDraw()函数的实现,它显示消息。
void CMFCServerView::OnDraw(CDC* pDC) { int y = 100; for (int i = 0; m_msgArray.GetSize(); i++) { pDC->TextOut(100, y, m_msgArray.GetAt(i)); y += 50; } CMFCServerDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here }
步骤19 - 服务器端现在已完成。它将接收来自客户端的消息。
创建客户端应用程序
步骤1 - 让我们为客户端应用程序创建一个新的基于MFC对话框的应用程序。
步骤2 - 在“高级功能”选项卡上,选中“Windows套接字”选项,如上所示。
步骤3 - 创建项目后,设计您的对话框,如下面的快照所示。
步骤4 - 为“连接”和“发送”按钮添加事件处理程序。
步骤5 - 为所有三个编辑控件添加值变量。对于端口编辑控件,选择变量类型UINT。
步骤6 - 添加用于连接和发送消息的MFC类。
步骤7 - 在头文件CMFCClientDemoApp类中包含CClientSocket类的头文件并添加类变量。同样,在CMFCClientDemoDlg头文件中也添加类变量。
CClientSocket m_clientSocket;
步骤8 - 这是“连接”按钮事件处理程序的实现。
void CMFCClientDemoDlg::OnBnClickedButtonConnect() { // TODO: Add your control notification handler code here UpdateData(TRUE); m_clientSocket.Create(); if (m_clientSocket.Connect(m_ipAddress, m_port)) { AfxMessageBox(L"Connection Successfull"); }else { AfxMessageBox(L"Connection Failed"); } DWORD error = GetLastError(); }
步骤9 - 这是“发送”按钮事件处理程序的实现。
void CMFCClientDemoDlg::OnBnClickedButtonSend() { // TODO: Add your control notification handler code here UpdateData(TRUE); if (m_clientSocket.Send(m_message.GetBuffer(m_message.GetLength()), m_message.GetLength())) { }else { AfxMessageBox(L"Failed to send message"); } }
步骤10 - 首先运行服务器应用程序,然后运行客户端应用程序。输入本地主机IP和端口,然后单击“连接”。
步骤11 - 您现在将在服务器端看到消息,如下面的快照所示。
MFC - GDI
Windows提供各种绘图工具供在设备环境中使用。它提供用于绘制线条的笔、用于填充内部的画刷和用于绘制文本的字体。MFC提供与Windows中的绘图工具等效的图形对象类。
绘图
设备上下文是 Windows 中的一种数据结构,包含有关设备(如显示器或打印机)的绘图属性的信息。所有绘图调用都是通过设备上下文对象进行的,该对象封装了 Windows 用于绘制线条、形状和文本的 API。
设备上下文允许在 Windows 中进行设备无关的绘图。设备上下文可用于绘制到屏幕、打印机或图元文件。
CDC 是 MFC 中进行绘图最基础的类。CDC 对象提供成员函数来执行基本的绘图步骤,以及用于处理与窗口客户区关联的显示上下文的成员。
序号 | 名称 & 描述 |
---|---|
1 | AbortDoc 终止当前打印作业,擦除应用程序自上次调用 StartDoc 成员函数以来写入设备的所有内容。 |
2 | AbortPath 关闭并丢弃设备上下文中的任何路径。 |
3 | AddMetaFileComment 将注释从缓冲区复制到指定的增强格式图元文件。 |
4 | AlphaBlend 显示具有透明或半透明像素的位图。 |
5 | AngleArc 绘制一条线段和一个弧线,并将当前位置移动到弧线的终点。 |
6 | Arc 绘制椭圆弧。 |
7 | ArcTo 绘制椭圆弧。此函数类似于 Arc,但会更新当前位置。 |
8 | Attach 将 Windows 设备上下文附加到此 CDC 对象。 |
9 | BeginPath 在设备上下文中打开路径括号。 |
10 | BitBlt 从指定的设备上下文复制位图。 |
11 | Chord 绘制弦(一个由椭圆和线段的交点限定的封闭图形)。 |
12 | CloseFigure 关闭路径中的开放图形。 |
13 | CreateCompatibleDC 创建一个与另一个设备上下文兼容的内存设备上下文。您可以使用它在内存中准备图像。 |
14 | CreateDC 为特定设备创建设备上下文。 |
15 | CreateIC 为特定设备创建信息上下文。这提供了一种快速获取有关设备信息的方法,而无需创建设备上下文。 |
16 | DeleteDC 删除与此 CDC 对象关联的 Windows 设备上下文。 |
17 | DeleteTempMap 由 CWinApp 空闲时间处理程序调用,以删除由 FromHandle 创建的任何临时 CDC 对象。还分离设备上下文。 |
18 | Detach 将 Windows 设备上下文从此 CDC 对象分离。 |
19 | DPtoHIMETRIC 将设备单位转换为 HIMETRIC 单位。 |
20 | DPtoLP 将设备单位转换为逻辑单位。 |
21 | Draw3dRect 绘制一个三维矩形。 |
22 | DrawDragRect 在矩形被拖动时擦除并重绘它。 |
23 | DrawEdge 绘制矩形的边框。 |
24 | DrawEscape 访问视频显示的绘图功能,这些功能无法通过图形设备接口 (GDI) 直接访问。 |
25 | DrawFocusRect 以用于指示焦点的样式绘制矩形。 |
26 | DrawFrameControl 绘制框架控件。 |
27 | DrawIcon 绘制图标。 |
28 | DrawState 显示图像并应用视觉效果以指示状态。 |
29 | DrawText 在指定的矩形中绘制格式化文本。 |
30 | DrawTextEx 使用其他格式在指定的矩形中绘制格式化文本。 |
31 | Ellipse 绘制椭圆。 |
32 | EndDoc 结束由 StartDoc 成员函数启动的打印作业。 |
33 | EndPage 通知设备驱动程序页面即将结束。 |
34 | EndPath 关闭路径括号并将括号定义的路径选入设备上下文。 |
35 | EnumObjects 枚举设备上下文中可用的笔和刷子。 |
36 | Escape 允许应用程序访问特定设备无法通过 GDI 直接访问的功能。还允许访问 Windows 转义函数。应用程序发出的转义调用会被翻译并发送到设备驱动程序。 |
37 | ExcludeClipRect 创建一个新的剪裁区域,该区域由现有剪裁区域减去指定的矩形组成。 |
38 | ExcludeUpdateRgn 通过从剪裁区域中排除窗口中更新的区域来防止在窗口的无效区域内绘制。 |
39 | ExtFloodFill 使用当前画刷填充区域。提供比 FloodFill 成员函数更多的灵活性。 |
40 | ExtTextOut 使用当前选定的字体在矩形区域内写入字符字符串。 |
41 | FillPath 关闭当前路径中的任何开放图形,并使用当前画刷和多边形填充模式填充路径的内部。 |
42 | FillRect 使用特定的画刷填充给定的矩形。 |
43 | FillRgn 使用指定的画刷填充特定区域。 |
44 | FillSolidRect 使用纯色填充矩形。 |
45 | FlattenPath 将路径中选定的任何曲线转换为当前设备上下文,并将每个曲线转换为一系列线。 |
46 | FloodFill 使用当前画刷填充区域。 |
47 | FrameRect 在矩形周围绘制边框。 |
48 | FrameRgn 使用画刷在特定区域周围绘制边框。 |
49 | FromHandle 在给定设备上下文句柄时返回指向 CDC 对象的指针。如果 CDC 对象未附加到句柄,则会创建一个临时 CDC 对象并将其附加。 |
50 | GetArcDirection 返回设备上下文的当前弧线方向。 |
51 | GetAspectRatioFilter 检索当前纵横比过滤器的设置。 |
52 | GetBkColor 检索当前背景颜色。 |
53 | GetBkMode 检索背景模式。 |
54 | GetBoundsRect 返回指定设备上下文的当前累积边界矩形。 |
55 | GetBrushOrg 检索当前画刷的原点。 |
56 | GetCharABCWidths 检索当前字体中给定范围内连续字符的宽度(以逻辑单位为单位)。 |
57 | GetCharABCWidthsI 检索当前 TrueType 字体中指定范围内连续字形索引的宽度(以逻辑单位为单位)。 |
58 | GetCharacterPlacement 检索有关字符字符串的各种类型的信息。 |
59 | GetCharWidth 检索当前字体中给定范围内连续字符的分数宽度。 |
60 | GetCharWidthI 检索当前字体中指定范围内连续字形索引的宽度(以逻辑坐标为单位)。 |
61 | GetClipBox 检索当前剪裁边界周围最紧密的边界矩形的尺寸。 |
62 | GetColorAdjustment 检索设备上下文的颜色调整值。 |
63 | GetCurrentBitmap 返回指向当前选定的 CBitmap 对象的指针。 |
64 | GetCurrentBrush 返回指向当前选定的 CBrush 对象的指针。 |
65 | GetCurrentFont 返回指向当前选定的 CFont 对象的指针。 |
66 | GetCurrentPalette 返回指向当前选定的 CPalette 对象的指针。 |
48 | GetCurrentPen 返回指向当前选定的 CPen 对象的指针。 |
67 | GetCurrentPosition 检索笔的当前位置(以逻辑坐标为单位)。 |
68 | GetDCBrushColor 检索当前画刷颜色。 |
69 | GetDCPenColor 检索当前笔颜色。 |
70 | GetDeviceCaps 检索有关给定显示设备的功能的特定于设备的信息。 |
71 | GetFontData 从可缩放字体文件检索字体度量信息。要检索的信息通过指定字体文件中的偏移量和要返回的信息的长度来标识。 |
72 | GetFontLanguageInfo 返回有关指定显示上下文当前选定字体的信息。 |
73 | GetGlyphOutline 检索当前字体中轮廓字符的轮廓曲线或位图。 |
74 | GetGraphicsMode 检索指定设备上下文的当前图形模式。 |
75 | GetHalftoneBrush 检索半色调画刷。 |
76 | GetKerningPairs 检索指定设备上下文中当前选定的字体的字符间距对。 |
77 | GetLayout 检索设备上下文 (DC) 的布局。布局可以是从左到右(默认)或从右到左(镜像)。 |
78 | GetMapMode 检索当前映射模式。 |
79 | GetMiterLimit 返回设备上下文的斜接限制。 |
80 | GetNearestColor 检索给定设备可以表示的与指定逻辑颜色最接近的逻辑颜色。 |
81 | GetOutlineTextMetrics 检索 TrueType 字体的字体度量信息。 |
82 | GetOutputCharWidth 使用输出设备上下文检索当前字体中连续字符组中各个字符的宽度。 |
83 | GetOutputTabbedTextExtent 计算输出设备上下文中字符字符串的宽度和高度。 |
84 | GetOutputTextExtent 使用当前字体确定尺寸,计算输出设备上下文上文本行的宽度和高度。 |
85 | GetOutputTextMetrics 从输出设备上下文检索当前字体的度量。 |
86 | GetPath 检索定义选入设备上下文中的路径中找到的线条端点和曲线的控制点的坐标。 |
87 | GetPixel 检索指定点处的像素的 RGB 颜色值。 |
88 | GetPolyFillMode 检索当前多边形填充模式。 |
89 | GetROP2 检索当前绘图模式。 |
90 | GetSafeHdc 返回 m_hDC,即输出设备上下文。 |
91 | GetStretchBltMode 检索当前位图拉伸模式。 |
92 | GetTabbedTextExtent 计算属性设备上下文中字符字符串的宽度和高度。 |
93 | GetTextAlign 检索文本对齐标志。 |
94 | GetTextCharacterExtra 检索字符间距量的当前设置。 |
95 | GetTextColor 检索当前文本颜色。 |
96 | GetTextExtent 使用当前字体确定尺寸,计算属性设备上下文上文本行的宽度和高度。 |
97 | GetTextExtentExPointI 检索指定字符串中适合指定空间的字符数,并用每个字符的文本范围填充数组。 |
98 | GetTextExtentPointI 检索指定字形索引数组的宽度和高度。 |
99 | GetTextFace 将当前字体的字体名称复制到缓冲区中,作为以 null 结尾的字符串。 |
100 | GetTextMetrics 从属性设备上下文检索当前字体的度量。 |
101 | GetViewportExt 检索视口的 x 和 y 范围。 |
102 | GetViewportOrg 检索视口原点的 x 和 y 坐标。 |
103 | GetWindow 返回与显示设备上下文关联的窗口。 |
104 | GetWindowExt 检索关联窗口的 x 和 y 范围。 |
105 | GetWindowOrg 获取关联窗口原点的 x 和 y 坐标。 |
106 | GetWorldTransform 检索当前世界空间到页面空间的转换。 |
107 | GradientFill 使用渐变颜色填充矩形和三角形结构。 |
108 | GrayString 在给定位置绘制灰显(变灰)文本。 |
109 | HIMETRICtoDP 将 HIMETRIC 单位转换为设备单位。 |
110 | HIMETRICtoLP 将 HIMETRIC 单位转换为逻辑单位。 |
111 | IntersectClipRect 通过形成当前区域和矩形的交集创建一个新的剪裁区域。 |
112 | InvertRect 反转矩形的内容。 |
113 | InvertRgn 反转区域中的颜色。 |
114 | IsPrinting 确定设备上下文是否用于打印。 |
115 | LineTo 从当前位置绘制一条线到(但不包括)一个点。 |
116 | LPtoDP 将逻辑单位转换为设备单位。 |
117 | LPtoHIMETRIC 将逻辑单位转换为 HIMETRIC 单位。 |
118 | MaskBlt 使用给定的掩码和光栅运算组合源和目标位图的颜色数据。 |
119 | ModifyWorldTransform 使用指定的模式更改设备上下文的 world 转换。 |
120 | MoveTo 移动当前位置。 |
121 | OffsetClipRgn 移动给定设备的剪裁区域。 |
122 | OffsetViewportOrg 相对于当前视口原点的坐标修改视口原点。 |
123 | OffsetWindowOrg 相对于当前窗口原点的坐标修改窗口原点。 |
124 | PaintRgn 使用选定的画刷填充区域。 |
125 | PatBlt 创建位模式。 |
126 | Pie 绘制一个扇形区域。 |
127 | PlayMetaFile 在给定的设备上播放指定元文件的內容。PlayMetaFile 的增强版本显示存储在给定增强格式元文件中的图片。元文件可以播放任意次数。 |
128 | PlgBlt 执行源设备上下文指定矩形中颜色数据的位块传输到给定设备上下文指定的平行四边形。 |
129 | PolyBezier 绘制一个或多个贝塞尔样条曲线。当前位置既不使用也不更新。 |
130 | PolyBezierTo 绘制一个或多个贝塞尔样条曲线,并将当前位置移动到最后一个贝塞尔样条曲线的结束点。 |
131 | PolyDraw 绘制一组线段和贝塞尔样条曲线。此函数更新当前位置。 |
132 | Polygon 绘制一个由两点或更多点(顶点)组成的多边形,这些点通过线连接。 |
133 | Polyline 绘制一组连接指定点的线段。 |
134 | PolylineTo 绘制一条或多条直线,并将当前位置移动到最后一条线的结束点。 |
135 | PolyPolygon 创建两个或多个多边形,这些多边形使用当前多边形填充模式填充。这些多边形可能是分离的,也可能重叠。 |
136 | PolyPolyline 绘制多个系列的连接线段。此函数既不使用也不更新当前位置。 |
137 | PtVisible 指定给定点是否在剪裁区域内。 |
138 | RealizePalette 将当前逻辑调色板中的调色板条目映射到系统调色板。 |
139 | Rectangle 使用当前笔绘制矩形,并使用当前画刷填充它。 |
140 | RectVisible 确定给定矩形的任何部分是否位于剪裁区域内。 |
141 | ReleaseAttribDC 释放m_hAttribDC(属性设备上下文)。 |
142 | ReleaseOutputDC 释放m_hDC(输出设备上下文)。 |
143 | ResetDC 更新 m_hAttribDC 设备上下文。 |
144 | RestoreDC 将设备上下文恢复到SaveDC保存的先前状态。 |
145 | RoundRect 使用当前笔绘制具有圆角的矩形,并使用当前画刷填充它。 |
146 | SaveDC 保存设备上下文的当前状态。 |
147 | ScaleViewportExt 相对于当前值修改视口范围。 |
148 | ScaleWindowExt 相对于当前值修改窗口范围。 |
149 | ScrollDC 水平和垂直滚动位矩形。 |
150 | SelectClipPath 选择当前路径作为设备上下文的剪裁区域,使用指定的模式将新区域与任何现有的剪裁区域组合。 |
151 | SelectClipRgn 使用指定的模式将给定区域与当前剪裁区域组合。 |
152 | SelectObject 选择 GDI 绘图对象(例如笔)。 |
153 | SelectPalette 选择逻辑调色板。 |
154 | SelectStockObject 选择 Windows 提供的预定义的库存笔、画刷或字体之一。 |
155 | SetAbortProc 设置程序员提供的回调函数,如果必须中止打印作业,Windows 会调用该函数。 |
156 | SetArcDirection 设置用于圆弧和矩形函数的绘制方向。 |
157 | SetAttribDC 设置 m_hAttribDC(属性设备上下文)。 |
158 | SetBkColor 设置当前背景颜色。 |
159 | SetBkMode 设置背景模式。 |
160 | SetBoundsRect 控制指定设备上下文的边界矩形信息的累积。 |
161 | SetBrushOrg 指定选择到设备上下文中的下一个画刷的原点。 |
162 | SetColorAdjustment 使用指定的值设置设备上下文的颜色调整值。 |
163 | SetDCBrushColor 设置当前画刷颜色。 |
164 | SetDCPenColor 设置当前笔颜色。 |
165 | SetGraphicsMode 设置指定设备上下文的当前图形模式。 |
166 | SetLayout 更改设备上下文 (DC) 的布局。 |
167 | SetMapMode 设置当前映射模式。 |
168 | SetMapperFlags 更改字体映射器在将逻辑字体映射到物理字体时使用的算法。 |
169 | SetMiterLimit 设置设备上下文的斜接连接长度限制。 |
170 | SetOutputDC 设置 m_hDC(输出设备上下文)。 |
171 | SetPixel 将指定点处的像素设置为指定颜色的最接近近似值。 |
172 | SetPixelV 将指定坐标处的像素设置为指定颜色的最接近近似值。SetPixelV 比 SetPixel 更快,因为它不需要返回实际绘制的点的颜色值。 |
173 | SetPolyFillMode 设置多边形填充模式。 |
175 | SetROP2 设置当前绘图模式。 |
176 | SetStretchBltMode 设置位图拉伸模式。 |
177 | SetTextAlign 设置文本对齐标志。 |
178 | SetTextCharacterExtra 设置字符间距量。 |
179 | SetTextColor 设置文本颜色。 |
180 | SetTextJustification 在字符串中的换行符字符中添加空格。 |
181 | SetViewportExt 设置视口的 x 和 y 范围。 |
182 | SetViewportOrg 设置视口原点。 |
183 | SetWindowExt 设置关联窗口的 x 和 y 范围。 |
184 | SetWindowOrg 设置设备上下文的窗口原点。 |
185 | SetWorldTransform 设置当前世界空间到页面空间的转换。 |
186 | StartDoc 通知设备驱动程序新的打印作业已开始。 |
187 | StartPage 通知设备驱动程序新页面已开始。 |
188 | StretchBlt 将位图从源矩形和设备移动到目标矩形,如有必要,拉伸或压缩位图以适应目标矩形的尺寸。 |
189 | StrokeAndFillPath 关闭路径中任何打开的图形,使用当前笔描绘路径的轮廓,并使用当前画刷填充其内部。 |
190 | StrokePath 使用当前笔呈现指定的路径。 |
191 | TabbedTextOut 在指定位置写入字符字符串,将制表符扩展到制表位数组中指定的值。 |
192 | TextOut 使用当前选定的字体在指定位置写入字符字符串。 |
193 | TransparentBlt 将颜色数据的位块从指定的源设备上下文传输到目标设备上下文,在传输中呈现指定的颜色透明。 |
194 | UpdateColors 通过逐像素地将客户端区域中的当前颜色与系统调色板匹配来更新设备上下文的客户端区域。 |
195 | WidenPath 将当前路径重新定义为如果使用当前选择到设备上下文中的笔描绘路径将绘制的区域。 |
Lines
步骤 1 - 让我们通过创建一个名为MFCGDIDemo的新基于 MFC 的单文档项目来了解一个简单的示例。
步骤 2 - 创建项目后,转到解决方案资源管理器,双击“源文件”文件夹下的MFCGDIDemoView.cpp文件。
步骤 3 - 在CMFCGDIDemoView::OnDraw()方法中绘制如下所示的线条。
void CMFCGDIDemoView::OnDraw(CDC* pDC) { pDC->MoveTo(95, 125); pDC->LineTo(230, 125); CMFCGDIDemoDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here }
步骤 4 - 运行此应用程序。您将看到以下输出。
步骤 5 - CDC::MoveTo()方法用于设置线条的起始位置。
使用 LineTo() 时,程序从 MoveTo() 点开始到 LineTo() 结束。
在 LineTo() 之后,如果您不调用 MoveTo(),并且再次使用其他点值调用 LineTo(),程序将从上一个 LineTo() 绘制一条线到新的 LineTo() 点。
步骤 6 - 要绘制不同的线条,您可以使用此属性,如下面的代码所示。
void CMFCGDIDemoView::OnDraw(CDC* pDC) { pDC->MoveTo(95, 125); pDC->LineTo(230, 125); pDC->LineTo(230, 225); pDC->LineTo(95, 325); CMFCGDIDemoDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here }
步骤 7 - 运行此应用程序。您将看到以下输出。
Polylines
折线是由一系列连接的线组成的。这些线存储在 POINT 或 CPoint 值的数组中。要绘制折线,可以使用 CDC::Polyline() 方法。要绘制折线,至少需要两点。如果您定义了两个以上的点,则第一条线之后的每一条线都将从前一点绘制到下一条点,直到包含所有点。
步骤 1 - 让我们了解一个简单的示例。
void CMFCGDIDemoView::OnDraw(CDC* pDC) { CPoint Pt[7]; Pt[0] = CPoint(20, 150); Pt[1] = CPoint(180, 150); Pt[2] = CPoint(180, 20); pDC−Polyline(Pt, 3); CMFCGDIDemoDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here }
步骤 2 - 运行此应用程序时,您将看到以下输出。
Rectangles
矩形是由四个组成四个直角的边组成的几何图形。与线条一样,要绘制矩形,必须定义它的起点和终点。要绘制矩形,可以使用 CDC::Rectangle() 方法。
步骤 1 - 让我们了解一个简单的示例。
void CMFCGDIDemoView::OnDraw(CDC* pDC) { pDC->Rectangle(15, 15, 250, 160); CMFCGDIDemoDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here }
步骤 2 - 运行此应用程序时,您将看到以下输出。
Squares
正方形是由四个组成四个直角的边组成的几何图形,但每条边的长度必须相等。
让我们来看一个简单的例子。
void CMFCGDIDemoView::OnDraw(CDC* pDC) { pDC->Rectangle(15, 15, 250, 250); CMFCGDIDemoDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here }
运行此应用程序时,您将看到以下输出。
Pies
扇形是椭圆的一部分或一段,这意味着扇形是一个不完整的椭圆。要绘制扇形,可以使用 CDC::Pie() 方法,如下所示 -
BOOL Pie(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);
(x1, y1) 点确定代表扇形的椭圆所在的矩形的左上角。(x2, y2) 点是矩形的右下角。
(x3, y3) 点以默认的逆时针方向指定扇形的起始角。
(x4, y4) 点指定扇形的结束点。
让我们来看一个简单的例子。
void CMFCGDIDemoView::OnDraw(CDC* pDC) { pDC->Pie(40, 20, 226, 144, 155, 32, 202, 115); CMFCGDIDemoDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here }
步骤 2 - 运行此应用程序时,您将看到以下输出。
Arcs
圆弧是椭圆的一部分或一段,这意味着圆弧是一个不完整的椭圆。要绘制圆弧,可以使用 CDC::Arc() 方法。
BOOL Arc(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);
CDC 类配备了 SetArcDirection() 方法。
以下是语法 -
int SetArcDirection(int nArcDirection)
序号 | 值 & 方向 |
---|---|
1 | AD_CLOCKWISE 图形按顺时针方向绘制 |
2 | AD_COUNTERCLOCKWISE 图形按逆时针方向绘制 |
步骤 1 - 让我们了解一个简单的示例。
void CMFCGDIDemoView::OnDraw(CDC* pDC) { pDC->SetArcDirection(AD_COUNTERCLOCKWISE); pDC->Arc(20, 20, 226, 144, 202, 115, 105, 32); CMFCGDIDemoDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here }
步骤 2 - 运行此应用程序时,您将看到以下输出。
Chords
到目前为止,我们绘制的圆弧被认为是开放图形,因为它们是由具有起点和终点的线组成的(与圆或矩形不同)。弦是一个两端用直线连接的圆弧。
要绘制弦,可以使用 CDC::Chord() 方法。
BOOL Chord(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);
让我们来看一个简单的例子。
void CMFCGDIDemoView::OnDraw(CDC* pDC) { pDC->SetArcDirection(AD_CLOCKWISE); pDC->Chord(20, 20, 226, 144, 202, 115, 105, 32); CMFCGDIDemoDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here }
运行上述应用程序时,您将看到以下输出。
此示例中的弧方向设置为顺时针方向。
颜色
颜色是增强对象美观外观的最基本对象之一。颜色是一个非空间对象,添加到对象中以修改其某些视觉方面。MFC 库与 Win32 API 结合使用,提供了各种操作,您可以利用这些操作来利用颜色的各个方面。
RGB 宏的行为类似于函数,并允许您传递三个用逗号分隔的数值。每个值必须在 0 到 255 之间,如下面的代码所示。
void CMFCGDIDemoView::OnDraw(CDC* pDC) { COLORREF color = RGB(239, 15, 225); }
让我们来看一个简单的例子。
void CMFCGDIDemoView::OnDraw(CDC* pDC) { COLORREF color = RGB(239, 15, 225); pDC->SetTextColor(color); pDC->TextOut(100, 80, L"MFC GDI Tutorial", 16); CMFCGDIDemoDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here }
运行此应用程序时,您将看到以下输出。
字体
CFont 封装了 Windows 图形设备接口 (GDI) 字体,并提供用于操作字体的成员函数。要使用 CFont 对象,请构造一个 CFont 对象并将 Windows 字体附加到它,然后使用对象的成员函数来操作字体。
让我们来看一个简单的例子。
void CMFCGDIDemoView::OnDraw(CDC* pDC) { CFont font; font.CreatePointFont(920, L"Garamond"); CFont *pFont = pDC->SelectObject(&font); COLORREF color = RGB(239, 15, 225); pDC->SetTextColor(color); pDC->TextOut(100, 80, L"MFC GDI Tutorial", 16); pDC->SelectObject(pFont); font.DeleteObject(); CMFCGDIDemoDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here }
运行上述应用程序时,您将看到以下输出。
画笔
画笔是一种用于在设备上下文中绘制线条和曲线的工具。在图形编程中,画笔也用于绘制几何闭合形状(如矩形或多边形)的边框。Microsoft Windows 考虑两种类型的画笔——装饰性和几何。
当画笔只能用于绘制固定宽度(小于或等于 1 像素)的简单线条时,称为装饰性画笔。当画笔可以采用不同的宽度和各种端点时,它就是几何画笔。MFC 提供了一个类CPen,它封装了 Windows 图形设备接口 (GDI) 画笔。
让我们来看一个简单的例子。
void CMFCGDIDemoView::OnDraw(CDC* pDC) { CPen pen; pen.CreatePen(PS_DASHDOTDOT, 1, RGB(160, 75, 90)); pDC->SelectObject(&pen); pDC->Rectangle(25, 35, 250, 125); CMFCGDIDemoDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here }
运行上述应用程序时,您将看到以下输出。
画刷
画刷是一种绘图工具,用于填充封闭的形状或线条的内部。画刷的行为就像拿起一桶油漆并将其倒在某个地方。MFC 提供了一个类CBrush,它封装了 Windows 图形设备接口 (GDI) 画刷。
让我们来看一个简单的例子。
void CMFCGDIDemoView::OnDraw(CDC* pDC) { CBrush brush(RGB(100, 150, 200)); CBrush *pBrush = pDC->SelectObject(&brush); pDC->Rectangle(25, 35, 250, 125); pDC->SelectObject(pBrush); CMFCGDIDemoDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here }
运行此应用程序时,您将看到以下输出。
MFC - 库
库是一组函数、类或其他资源,可以提供给需要已实现的实体的程序,而无需了解这些函数、类或资源是如何创建的或如何工作的。库使程序员可以轻松地使用其他人或公司创建的函数、类和资源等,并相信此外部来源是可靠且高效的。与库相关的一些独特功能包括:
库是创建的,并且像普通的常规程序一样工作,使用函数或其他资源并与其他程序通信。
为了实现其功能,库包含其他程序需要完成其功能的函数。
同时,库可能会使用某些其他程序不需要的函数。
使用库的程序也称为库的客户端。
您将在库中创建或包含两种类型的函数:
内部函数仅供库本身使用,库的客户端不需要访问这些函数。
外部函数是可以被库的客户端访问的函数。
在您的程序中,您将处理两种主要类型的库:
- 静态库
- 动态库
静态库
静态库是一个包含函数、类或资源的文件,外部程序可以使用这些文件来补充其功能。要使用库,程序员必须创建一个到它的链接。项目可以是控制台应用程序、Win32 应用程序或 MFC 应用程序。库文件具有 lib 扩展名。
步骤 1 - 让我们通过创建一个新的 Win32 项目来了解静态库的一个简单示例。
步骤 2 - 在应用程序向导对话框上,选择静态库选项。
步骤 3 − 单击“完成”继续。
步骤 4 - 右键单击解决方案资源管理器中的项目,并从添加→新建项…菜单选项中添加头文件。
步骤 5 - 在名称字段中输入 Calculator.h,然后单击添加。
在头文件中添加以下代码:
#pragma once #ifndef _CALCULATOR_H_ #define _CALCULATOR_H_ double Min(const double *Numbers, const int Count); double Max(const double *Numbers, const int Count); double Sum(const double *Numbers, const int Count); double Average(const double *Numbers, const int Count); long GreatestCommonDivisor(long Nbr1, long Nbr2); #endif // _CALCULATOR_H_
步骤 6 - 在项目中添加源 (*.cpp) 文件。
步骤 7 - 在名称字段中输入 Calculator.cpp,然后单击添加。
步骤 8 - 在 *.cpp 文件中添加以下代码:
#include "StdAfx.h" #include "Calculator.h" double Min(const double *Nbr, const int Total) { double Minimum = Nbr[0]; for (int i = 0; i < Total; i++) if (Minimum > Nbr[i]) Minimum = Nbr[i]; return Minimum; } double Max(const double *Nbr, const int Total) { double Maximum = Nbr[0]; for (int i = 0; i < Total; i++) if (Maximum < Nbr[i]) Maximum = Nbr[i]; return Maximum; } double Sum(const double *Nbr, const int Total) { double S = 0; for (int i = 0; i < Total; i++) S += Nbr[i]; return S; } double Average(const double *Nbr, const int Total) { double avg, S = 0; for (int i = 0; i < Total; i++) S += Nbr[i]; avg = S / Total; return avg; } long GreatestCommonDivisor(long Nbr1, long Nbr2) { while (true) { Nbr1 = Nbr1 % Nbr2; if (Nbr1 == 0) return Nbr2; Nbr2 = Nbr2 % Nbr1; if (Nbr2 == 0) return Nbr1; } }
步骤 9 - 从主菜单构建此库,方法是单击生成→生成 MFCLib。
步骤 10 - 库成功构建后,它将显示上述消息。
步骤 11 - 要从库中使用这些函数,让我们从文件→新建→项目中添加另一个基于 MFC 的对话框应用程序。
步骤 12 - 转到 MFCLib\Debug 文件夹,并将头文件和 *.lib 文件复制到 MFCLibTest 项目,如下面的快照所示。
步骤 13 - 要将库添加到当前项目,请在主菜单上单击项目→添加现有项,然后选择 MFCLib.lib。
步骤 14 - 设计您的对话框,如下面的快照所示。
步骤 15 - 为两个值为 double 类型的编辑控件添加值变量。
步骤 16 - 为位于对话框末尾的静态文本控件添加值变量。
步骤 17 - 为计算按钮添加事件处理程序。
要添加来自库的功能,我们需要在 CMFCLibTestDlg.cpp 文件中包含头文件。
#include "stdafx.h" #include "MFCLibTest.h" #include "MFCLibTestDlg.h" #include "afxdialogex.h" #include "Calculator.h"
步骤 18 - 以下是按钮事件处理程序的实现。
void CMFCLibTestDlg::OnBnClickedButtonCal() { // TODO: Add your control notification handler code here UpdateData(TRUE); CString strTemp; double numbers[2]; numbers[0] = m_Num1; numbers[1] = m_Num2; strTemp.Format(L"%.2f", Max(numbers,2)); m_strText.Append(L"Max is:\t" + strTemp); strTemp.Format(L"%.2f", Min(numbers, 2)); m_strText.Append(L"\nMin is:\t" + strTemp); strTemp.Format(L"%.2f", Sum(numbers, 2)); m_strText.Append(L"\nSum is:\t" + strTemp); strTemp.Format(L"%.2f", Average(numbers, 2)); m_strText.Append(L"\nAverage is:\t" + strTemp); strTemp.Format(L"%d", GreatestCommonDivisor(m_Num1, m_Num2)); m_strText.Append(L"\nGDC is:\t" + strTemp); UpdateData(FALSE); }
步骤 19 - 编译并执行上述代码后,您将看到以下输出。
步骤 20 - 在编辑字段中输入两个值,然后单击计算。现在,您将看到计算库结果后的结果。
动态库
Win32 DLL 是一个库,可以提供给在 Microsoft Windows 计算机上运行的程序。作为一个普通的库,它由一组函数和/或其他资源组成,这些函数和/或其他资源分组在一个文件中。
DLL 的缩写代表动态链接库。这意味着,与静态库相反,DLL 允许程序员决定何时以及如何将其他应用程序链接到此类库。
例如,DLL 允许不同的应用程序根据需要使用其库。实际上,在不同编程环境中创建的应用程序可以使用存储在一个特定 DLL 中的函数或资源。因此,应用程序动态链接到库。
步骤 1 - 让我们通过创建一个新的 Win32 项目来了解一个简单的示例。
步骤 2 - 在应用程序类型部分,单击 DLL 单选按钮。
步骤 3 − 单击“完成”继续。
步骤 4 - 在 MFCDynamicLib.cpp 文件中添加以下函数,并使用以下方法公开其定义:
extern "C" _declspec(dllexport)
步骤 5 - 为将在 DLL 外部访问的每个函数使用 _declspec(dllexport) 修饰符。
// MFCDynamicLib.cpp : Defines the exported functions for the DLL application.// #include "stdafx.h" extern "C" _declspec(dllexport) double Min(const double *Numbers, const int Count); extern "C" _declspec(dllexport) double Max(const double *Numbers, const int Count); extern "C" _declspec(dllexport) double Sum(const double *Numbers, const int Count); extern "C" _declspec(dllexport) double Average(const double *Numbers, const int Count); extern "C" _declspec(dllexport) long GreatestCommonDivisor(long Nbr1, long Nbr2); double Min(const double *Nbr, const int Total) { double Minimum = Nbr[0]; for (int i = 0; i < Total; i++) if (Minimum > Nbr[i]) Minimum = Nbr[i]; return Minimum; } double Max(const double *Nbr, const int Total) { double Maximum = Nbr[0]; for (int i = 0; i < Total; i++) if (Maximum < Nbr[i]) Maximum = Nbr[i]; return Maximum; } double Sum(const double *Nbr, const int Total) { double S = 0; for (int i = 0; i < Total; i++) S += Nbr[i]; return S; } double Average(const double *Nbr, const int Total){ double avg, S = 0; for (int i = 0; i < Total; i++) S += Nbr[i]; avg = S / Total; return avg; } long GreatestCommonDivisor(long Nbr1, long Nbr2) { while (true) { Nbr1 = Nbr1 % Nbr2; if (Nbr1 == 0) return Nbr2; Nbr2 = Nbr2 % Nbr1; if (Nbr2 == 0) return Nbr1; } }
步骤 6 - 要创建 DLL,请在主菜单上单击生成>生成 MFCDynamicLib。
步骤 7 - 成功创建 DLL 后,您将看到输出窗口中显示一条消息。
步骤 8 - 打开 Windows 资源管理器,然后打开当前项目的 Debug 文件夹。
步骤 9 - 请注意,已创建了一个具有 dll 扩展名的文件和另一个具有 lib 扩展名的文件。
步骤 10 − 要使用 dll 扩展名测试此文件,我们需要从“文件”→“新建”→“项目”创建一个新的基于 MFC 对话框的应用程序。
步骤 11 − 进入 MFCDynamicLib\Debug 文件夹,并将 *.dll 和 *.lib 文件复制到 MFCLibTest 项目中,如下面的快照所示。
步骤 12 − 要将 DLL 添加到当前项目中,请在主菜单上单击“项目”→“添加现有项”,然后选择 MFCDynamicLib.lib 文件。
步骤 13 − 设计您的对话框,如下面的快照所示。
步骤 14 − 为两个编辑控件添加值为 double 类型的变量。
步骤 15 − 为对话框末尾的静态文本控件添加变量。
步骤 16 − 为“计算”按钮添加事件处理程序。
步骤 17 − 在使用 DLL 的项目中,每个将被访问的函数都必须使用 _declspec(dllimport) 修饰符进行声明。
步骤 18 − 在 MFCLibTestDlg.cpp 文件中添加以下函数声明。
extern "C" _declspec(dllimport) double Min(const double *Numbers, const int Count); extern "C" _declspec(dllimport) double Max(const double *Numbers, const int Count); extern "C" _declspec(dllimport) double Sum(const double *Numbers, const int Count); extern "C" _declspec(dllimport) double Average(const double *Numbers, const int Count); extern "C" _declspec(dllimport) long GreatestCommonDivisor(long Nbr1, long Nbr2);
步骤 19 − 以下是按钮事件处理程序的实现。
void CMFCLibTestDlg::OnBnClickedButtonCal() { // TODO: Add your control notification handler code here UpdateData(TRUE); CString strTemp; double numbers[2]; numbers[0] = m_Num1; numbers[1] = m_Num2; strTemp.Format(L"%.2f", Max(numbers,2)); m_strText.Append(L"Max is:\t" + strTemp); strTemp.Format(L"%.2f", Min(numbers, 2)); m_strText.Append(L"\nMin is:\t" + strTemp); strTemp.Format(L"%.2f", Sum(numbers, 2)); m_strText.Append(L"\nSum is:\t" + strTemp); strTemp.Format(L"%.2f", Average(numbers, 2)); m_strText.Append(L"\nAverage is:\t" + strTemp); strTemp.Format(L"%d", GreatestCommonDivisor(m_Num1, m_Num2)); m_strText.Append(L"\nGDC is:\t" + strTemp); UpdateData(FALSE); }
步骤 20 − 当以上代码编译并执行后,您将看到以下输出。
步骤 21 − 在编辑字段中输入两个值,然后单击“计算”。您现在将看到从 DLL 计算后的结果。