简介:MFC,即微软基础类库,是一种C++类库,用于简化Windows程序开发并建立在Windows API之上。本文介绍了如何使用MFC创建单文档接口(SDI)应用程序,涵盖了从项目创建到文档管理、视图显示、消息处理和用户交互的基本概念。通过实操示例“MFC单文档学习程序1”,读者将学习MFC核心组件,包括应用程序框架、文档/视图架构、消息映射机制以及文件操作,为进一步开发Windows桌面应用打下基础。
1. MFC类库简介
简介
MFC(Microsoft Foundation Classes)是微软提供的一套用于Windows平台的C++类库,旨在简化Windows应用程序的开发。它提供了一组高级组件和控件,以及封装后的API,使得开发者可以方便地创建复杂的用户界面和应用程序逻辑。
MFC的构成
MFC由一系列类组成,这些类封装了Windows API,同时包括了应用程序框架、文档/视图架构、控件模板等。MFC提供了对事件驱动编程的支持,并且提供了一种快速实现常见用户界面需求的方法。
应用MFC的优势
使用MFC,开发者可以享受到高度封装带来的便利,比如无需直接处理大量的Windows消息,可以更加专注于业务逻辑的实现。此外,MFC的文档/视图模型为复杂数据的显示和编辑提供了有效的解决方案。
// 示例:简单的MFC程序入口
#include <afxwin.h>
class CMyApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
BOOL CMyApp::InitInstance()
{
m_pMainWnd = new CFrameWnd;
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
CMyApp theApp;
在上述代码中,展示了MFC程序的一个基本框架,包括应用程序类的定义和实例化。在此基础上,程序员可以进一步扩展功能,构建完整的Windows应用程序。
2. 单文档接口(SDI)的探索
2.1 单文档界面(SDI)的基本概念
2.1.1 SDI的特点与应用场景
SDI(Single Document Interface),即单文档界面,是一种用户界面架构,其特点是每次只能打开和编辑一个文档。这种设计模式适用于不依赖于其他文档数据,且用户的操作流程相对独立的应用程序,例如记事本、画图和简单的文本编辑器等。
SDI具有以下特点:
- 资源占用相对较少 :由于SDI应用程序结构较简单,相比MDI应用程序,其内存和系统资源占用要小。
- 操作单一 :用户界面通常只包含一个窗口,用户在同一时间只处理一份文档,降低了操作复杂度。
- 界面简洁 :界面设计相对简单,没有MDI那样的文档切换菜单和子窗口,适合界面定制和风格统一。
SDI在一些专业领域的应用程序中得到广泛运用,例如:
- 文本编辑器 :比如Notepad++、Sublime Text等都采用SDI设计,突出文本内容的编辑功能。
- 小型的图像处理软件 :如Photoshop早期版本的部分功能,GIMP中的某些插件。
- 轻量级的报告生成工具 :生成和展示报告的软件,用户关注点在于报告的阅读,而非多文档之间的比较。
2.1.2 SDI与多文档界面(MDI)的比较
SDI与MDI的主要区别在于它们管理文档的方式不同:
- MDI(Multiple Document Interface) 允许多个文档在同一应用程序窗口内打开,并在一个父窗口内创建多个子窗口。每个子窗口可以独立操作,适用于如文字处理软件或电子表格软件等需要同时处理多个文档的应用程序。
MDI和SDI各有利弊,应用场景也有所不同:
- MDI的优势 :
- 更适合同时处理多个文档的应用。
- 可以让用户在多个文档之间方便地切换。
-
支持并排或层叠查看多个文档,便于比较。
-
MDI的劣势 :
- 界面相对复杂,用户需要一定的学习曲线来熟悉。
- 管理多个子窗口可能会占用较多的系统资源。
-
窗口管理和布局可能会增加用户的操作难度。
-
SDI的优势 :
- 界面简洁,用户可以集中精力在一个文档上。
- 实现起来相对简单,适合快速开发小型应用程序。
-
资源占用较少,运行效率相对较高。
-
SDI的劣势 :
- 不适合需要同时查看或处理多个文档的应用程序。
- 功能扩展有限,对于复杂的操作流程可能需要额外的设计。
SDI和MDI的选择,应根据应用程序的使用场景、预期的用户体验以及系统资源的限制来决定。在进行应用程序设计时,开发者需要权衡不同架构带来的利弊,并作出最佳的选择。
2.2 SDI程序的结构组成
2.2.1 SDI的框架结构分析
SDI程序的基本框架结构可以分解为以下几个核心部分:
- 主窗口 :主窗口是用户与应用程序交互的主界面,其中会包含菜单栏、工具栏、状态栏等元素。
- 文档模板 :定义了文档的数据结构和相关的操作,以及如何创建和管理文档对象。
- 视图窗口 :视图窗口用于显示文档内容,并允许用户通过不同的方式来查看和编辑数据。
- 应用程序类 :如CWinApp的子类,负责处理应用程序级的操作,如初始化、消息处理等。
SDI程序的结构从总体上看,较为简单直接。主窗口负责维持界面和接收用户操作,文档模板负责管理文档数据,视图窗口负责展示文档内容,应用程序类负责协调整个程序的运行。
2.2.2 SDI程序的主要组件功能
在SDI程序中,每个组件都有其不可替代的作用,下面是各组件的主要功能:
- 主窗口(CFrameWnd) :
- 显示菜单栏、工具栏和状态栏。
- 通常会有一个视图窗口嵌入其中。
- 处理窗口移动、缩放等界面操作。
-
提供文件、编辑等基本操作。
-
文档类(CDocument) :
- 存储文档的数据。
- 实现文档数据的加载和保存。
-
提供数据变更的通知机制。
-
视图类(CView) :
- 实现文档数据的显示逻辑。
- 处理用户的输入操作,如鼠标点击、键盘输入等。
-
负责与用户直接交互,更新显示内容。
-
应用程序类(CWinApp) :
- 管理应用程序的启动和结束。
- 负责创建和初始化主窗口。
- 管理文档的创建与撤销。
- 处理应用程序级的消息,例如错误消息、警告消息等。
SDI程序的组件之间的关系和交互是程序能否正常运行的关键。开发者在设计和实现SDI程序时,需要充分理解各组件的功能和它们之间的协作关系,以构建出稳定、高效的应用程序。接下来的章节中,我们将对MFC中的这些类进行深入的探讨,以便更全面地理解SDI架构的设计与应用。
3. Visual Studio中的MFC应用程序向导详解
Visual Studio中的MFC应用程序向导(Application Wizard)是一个非常实用的工具,它帮助开发者快速生成标准的MFC应用程序代码框架,从而节省了大量手动编写代码的时间。向导不仅生成了基础的代码结构,还提供了丰富的配置选项以满足不同开发者的需求。接下来,我们将深入探讨MFC应用程序向导的工作原理和如何使用它创建基础的MFC应用程序。
3.1 MFC应用程序向导的工作原理
3.1.1 向导生成项目的流程
MFC应用程序向导在Visual Studio中通过一系列的向导页面引导用户选择项目设置。当启动向导并选择MFC应用程序类型后,向导会按照以下流程操作:
- 应用程序类型选择 :开发者首先需要选择要创建的MFC应用程序的类型,包括但不限于对话框为基础的应用程序、单文档界面(SDI)、多文档界面(MDI)等。
-
应用程序特性配置 :随后,向导会询问关于程序特性的选择,如是否支持Unicode、是否使用共享DLL等。这些配置将影响程序的最终构建和运行方式。
-
项目名称和位置设置 :开发者指定项目名称和保存位置,以及是否创建新的解决方案。
-
用户界面元素添加 :在随后的向导页面,可以为应用程序添加额外的用户界面元素,如工具栏、状态栏、视图以及对话框资源等。
-
附加功能和高级特性配置 :如果需要,向导还会提供为应用程序添加附加功能,如数据库支持、ActiveX控件、Web支持等高级配置选项。
-
完成与代码生成 :最后,通过点击“完成”,向导将根据前面的设置选项生成相应项目的源代码文件、资源文件以及项目文件,全部文件将被放置在指定的项目目录中。
3.1.2 向导对MFC项目的配置
MFC应用程序向导还提供了一套预设的配置,这些配置通常包括以下几个方面:
-
应用程序设置 :如应用程序的运行方式(窗口或对话框)、是否支持MDI、是否支持多线程等。
-
项目配置 :如项目的编译设置,包括预处理器定义、链接器设置等,这将影响最终的可执行文件生成。
-
资源文件配置 :向导会根据选择的应用程序类型自动生成默认的资源文件,例如菜单、工具栏、对话框资源等。
-
代码生成 :包括应用程序的主要类框架、消息映射宏、以及预定义的消息处理函数等。
3.2 使用向导创建基础MFC应用程序
使用MFC应用程序向导创建基础MFC应用程序非常直接。下面,我们将分析这一过程的细节,并对生成的代码进行详细解读。
3.2.1 向导中的选项设置与应用
在使用向导创建应用程序时,我们会遇到一系列的选项配置页面。每个页面都允许用户根据项目的实际需求进行个性化设置。以下是部分关键设置项:
-
应用程序的类型 :这是最重要的一步,因为它定义了应用程序的基本框架。对于初学者来说,通常会选择创建单文档应用程序。
-
用户界面特性 :在这里,开发者可以选择是否要包括工具栏、状态栏等用户界面元素。
-
附加特性 :这些特性可能包括数据库连接、Web功能等,具体选项取决于Visual Studio的版本和安装的组件。
3.2.2 向导生成的代码分析
向导生成的代码分为多个部分,每个部分都有其特定的作用。以下是生成的基础MFC应用程序代码的关键组成:
-
项目头文件 :包含了整个项目的配置信息,如应用程序的入口点(WinMain函数)。
-
应用程序类 :这是MFC应用程序的核心,它继承自CWinApp类。向导生成的代码包括了应用程序的初始化和清理方法。
-
文档和视图类 :基于SDI应用程序,向导会生成一个文档类和一个视图类。文档类负责数据的保存与管理,视图类则负责数据显示。
-
消息映射宏 :这些宏用于将特定的Windows消息映射到相应的消息处理函数,这是MFC框架的基础。
以下是向导生成的部分代码示例,展示了一个基础的SDI应用程序的入口和应用程序类的声明:
// MySDIApp.cpp : 实现应用程序的类
#include "stdafx.h"
#include "MySDI.h"
#include "MySDIDoc.h"
#include "MySDIView.h"
CMySDIApp theApp;
// CWinApp派生类的实现
BEGIN_MESSAGE_MAP(CMySDIApp, CWinApp)
//{{AFX_MSG_MAP(CMySDIApp)
// TODO: 在此添加消息映射驱动程序代码
ON_COMMAND(ID_APP_ABOUT, &CWinApp::OnAppAbout)
ON_COMMAND(ID_FILE_NEW, &CWinApp::OnFileNew)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// CWinApp派生类的实现
BOOL CMySDIApp::InitInstance()
{
// TODO: 在此添加额外的代码
m_pMainWnd = new CMySDIView();
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
// 程序入口点
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
// TODO: 在此添加代码
return theApp.MainLoop();
}
在上述代码中, CMySDIApp
是应用程序类,它通过消息映射宏定义了应用程序的入口点和消息处理函数。 InitInstance
方法初始化应用程序实例,并创建主窗口。 _tWinMain
是程序的实际入口函数,它调用了 CWinApp
类的 MainLoop
方法来运行消息循环。
通过以上介绍,我们可以看到,使用MFC应用程序向导不仅可以快速构建MFC应用程序,还可以通过向导提供的选项和自动生成的代码,来简化开发过程并加快开发速度。随着对向导使用熟练程度的提高,开发者可以进一步探索向导的高级配置,为创建更复杂的MFC应用程序打下坚实的基础。
4. MFC基础类的实现与应用
4.1 CWinApp类的角色与扩展
4.1.1 CWinApp类的核心功能
CWinApp是MFC中的应用程序类,它是整个MFC程序的根。一个CWinApp对象代表了MFC应用程序的实例。CWinApp类的核心功能涵盖了应用程序生命周期的管理、文档模板的注册、消息循环的运行等。
首先,CWinApp负责初始化MFC应用程序,它在应用程序启动时被创建,并在其析构函数中进行清理工作。在初始化过程中,CWinApp对象读取注册表信息,初始化各种MFC模块和服务。此外,CWinApp还负责管理应用程序的消息循环,使得MFC应用程序能够响应Windows消息。
其次,CWinApp类使得文档-视图架构成为可能。文档模板的注册使得应用程序能够知道如何创建、管理文档和视图对象,以及它们之间的关联。MFC应用程序通常通过一个CWinApp派生类来实现特定的应用程序设置。
4.1.2 CWinApp的子类化与应用
在实际开发中,我们几乎总是会创建一个CWinApp的派生类,并用它来定义应用程序的特定行为。通过子类化CWinApp,开发者可以重写其虚函数,如InitInstance和ExitInstance,从而在应用程序启动和退出时执行自定义的初始化和清理工作。
例如,以下代码展示了如何创建一个名为 CMyApp
的CWinApp派生类,并重写InitInstance函数:
class CMyApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
BOOL CMyApp::InitInstance()
{
// 应用程序启动时的初始化代码
// ...
// 创建和初始化主窗口
m_pMainWnd = new CMyMainWnd;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
// 返回TRUE表示初始化成功
return TRUE;
}
在上面的代码中, m_pMainWnd
是CWinApp类中的一个成员变量,它指向应用程序的主窗口。在InitInstance中创建一个主窗口对象,并通过调用其方法来显示窗口,是应用程序启动时的标准步骤。
CWinApp类的子类化为开发者提供了一个机会,让他们可以添加特定于应用程序的代码,同时保留MFC应用程序的结构和行为。通过这样的扩展,开发者能够实现应用程序的自定义行为和特性。
4.2 CDocument和CView类的关联与实现
4.2.1 文档-视图架构概述
文档-视图架构是MFC设计的核心概念之一,它旨在分离数据(文档)和表示(视图)。这种分离允许同时从不同的视图查看同一文档数据,或者实现对同一数据的不同类型的表示。
文档对象负责存储和管理应用程序的数据,而视图对象则负责显示这些数据。在MFC中,文档对象派生自CDocument类,而视图对象派生自CView类。当用户与应用程序交互时,视图对象接收消息并可能请求文档对象更新其数据,反之文档对象的数据更改可能触发视图对象更新其显示。
文档-视图架构为应用程序提供了一种灵活的方式来组织数据和用户界面,使得应用程序能够更容易地进行扩展和维护。
4.2.2 文档类和视图类的协同工作
在文档-视图架构中,文档类和视图类之间的协同工作是通过消息映射实现的。视图类向文档对象请求数据,文档对象响应请求并提供数据。当文档数据发生变化时,它会通知所有连接的视图对象,视图对象随后更新它们的显示以反映这些变化。
这种架构的实现涉及几个关键组件:文档模板、文档对象、视图对象以及框架窗口。文档模板负责管理文档和视图对象之间的关系,确保应用程序能够创建和关联正确类型的对象。
以下是一个简单的例子,展示了CDocument派生类的定义以及如何在视图对象中使用它:
class CMyDocument : public CDocument
{
// 文档类的实现
// ...
};
class CMyView : public CView
{
public:
// 视图类的实现
// ...
// 假设有一个方法来请求文档数据
void RequestData()
{
// 从文档对象获取数据的逻辑
// ...
}
};
在这个例子中, CMyDocument
类负责存储特定的数据,而 CMyView
类负责显示这些数据。当需要显示数据时, CMyView
对象会调用 RequestData
方法来从文档对象中获取数据。MFC的文档-视图框架确保当数据更新时,所有相关视图都会得到通知,从而能够重新绘制它们的内容。
文档类和视图类的协同工作是通过MFC的消息映射机制实现的,这使得程序员能够专注于实现具体的业务逻辑,而不必担心底层的通信细节。这大大简化了复杂应用程序的开发,允许开发者更加高效地构建功能丰富的应用程序。
5. 深入理解MFC单文档的高级特性
5.1 消息映射机制的深度解析
5.1.1 消息映射的工作原理
在MFC(Microsoft Foundation Classes)单文档界面(SDI)应用程序中,消息映射机制扮演着核心的角色。它是如何工作的呢?当一个事件或消息(如鼠标点击、按键等)发生时,Windows操作系统会生成一个消息,并将其放入消息队列中。随后,MFC框架将从消息队列中取出消息,并利用消息映射机制将这个消息与处理函数进行关联和映射。
消息映射实际上是一种函数指针的映射表,这个映射表将消息标识符(通常是Windows消息ID)与特定的消息处理函数关联起来。消息处理函数通常以 ON_COMMAND
, ON_UPDATE_COMMAND_UI
, ON_MESSAGE
等宏定义的形式存在,在MFC中进行消息分派处理。
让我们看一个简单的例子:
BEGIN_MESSAGE_MAP(CMyView, CView)
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
在这段代码中, BEGIN_MESSAGE_MAP
和 END_MESSAGE_MAP
宏之间定义了消息映射表, ON_WM_PAINT
和 ON_WM_LBUTTONDOWN
宏分别将窗口重绘消息和鼠标左键点击消息映射到相应处理函数。
5.1.2 消息映射与回调函数
回调函数在MFC中广泛使用,它们是消息映射机制的核心部分。在MFC中,回调函数是应用程序为了响应消息而提供给操作系统的函数。MFC框架在内部处理消息之后,会调用相应的回调函数来进行实际的消息处理。
例如,在文档视图架构中,当一个视图需要重绘时,框架会发送 WM_PAINT
消息,而实际的绘制工作将由视图类中的回调函数 OnPaint
来完成。在MFC中,大多数的消息映射宏(如 ON_WM_PAINT
)最终都会关联到某个回调函数。
void CMyView::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
// 不要调用 CView::OnPaint() 对于绘制消息,因为已经由框架处理
}
5.2 文件操作与用户交互的高级实践
5.2.1 文件操作的处理函数详解
在开发MFC应用程序时,处理文件通常是必不可少的功能之一。在单文档界面中,文件操作常与视图类相关联。MFC提供了一套预定义的消息映射宏,用于处理文件相关的操作,例如打开文件、保存文件等。
-
ON_COMMAND(ID_FILE_NEW, &CYourView::OnFileNew)
:创建新文档。 -
ON_COMMAND(ID_FILE_OPEN, &CYourView::OnFileOpen)
:打开一个已存在的文件。 -
ON_COMMAND(ID_FILE_SAVE, &CYourView::OnFileSave)
:保存当前文档到文件。 -
ON_COMMAND(ID_FILE_SAVE_AS, &CYourView::OnFileSaveAs)
:另存为新文件。
这里, ID_FILE_NEW
、 ID_FILE_OPEN
等是预定义的命令ID, CYourView::OnFileNew
是对应的处理函数。这些函数通常会在视图类中重写,以实现相应的文件操作逻辑。
下面是一个典型的打开文件操作的实现:
void CMyView::OnFileOpen()
{
CFileDialog fileDlg(TRUE);
if(fileDlg.DoModal() == IDOK)
{
// 文件操作逻辑
// 例如:加载文件到文档对象
LoadDocument(fileDlg.GetPathName());
}
}
5.2.2 用户交互和事件驱动编程的案例分析
用户交互是应用程序与用户之间沟通的桥梁。在MFC应用程序中,用户交互常常通过事件驱动的方式进行处理。事件可以是来自键盘或鼠标的输入,也可以是来自系统或应用程序的消息。
以一个典型的文本编辑器为例,用户可能会使用菜单栏中的保存文件选项。当用户点击“文件->保存”时,应用程序会显示一个对话框,让用户选择保存位置和文件名。这个过程中,用户与应用程序之间通过点击、选择等动作产生了多个事件,应用程序根据事件的类型调用相应的事件处理函数。
在事件驱动编程中,程序员需要理解每个事件的含义,并编写相应的处理代码。这个过程可能涉及到对UI控件的操作,对文件的读写,以及对应用程序状态的更新等等。
以下是用户进行文件操作时,事件处理的一个流程案例:
graph LR
A[开始] --> B[用户点击菜单“文件->保存”]
B --> C{是否选择保存位置}
C -- 是 --> D[显示保存对话框]
D --> E{是否选择文件名并确认}
E -- 是 --> F[保存文件并更新界面]
E -- 否 --> C
C -- 否 --> G[结束操作]
F --> G
在这个流程中,用户的每一个选择都触发了一个事件,并且程序通过事件处理函数响应了这些事件。MFC通过消息映射和回调函数机制,使得开发人员可以更专注于业务逻辑的处理,而不是底层的消息处理细节。
简介:MFC,即微软基础类库,是一种C++类库,用于简化Windows程序开发并建立在Windows API之上。本文介绍了如何使用MFC创建单文档接口(SDI)应用程序,涵盖了从项目创建到文档管理、视图显示、消息处理和用户交互的基本概念。通过实操示例“MFC单文档学习程序1”,读者将学习MFC核心组件,包括应用程序框架、文档/视图架构、消息映射机制以及文件操作,为进一步开发Windows桌面应用打下基础。