Windows界面编程:MFC

article/2025/9/7 17:41:17

前言

大家之前在学C/C++语言时,基本都是通过控制台实现“人机交流”的。但大家每次在写控制台程序时,看到黑框框应该都会有些许不爽吧:“输入输出为什么不能基于图形用户界面而非要使用文本用户界面呢?”事实上,在各个环境下均可用C/C++实现图形用户界面。下文的MFC技术,就是在Windows环境下实现对图形用户界面的编程的。

MFC即微软基础类库(Microsoft Foundation Classes),是微软公司以C++的形式对Windows API进行封装,从而使开发者较为方便的对Windows进行图形用户界面(GUI)开发。在下文中笔者将和大家聊聊MFC的发展和原理以及简单的应用——计算机绘图。

从C语言开始!

MFC最初的思想是面向过程的,即通过C语言和Windows API函数,实现对图形用户界面的开发。Windows API 函数主要包括三大类型:窗口管理函数(实现窗口的创建、移动和修改等功能)、图形设备函数(实现图形的绘制及操作功能)和系统服务函数(实现与操作系统有关的一些功能)。API函数所操作的基本数据类型是句柄——一种复杂的程序对象和实例(如滚轮、按钮、滚动条等)的标识,类似于用于标识整形变量的int。常见的句柄类型如下:

句柄类型

说明

句柄类型

说明

HWND

窗口句柄

HDC

图形设备环境句柄

HINSTANCE

当前程序应用实例句柄

HBITMAP

位图句柄

HCURSOR

光标句柄

HICON

图标句柄

HFONT

字体句柄

HMENU

菜单句柄

HPEN

画笔句柄

HFELE

文件句柄

HBRUSH

画刷句柄

 

 

接下来介绍“事件”和“消息”的概念。

大家用的操作系统基本都是Windows,Windows的一个特点是在它会弹出一个个窗口,而每个窗口弹出后会进入一个等待状态(while循环),直到接收到了某种刺激(如鼠标点击、键盘输入等)后,系统和程序才会脱离等待状态并对这个刺激进行处理。我们把这个可能触发计算机程序做出相应反应的刺激称作“事件”。

为了描述“事件”的各种信息(即何时、何地发生了何种事件),Windows定义了一个结构,即“消息”。

部分常见消息的标识

说明

WM_LBUTTONDOWN

按下鼠标左键时产生的消息

WM_LBUTTONBLICK

双击鼠标左键时产生的消息

WM_CLOSE

关闭窗口时产生的消息

WM_DESTROY

消除窗口时产生的消息

WM_PAINT

需要窗口重画时产生的消息

当产生某种消息后,Windows把这种消息送入消息队列中等待使用,此时应用程序调用API函数从消息队列中不断的获取消息并发送给系统,从而构成了“消息循环”。消息循环的代码如下:

此后,系统根据消息中的标识message调用相应的窗口函数处理消息。待处理结束后,返回消息循环等待获取下一个消息直到应用程序结束。

通过上面的分析,大家可以发现应用程序的关键在于消息循环的建立。事实上,建立消息循环和创建应用程序窗口(可以理解为程序提供“舞台”)正是主函数的两个任务。在Windows应用程序中,主函数写作WinMain而不是main。

将上述的API函数、句柄、消息循环、创建的窗口和主函数相结合,就可以实现图形用户界面的设计了。

进化——函数封装、消息映射表和宏定义

上一部分为大家分析和介绍了早期MFC的实现过程,即通过调用API函数来创建窗口和消息循环,之后再利用窗口函数对消息进行处理。这一过程很容易理解,但实际上在写代码时由于要做的任务很多(如完成依次对各个窗口的类型进行初始化和注册、依次初始化窗口函数等),很容易把代码写的很乱且不容易理解,此时可以利用C语言模块化的特点将程序的各个部分用函数封装好,及分别使用不同的函数完成上述的任务,从而使得整个程序由主函数和多个窗口函数组成。

例外,窗口函数(用于处理消息队列中的消息)通常使用了switch语句(因为要根据消息的不同而进行不同的处理),我们可以将各个消息处理程序段(即switch语句中各个case后面的部分)封装成函数,并用函数指针去分别指向这些函数。这种有消息标识和函数指针之间的关系被称作消息映射表,常用数组或链表实现。

某个消息映射表

WM_LBUTTONDOWN

On_LButtonDown

WM_PAINT

On_Paint

WM_RESTROY

On_Restroy

大家肯定有疑惑:“好好的函数不是挺好嘛,为啥要多此一举呢?”就笔者个人的理解,使用函数封装表一是为了使程序更加模块化、更便于修改和理解,二是随着窗口函数的复杂化,比如有多个子类继承了父类,而每个子类又有各自的switch语句(如CcmdTarget类),在这种情况下,建立一个多维的消息映射表就更便于理解和维护程序了。

更进一步,我们还可以把消息映射表的各项定义为宏使代码更规整漂亮。

至此,我们已经把用C语言建立的最初的“MFC”程序通过函数封装、消息映射表和宏定义的方法很好的封装起来了——而此时我们的程序已经很舒服、很接近MFC程序了。

面向对象化

我们在上一部分末尾得到的MFC仍然是面向过程的,虽然它已经封装的很好了,但在设计开发图形用户界面时仍十分困难——代码的重复量太多了。在此基础上,结合面向对象的思想,便可大大简化开发难度得到现在的MFC。

具体来讲,我们可以把主函数(用于创建并显示窗体和实现消息循环)看作是一个对象——应用程序类对象,窗体则是嵌入应用程序对象的另一个对象。在设计具体程序时,其他类派生于窗口类或应用程序类(派生时会用到虚函数和在类中声明消息映射表)。

 

至此,早期的MFC程序——窗口类+应用程序类便实现了。

MFC的应用实例

上面的三个部分主要介绍了MFC的思想和发展,但现在我们要写一个MFC程序远没有这么麻烦。借助VS中的MFC应用程序向导、类视图、资源试图我们可以相对容易的设计MFC程序。接下来笔者将具体介绍一个用MFC进行绘图的例子。

Windows环境下依靠GDI(图形用户接口)和DC(设备描述环境)对图形进行支持,两者被封装到一起形成CDC类。调用CDC类的派生类的部分函数并结合一些数学知识便可以绘出很棒的图形。

CDC类部分函数

说明

Arc()

画圆弧

Ellipse()

画椭圆

Lineto()

把指定画笔画直线到参数指定的位置

Textout()

绘制字符串

CDC类部分派生类

说明

CClientDC

窗口客户区的设备描述环境,在WM_PAINT消息之外的消息处理函数中

CMetaFileDC

图元文件的设备描述环境,在创建可以放回的图像时使用

CpaintDC

窗口用户区的设备描述环境,在OnDraw()函数中处理WM_PAINT消息

CwindowDC

在整个窗口内(不只是用户区)绘图的设备描述环境

下面介绍一个具体的例子。在VS中新建工程,选择VC++中的MFC,打开MFC应用程序,依次选择“下一步”“单个文档”“MFC标准”“完成”。

 

 

在“解决方案管理器”中找到myMFCView.cpp,并修改其中的OnDraw函数,添加如下代码:

执行程序结果如下:

结语

随着技术的不断发展,基于对话框的图形用户程序多使用C#编程,绘图则更多地使用opencv、opengl等,但MFC中所蕴涵的早期的图形化界面思想和处理问题的方法仍值得我们学习(笔者觉得“函数映射表”的处理方法对于模块化和封装程序很实用)。由于笔者和大家一样都是初学者,上文中对MFC的阐释难免会有一些不太恰当的地方,望大家见谅。

更多关于MFC的内容,大家可以参考任哲的《MFC Windows应用程序设计》、孙鑫的《VC++深入详解》以及CSDN博客(笔者参考的博客网址:https://blog.csdn.net/Eastmount/article/details/53180524)。



http://chatgpt.dhexx.cn/article/sVoYoonv.shtml

相关文章

MFC-登录界面(可修改密码)

登录界面的博客有很多,参考了这一篇文章,下面给出链接。 MFC做一个登录界面(用户名,密码以及密码的明文密文切换)_umarutyan的博客-CSDN博客 但这里无法修改密码,下面做出一些补充。 由于用不到数据库&a…

MFC界面工具BCGControlBar入门:如何使用工具栏编辑器

BCGControlBar ("Business Components Gallery ControlBar")是MFC扩展库,使您可以创建具有完全自定义选项(功能区、可自定义工具栏、菜单等)以及一组专业设计的丰富Microsoft Office和Microsoft Visual Studio的应用程序 GUI控件&a…

MFC程序中使用QT开发界面

如果你有一个现成的MFC项目在做维护,但是你厌倦了使用MFC繁琐的操作来做界面美化,或者你需要在这个项目中用到QT里面好用的某些功能;亦或者是你需要使用某些只能在MFC中使用的组件,但是界面这部分已经用QT做好了。那么这篇文章可能…

一、MFC登陆界面

一、新建项目 打开VS2022——>创建新项目——>选择MFC应用,点击下一步——>修改项目名称和位置,点击创建——>修改应用程序类型、项目样式经典菜单选项 二、添加预处理指令 VS建议采用带_s的函数,如scanf_s、strcpy_s&#xff0…

MFC基础知识与课程设计思路

引言 本文致力于提供MFC的相关知识,以方便大家更好地认识MFC的使用方法。介绍将会分为以下几个部分:MFC初始文件的理解、MFC我们所使用的框架理解、MFC的进阶用法、MFC我在使用过程中遇到的问题及解决方法。 MFC初始文件的理解 MFC的初始文件中有两项文…

MFC添加程序关闭时时的提示界面

文章目录 MFC一.简介二.方法 MFC 一.简介 在应用程序退出的时候,不能点击叉直接退出,我们想添加一个退出提示。在点击叉后,弹出是否确定退出的界面,如下面的界面,具体操作往下看 二.方法 找到我们需要操作的主界面…

【MFC】模拟采集系统——界面设计(17)

功能介绍 启动界面 开始采集: PS:不涉及 数据保存,重现等功能 界面设计 界面分为三块:顶部黑条带关闭按钮、左边对话框,右边的主界面 资源: 顶部黑条 top.bmp 2* 29 (宽 * 高 像素点&…

MFC界面设计入门篇

点击C里的MFC再点击MFCApplication,到下面改名字和路径,然后OK 然后点击Next, 选择single document,MFCstandard,简体中文,然后Finish 这时候可以先直接运行,看看工程的样子&#x…

最最简单的几个Mac终端命令

几个简单的Mac终端命令 目录切换相关 cd空格/ 回到根目录cd空格… (或者 cd空格…/) 回到上一级目录cd空格. 回到当前目录pwd 显示从根目录到当前目录的完整目录 vi操作相关 注意:vi操作的文件如果不存在,则先自动创建一个该名字…

Mac 终端基本命令

基本命令 1、列出文件 ls 参数 目录名 例: 看看驱动目录下有什么:ls /System/Library/Extensions 参数 -w 显示中文,-l 详细信息, -a 包括隐藏文件 2、转换目录 cd 例:想到驱动目录下溜达一圈 cd /System/Library/Extension…

10需要知道Mac终端命令

如果你想进入web开发领域,知道什么是终端,如何使用终端是非常有益的。在今天的文章中,我们将介绍Mac终端命令的10个必要知识! 🙂 什么是终端(Terminal) 终端最基本的用途是可以浏览计算机的文…

Mac终端命令

Mac电脑安装程序,打开允许任何来源的方法,在终端执行命令行即可。 打开命令:sudo spctl --master-disable 关闭命令:sudo spctl --master-enable Mac终端打开文件 1.打开文件夹的命令很简单,使用 open 文件夹…

Mac终端命令失效( command not found)/

ls vi vim 输入完来一句command not found xx 心哇凉哇凉的,心态都崩了。 guolianggldeMacBook-Pro ~ % cat zsh: command not found: cat guolianggldeMacBook-Pro ~ % ls zsh: command not found: ls guolianggldeMacBook-Pro ~ % mdfind zsh: command not fo…

MacOS系统终端常用命令大全

MacOS系统终端是使用mac电脑的小伙伴需要或多或少了解的一个应用。熟悉并掌握一些基本的常用命令可以帮助我们快速的解决一些日常问题,提高工作效率,下面让小编带大家了解一下吧! 基本概念 命令的构成:Command Name、Options、Ar…

Mac终端 常用命令

OSX 的文件系统 OSX 采用的Unix文件系统,所有文件都挂在跟目录 / 下面,所以不在要有Windows 下的盘符概念。 你在桌面上看到的硬盘都挂在 /Volumes 下。 比如接上个叫做 USBHD的移动硬盘,桌面上会显示出一个硬盘图标,它实际在哪…

mac 终端 常用命令

文件目录 首先要清楚几个文件目录: " / " :根目录 " ~ " :用户主目录的缩写。例如当前用户为hello,那么" ~ "展开来就是:/Users/hello " . " :当前目录 "…

RANSAC原理及直线拟合(python动态图解)

一、简介 随机采样一致性(Random Sample Consensus,RANSAC)由斯坦福国际研究院的Fischler和Bolles于1981年首次提出[1]。RANSAC算法是一种随机参数估计迭代算法;从一组包含异常数据的样本数据集中,通过迭代的方式&…

RANSAC初识

RANSAC算法:随机抽样一致算法(random sample consensus,RANSAC) 一个简单的例子是从一组观测数据中找出合适的二维直线。假设观测数据中包含局内点和局外点,其中局内点近似的被直线所通过,而局外点远离于直线。简单的…

RANSAC算法详解

RANSAC算法详解 给定两个点p1与p2的坐标,确定这两点所构成的直线,要求对于输入的任意点p3,都可以判断它是否在该直线上。初中解析几何知识告诉我们,判断一个点在直线上,只需其与直线上任意两点点斜率都相同即可。实际…

Ransac拟合椭圆

一、Ransac算法介绍 RANSAC(RAndom SAmple Consensus,随机采样一致)最早是由Fischler和Bolles在SRI上提出用来解决LDP(Location Determination Proble)问题的,该算法是从一组含有“外点”(outliers)的数据中正确估计数学模型参数的迭代算法。“外点”一般指的的数据…