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

article/2025/9/7 17:45:16

引言

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

MFC初始文件的理解

        MFC的初始文件中有两项文件是我们经常需要使用的,其余文件可以暂时忽略,我们基本不会用上。一个是MFCApplicationView,我们简称View类,一个是MFCApplicationDoc,我们简称Doc类。

        Doc类是Document的缩写,是用来处理程序与计算机文件的关系的,我们读取、保存文件就是通过这个类实现的,我们如果想要获取读取的图像信息,也都是通过Doc类进行读取的。例如以下这个我们常用的代码段。

	CMFCApplication1Doc* pDoc = GetDocument();// 获取文档		//long	lSrcLineBytes;		//图象每行的字节数long	lSrcWidth;      //图象的宽度和高度long	lSrcHeight;int     lpSrcBitCount;       //图像的位深LPSTR	lpSrcDib;		//指向源图象的指针	LPSTR	lpSrcStartBits;	//指向源像素的指针lpSrcDib = (LPSTR) ::GlobalLock((HGLOBAL)pDoc->GetHObject());// 锁定DIBif (!lpSrcDib) return;/*if (pDoc->m_dib.GetColorNum(lpSrcDib) != 256)// 判断是否是8-bpp位图{AfxMessageBox(L"对不起,不是256色位图!");// 警告::GlobalUnlock((HGLOBAL) pDoc->GetHObject());// 解除锁定return;									//返回}										//判断是否是8-bpp位图,不是则返回*/lpSrcStartBits = pDoc->m_dib.GetBits(lpSrcDib);			// 找到DIB图象像素起始位置	lSrcWidth = pDoc->m_dib.GetWidth(lpSrcDib);					// 获取图象的宽度		lSrcHeight = pDoc->m_dib.GetHeight(lpSrcDib);					// 获取图象的高度	lpSrcBitCount = pDoc->m_dib.GetBitCount(lpSrcDib);                    //获取图像位深lSrcLineBytes = pDoc->m_dib.GetReqByteWidth(lSrcWidth * lpSrcBitCount);		// 计算图象每行的字节数
///

        其中GetDocument()函数是View类的函数,其功能就是提取先前Doc类已经读取好的内容(通过指针直接读取Doc类的内存)而此后的pDoc->m_dib是CDib类,CDib的功能是存储图像信息。

        为什么信息存放在CDib中而不是直接存放在Doc类中,这是个值得思考的问题,大家可以通过查看两个类中定义好的函数,感知一下两个类功能上的差别,这个差别很难准确描述。

        View类是一个用于与用户交互的类,其功能包含很广泛,比如用户按下某个选项后应该进行什么函数操作(其实就是我们做的部分)、弹窗、鼠标的形状改变等。

        如果要进行一个比较形象的比喻,View类是前端,Doc类是后端。

MFC我们所使用的框架理解

        我们使用的MFC框架基本流程如下:

        在菜单MENU中(你可以理解为View类给出的对话框),我们可以定义选项卡,命名为ID_名字。我们在View类事件中直接添加COMMAND,可以添加该ID的On函数,并自己编写改函数。我们所编写的函数体将会在这个选项卡被点击后执行。执行的内容可以是直接进行某项操作,例如弹出对话框,也可以调用指定的对话框类函数(可以不调用)。

        其实以上这一部分理解了之后我们就已经可以进行很多功能的实现了,只是为了让我们与用户的交互性更强,我们需要定义对话框类来收集用户想要输入的信息。(一般情况下,对话框类只负责收集信息,收集完信息后,我们仍需要在View类中处理这个输入对应的结果)

        搭建对话框类的时候,我们需要在Dialog中创建一个IDD_名字,这个东西是我们的设计页面。

        在进行页面设计的过程中,最好对同一组物体按照顺序生成,如我想要创建三个单选框,我就直接按顺序创建IDC_RADIO1、IDC_RADIO2、IDC_RADIO3,而不是在创建IDC_RADIO1后创建一个文本框,再创建IDC_RADIO2,或者创建IDC_RADIO1、IDC_RADIO2之后,删掉IDC_RADIO2再创建一个。设计页面的时候最好提前构思,少做修改,因为我们创建的所有控件都会在后台有一个数字ID,例如IDC_RADIO1可能对应1001,那么IDC_RADIO2就会对应1002,用于让电脑快速识别,不按照顺序创建可能会打乱这个ID,影响以后的操作。

        在设计完页面之后,我们需要先创建类,右键对话框直接创建一个CDlg名字类就行。创建完毕后,我们可以直接在IDD页面添加成员变量,在CDlg类中添加对话框函数与控件函数,也可以通过类向导构建。类向导构建的速度更快,上手之后使用会很方便。

        下图就是类向导页面,我们可以观察资源这里是不是我们想要的IDD名称,如果是ABOUTBOX,说明你还没有添加类,一定要添加类才能使用类向导。类向导有五个部分组成,命令、消息、虚函数、成员变量、方法。方法一般用不到,我不做解释。

        其中,命令是用于添加控件函数的,包含了所有的ID,里面有很多是不需要我们管的,我也不知道为什么他不进行一个筛选。我们可以手动翻看IDC开头的所有ID,如果某个对象的消息函数不是只有上图所示的这两个消息的时候,就说明这个ID是我们的IDD页面里面的控件,可以放心添加函数了。

        消息包含了所有的对话框的消息函数,比如你按下鼠标的时候(OnLButtonDown)、抬起鼠标的时候(OnLButtonUp),移动鼠标的时候对应的函数。(OnMouseMove)。虚函数则是系统定义过了,我们可以重写的函数,比如OnInitDialog(初始化函数)、OnOK(按下确定按钮后发生的事情)。消息和虚函数的差别就是,消息函数都是可有可无的函数,我移动鼠标可以有函数,也可以没有。而虚函数都是必须有的函数,你没写,系统就会用他默认的那一套。

        成员变量就不用细说了,每个控件可以添加一个值与一个CButton类的控件。CButton类的控件可以控制这个控件的状态。下面这个框里的可见,禁用,只读什么的应该都能控制。值就是值,是控件的内容,是编辑框的值,是单选框的序号。

 

         针对单选框,有一个需要注意的地方。单选框是有组选项的,我们可以通过设置第一个单选框的组为TRUE,第N+1个为TRUE,来让第一个到第N个单选框的成为一组。如果不需要分多组,只需要让第一个单选框的组为TRUE,其余全为FALSE即可。这一切的大前提是你的RADIO的ID是按照顺序来的,没有乱,不然计算机就没法将他们识别为一组了。

        在类向导中,一组的单选框只有第一个能设置成员变量与函数,我也不知道为什么,但是你可以通过手动添加函数的方式,在CDlg类中直接添加。对于单选框来说,值就是他们的序号,当你按下第一个单选框,值会变成0,按下第二个,值会变成1,按下第N个,值会变成N-1。

         在创建完对话框类类,添加完对话框类函数、每个控件的变量与函数之后,我们需要将对话框类添加到View类中。我们需要在View类中添加头文件以导入对话框类,然后通过函数去调用,一般函数如下:

	CDIG名字 dlgPara;// 创建对话框		dlgPara.变量= View里的变量;//导入一些对话框需要的变量,比如图像数据之类的if (dlgPara.DoModal() != IDOK)// 显示对话框, {return;}View里的变量 = dlgPara.变量;//将对话框的变量导出来,我们可以通过这个知道用户输入了什么,并进行相应的操作//此处有处理函数pDoc->SetModifiedFlag(true);pDoc->UpdateAllViews(NULL);//是否保存更改后的图像

        其中if(dlgPara.DoModal()!=IDOK){return;}的作用是,打开对话框,并在关闭对话框后结束这个语句。

        综上所述,实际上整个框架的流程是:View类控制Doc类读取文件内容,点击选项卡,(View类调用对话框,人输入信息),View类处理相关操作。

MFC的进阶用法

        一些常规的操作使用MFC的基本框架是很难实现的,我分享一些我用到的、大家经常看到但是不知道什么用的函数与操作。
        1、UpdateData()

        此函数经常出现于对话框类中,UpdateData其实是用于控制控件上显示的值(如编辑框中用户输入的值)与后台存储的值之间转化用的。UpdateData(TRUE)会将编辑框中的值全部复制到后台存储的值(其实就是你这个控件添加的那个值变量);UpdateData(FALSE)会将后台存储的值复制到编辑框中,形成一个交互的作用。

        2、BeginWaitCursor()

        这个函数是View类中的函数,用处是将鼠标变成转圈圈的等待状态,可以用EndWaitCursor()取消等待状态。如果有些函数的时间复杂度比较高,可以考虑在函数前BeginWaitCursor(),结束后EndWaitCursor()。

        3、预览图像

        预览图像的操作在传统框架中很难实现。因为按照我们之前的框架的逻辑,我们在结束对话框前是不会进行图像的处理操作的,因为我们的对话框只是收集数据。如果我们想要在对话框内修改数据,就必须面临一个问题:如果将在对话框中调用pDoc的函数,毕竟我们至少要使用

    pDoc->SetModifiedFlag(true);
    pDoc->UpdateAllViews(NULL);//是否保存更改后的图像

这两个函数才可以。

        方法很简单,就是传参。我们在对话框.h文件中加入头文件#include "MFCApplication1Doc.h",并在对话框类的public中添加    CMFCApplication1Doc* m_pDoc;成员变量,最后只需要在View类中输入    dlgPara.m_pDoc = pDoc;即可。以下是我编写的一个View类中的选项函数的例子:

void CMFCApplication1View::OnStylize()
{CMFCApplication1Doc* pDoc = GetDocument();// 获取文档	CDlgStylize dlgPara;// 创建对话框	dlgPara.m_pDoc = pDoc;if (dlgPara.DoModal() != IDOK)// 显示对话框 {return;}
}

        剩下的我们就只需要在对话框类中预览图像按钮的OnClickButton函数中加入我们的处理函数即可。取消预览只需要交换后台数据即可。(我们还需要重写OnOK函数,OnOK函数只需要调用我们的预览图像函数即可)

        以上方法是通过小改动实现的预览图像,效果并不是很好。加入我们通过平移操作处理了这幅图像,然后预览了这幅图像的油画效果,这时候我们点取消预览,结果连平移效果也没有了。有什么方法可以解决这个问题吗?有的,理论可行,我没实现,方法如下:

        给每一个操作编号,使用栈存储用户输入的操作,预览与确定即入栈,取消预览与取消即出栈,我们每次更新视图的时候,将图像与后台交换,并从栈底按照操作编号操作到栈顶即可。

        4、存储并读取对话框中的数据

        

#include <fstream>
//存储
CFileDialog fileDlg(TRUE, _T("txt"), NULL, 0, _T("*.txt|All Files (*.*) |*.*|"), this);
fileDlg.DoModal();
CString strFilePath = fileDlg.GetPathName();		//文件路径
ofstream fout;
fout.open(strFilePath, ios::out);
if (!fout.is_open())
{AfxMessageBox(L"无法打开文件");return;
}
char buff[1024] = { 0 };
fout << 数据<<" ";fout.close();//读取
CFileDialog fileDlg(TRUE, _T("txt"), NULL, 0, _T("*.txt|All Files (*.*) |*.*|"), this);
fileDlg.DoModal();
CString strFilePath = fileDlg.GetPathName();		//文件路径
ifstream fin;
fin.open(strFilePath, ios::in);
if (!fin.is_open())
{AfxMessageBox(L"无法打开文件");return;
}
fin >> 数据;fin.close();
UpdateData(FALSE);

MFC过程中遇到的问题

        暂时就遇到一个比较严重的问题:当你在CDlg类中同时包含Doc类与function类时,由于View类中有function类,会重定义。修改所有function类中的函数,添加static即可。

        其他的内存读取冲突问题可能是空指针


http://chatgpt.dhexx.cn/article/53uGirJI.shtml

相关文章

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

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

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

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

MFC界面设计入门篇

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

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

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

Mac 终端基本命令

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

10需要知道Mac终端命令

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

Mac终端命令

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

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

ls vi vim 输入完来一句command not found xx 心哇凉哇凉的&#xff0c;心态都崩了。 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电脑的小伙伴需要或多或少了解的一个应用。熟悉并掌握一些基本的常用命令可以帮助我们快速的解决一些日常问题&#xff0c;提高工作效率&#xff0c;下面让小编带大家了解一下吧&#xff01; 基本概念 命令的构成&#xff1a;Command Name、Options、Ar…

Mac终端 常用命令

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

mac 终端 常用命令

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

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

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

RANSAC初识

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

RANSAC算法详解

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

Ransac拟合椭圆

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

RANSAC算法

算法基本思想和流程 RANSAC是通过反复选择数据集去估计出模型&#xff0c;一直迭代到估计出认为比较好的模型。 具体的实现步骤可以分为以下几步&#xff1a; 选择出可以估计出模型的最小数据集&#xff1b;(对于直线拟合来说就是两个点&#xff0c;对于计算Homography矩阵就…

RANSAC迭代估计

RANSAC迭代估计 1. 定义2. 功能3. 流程4. 迭代次数推导5. 实现直线拟合 1. 定义 根据一组包含异常数据的样本数据集&#xff0c;计算出数据的数学模型参数&#xff0c;得到有效样本数据的算法 从一组含有“外点”(outliers)的数据中正确估计数学模型参数的迭代算法 “外点”一…

RANSAC

转自&#xff1a;http://www.cnblogs.com/xrwang/archive/2011/03/09/ransac-1.html 作者&#xff1a;王先荣 本文翻译自维基百科&#xff0c;英文原文地址是&#xff1a;http://en.wikipedia.org/wiki/ransac&#xff0c;如果您英语不错&#xff0c;建议您直接查看原文。 …

机器视觉:ransac算法详解

目录 一、说明&#xff1a; 二、算法步骤 三、算法代码 四、其它补充 一、说明&#xff1a; RANSAC是一种常用的参数估计方法&#xff0c;全称为Random Sample Consensus&#xff08;随机抽样一致性&#xff09;。它通过随机选择数据中的一部分&#xff0c;然后根据这些数据…

RANSAC算法介绍与总结

RANSAC算法 简介RANSAC地面分割 简介 粒子分割主要使用RANSAC算法. RANSAC全称Random Sample Consensus, 即随机样本一致性, 是一种检测数据中异常值的方法. RANSAC通过多次迭代, 返回最佳的模型. 每次迭代随机选取数据的一个子集, 并生成一个模型拟合这个子样本, 例如一条直线…