MFC对话框程序设计详解(中南大学朱从旭老师)1. 利用MFCAppWizard(exe)建立程序框架注意:请各位同学新建的“工程名称”用自己的全学号命名(这里假定学号是“123”),此过程的操作详见图1.1~图1.7所示。
图1.1 “新建”对话框之新建“工程”图1.2 选择“程序类型”图1.3 选择“程序类型”续1 图1.4 选择“程序类型”续2图1.5 “程序类”总结图1.6 程序最后总结到此为止,程序的图像见面的框架已经建立,如图1.7所示。
接下来第2节将介绍如何在界面上添加控件(文本框、命令按钮、等),以便得到具体程序的个性化界面。
图1.7 在开发环境下看程序的界面2. 设计程序的界面本节所述的操作步骤和方法详见图2.1~图2.7所示。
图2.1 在开发环境下调出“控件”工具箱图2.2 在程序的“界面”中删除不要的控件图2.3 用鼠标在“控件”工具箱里单击“静态文本”控件,然后在界面上画出一个“静态文本”控件(Static),然后右击该控件并单击“属性”菜单图2.4 在弹出的“属性”对话框中指定该控件的ID(名称)、标题(可见内容)图2.5 按照图2.1~2.4的方法画出一个编辑框(EDIT),并在它的“属性”对话框中暂时只要指定该控件的ID(名称)图2.6 按照图2.1~2.4的方法画出一个命令按钮(BUTTON),并在它的“属性”对话框中指定该命令按钮控件的ID(名称)、标题(可见内容)图2.7 按上述方法画出本程序的所有控件;同时将原有“确定”控件的标题改为“添加学生”,将原有“取消”控件的标题改为“退出程序”完成图2.1~2.7的操作后,你的个性化程序界面已经设计好,此时,最好“编译”->“链接”->“运行”一下看看,如果上述操作无误,你将看到一个像模像样的图形界面程序运行起来了。
不过,当用户用鼠标单击界面上的命令按钮时程序不会有任何反应,要想实现命令按钮的相应功能,需要在相应“消息映射函数”中自己写一些程序代码,请阅读第3节。
3. 设置“成员变量Member Variables”和“消息映射Message Maps”3.1从“查看”菜单/“建立类向导”引出“MFC ClassWizard”对话框(图3.1);图3.1 “MFC ClassWizard”对话框3.2 为每个编辑框(Edit)设置成员变量(Member Variables)以“学号”框设置成员变量为例:在图3.1中,选“Member Variables”标签;然后在控件列表框中选定“Number_EDIT”,并单击“Add Variable…"按钮,在弹出的“Add Member Variable"对话框中,指定它的关联变量的名字为m_1(也可以是其它名字),关联变量的数据类型为"int",如图3.2所示。
图3.2 设置编辑框的关联变量(变量名、数据类型)按上述方法为每个编辑框指定一个关联变量(含名字、数据类型);本示例中,给每个编辑框指定的关联变量(含名字、数据类型)如下面的表1所示。
表1. 本示例中每个编辑框的名字、对应关联变量名编辑框描述的内容编辑框的名称编辑框的关联变量名关联变量的数据类型学号Number_EDIT m_1 int姓名Name_EDIT m_2 Cstring语文Chinese_EDIT m_3 int数学Math_EDIT m_4 int英语English_EDIT m_5 int 输出姓名OutName_EDIT m_6 Cstring输出总分OutTotal_EDIT m_7 int输出平均分OutAverage_EDIT m_8 Int指定学号值Specify_EDIT m_9 int备注:Cstring类型是字符串类型(而char类型是字符类型,属于基本数据类型),Cstring 类型不是基本数据类型,实际上是C++的<string>文件中预定义的一种类,所以Cstring类型的变量实际上是对象变量。
用Cstring类型的变量处理字符串,比用char类型的数组处理字符串更加简单、方便。
3.3 为每个命令按钮(Button)设置消息映射(Message Maps)以“添加学生”按钮设置鼠标单击的消息映射为例:在图3.1中,选“Message Maps”标签;然后在控件列表框中选定“Add_BUTTON”,在“Message”列表框中选定“BN_CLICKED”;并单击“Add Function…"按钮; 如图3.3所示。
图3.3 为Add_BUTTON按钮设置消息映射在弹出的“Add Member Function "对话框中(图3.4),指定函数名字(如OnAddButton);单击“OK”按钮。
按此法为其余的每个命令按钮分别指定一个成员函数;本例中给每个按钮鼠标单击消息指定的关联成员函数名分别如下面的表2所示。
图3.4 为Add_BUTTON按钮设置的成员函数名(OnAddButton)表2. 每个命令按钮的名称、鼠标单击消息关联的成员函数名按钮的功能描述按钮的名称按钮鼠标单击消息关联的成员函数名添加学生Add_BUTTON OnAddButton退出程序Exit_BUTTON OnExitButton查找指定学号学生并输出Search_BUTTON1 OnSchearButton1查找总分最高学生输出Search_BUTTON2 OnSchearButton2查找总分最低学生输出Search_BUTTON3 OnSchearButton3删除指定学号学生全部信息Delete_BUTTON OnDeleteButton至此,程序界面基本上已经设置好。
用户可以运行程序,即可看到图3.5所示的程序界面;但是用户单击界面上的任何按钮时,程序不能执行任何功能,那是因为我们还没有写一句程序代码。
接下来的事情将是为程序写代码(即程序语句)。
图3.5 程序运行时的界面为了下一步写程序的方便,我们需要记住界面上控件的一些关键信息。
为清楚起见,在此已将这些关键信息列于了前面的表1~表2中:表1是每个编辑框的名字、对应关联变量名、关联变量的数据类型;表2是每个命令按钮的名字、鼠标单击消息所关联的成员函数名。
4. 编写程序代码现在,单击开发环境界面左半窗下面的“FileView”标签,再双击“Source Files”下面的文件名“123Dlg.cpp”(对话框的源文件,dlg是“对话框”的英文单词缩写),即可看到系统自动生成了每个消息的关联成员函数的框架代码(如图4.4)。
编程人员要做的事就是在相应函数体内填写所需要的程序代码;当然,用户还得事先为整个程序的“123.cpp”源文件以及“123.h”头文件写一些代码。
下面给出一些主要代码,供参考,尤其要通过关键代码的注释内容理解关键语句的功能。
图4.1对话框源文件123Dlg.cpp的初始内容4.1 头文件123.h (其内容是定义一个描述学生信息的“类”:c123)以下只给出该文件自己写的那些代码,而系统生成的代码在此省略。
#include <string> // 添加包含字符处理库函数的头文件---此句加在文件最前面………//这里略去的是系统自动生成的代码// 以下类定义添加在文件原有内容的末尾class c123//要求类名的命名规则为"C+你的学号"){protected: // 数据成员变量的访问权限设置为保护型,类外不能直接访问int Number; //学生学号int Chinese, Math, English; //语文、数学、英语三科成绩float Total, Average; //三科的总分、平均分char Name[20]; //学生姓名public: //成员函数的访问权限设置为公开型,类外可以调用它们c123(int Num, char *pc, int Eng, int Chi, int Mat); // 构造函数的声明c123( ); // 默认构造函数的声明(没有形式参数的构造函数)// 本程序将构造函数的实现代码写到了类外(参见另一个源文件123.cpp)// 本程序将类的其余成员函数实现代码都直接写在类里面(参见本文件下面)// 以下为设置成员变量值的函数,均以setXXX形式命名void setNum(int x) // 设置“学号”变量的值{ Number=x; }void setName(char *pc) // 设置“姓名”变量的值{ strcpy(Name, pc); }void setChi(int x) // 设置“语文”变量的值{ Chinese=x; }void setMat(int x) // 设置“数学”变量的值{ Math=x; }void setEng(int x) // 设置“英语”变量的值{ English=x; }// 以下为读取成员变量值的函数,均以getXXX形式命名int getNum( ) // 读取“学号”变量的值{ return Number; }char *getName( ) // 读取“姓名”变量的值{ return Name; }int getChi( ) // 读取“语文”变量的值{ return Chinese; }int getMat( ) // 读取“数学”变量的值{ return Math; }int getEng( ) // 读取“英语”变量的值{return English; }int getTotal( ) // 读取“总分”变量的值{ return (English+Chinese+Math); } // “总分”靠计算得出int getAverage( ) // 读取“平均分”变量的值{ return (English+Chinese+Math)/3; } // “平均分”靠计算得出}; // 类定义结束,注意魔末尾有分号4.2 源文件123.cpp (对类c123的构造函数进行实现)#include "stdafx.h"//VC++程序要包含这个文件,而且要先包含#include "123.h" //"123.h"头文件放在(#include "stdafx.h")的后面#include "string" // 添加包含字符处理库函数的头文件//类c123的构造函数c123( )实现c123::c123(int Num, char *pc, int Eng, int Chi, int Mat){ Number=Num; //给c123类成员变量Number赋值(即设置"学号"数据)strcpy(Name, pc); //字符串不能直接赋值,要调用C++库函数strcpy()进行复制English=Eng; //给类c123的English成员变量赋值(即设置"英语"课成绩)Chinese=Chi; //给类c123的Chinese成员变量赋值(即设置"语文"课成绩)Math=Mat; //给类c123的Math成员变量赋值(即设置"数学"课成绩)Total=Eng+Chi+Mat; //总分Total的值是算出来的Average=(Eng+Chi+Mat)/3; //平均分average的值也是算出来的}//类c123的默认构造函数c123( )实现c123::c123( ) //(没有形式参数,也没有函数体语句){ }4.3 源文件123Dlg.cpp中按钮单击消息对应的成员函数功能实现#include "stdafx.h"#include "123.h"#include "123Dlg.h"//以下为自己添加的预处理语句--------#include <fstream> //文件输入、输出流类对象的使用#include <string> //字符处理库函数的使用using namespace std;//以下为自己添加的全局变量(普通变量1个、对象变量2个,对象数组1个)定义语句int i = -1; //用i用于存储对象数组中实际元素的个数ifstream fin("a.txt"); //用ifstream定义一个输入文件对象fin (类似于标准输入对象cin) ofstream fout("b.txt"); //用ofstream定义一个输出文件对象fout(类似于标准输出对象cout) c123 MyObj[100]; //定义类c123的100个对象, 即对象数组MyObj[]// …………此处省略的是系统自动生成的其它代码(不要改动)//-------------------------------------以下为消息映射函数自己添加的代码-------------------------- void CMy123Dlg::OnAddButton( )//添加学生信息{// TODO: Add your control notification handler code herechar c[50];i ++;UpdateData(TRUE); // 最关键语句,记住其功能:将控件中的数据传递给相应的关联变量:m_1,m_2等MyObj[i].setNum(m_1);strcpy(c, m_2);MyObj[i].setName(c);MyObj[i].setChi(m_3);MyObj[i].setMat(m_4);MyObj[i].setEng(m_5);}void CMy123Dlg::OnDeleteButton( ) //删除学生信息{// TODO: Add your control notification handler code hereUpdateData(TRUE); //将控件中的数据传递给相应的关联变量,主要是为了使m_9得到第9个Edit框里面的内容);要删除的学号是m_9int n, k; //n做循环变量,k记录学号等于待查学号m_9的对象数组元素的下标k=-1; //给k初始化一个负数for (n=0;n<=i;n++)if (MyObj[n].getNum( )==m_9) k=n;if (k==-1)m_6="无此学号可删除";elsefor (n=k;n<=i-1;n++){MyObj[n].setName(MyObj[n+1].getName());MyObj[n].setNum(MyObj[n+1].getNum( ));MyObj[n].setChi(MyObj[n+1].getChi( ));MyObj[n].setMat(MyObj[n+1].getMat( ));MyObj[n].setEng(MyObj[n+1].getEng( ));}i--; //对象数组实际元素个数减1}void CMy123Dlg::OnExitButton( ) //退出程序{// TODO: Add your control notification handler code hereCDialog::OnOK( );}void CMy123Dlg::OnSchearButton1( ) //查找指定学号的学生{// TODO: Add your control notification handler code hereUpdateData(TRUE); //将Edit框中的数据传递给相应的关联变量,主要是为了使m_9得到第9个Edit框里面的内容;使m_9得到要删除的学号int n, k; //n做循环变量,k记录找到的对象数组元素的下标(找到的对象元素其学号等于待查学号m_8)k=-1; //给k初始化一个负数for (n=0;n<=i;n++)if (MyObj[n].getNum( )==m_9) k=n;if (k==-1)m_6="查无此学号";else{ m_6=MyObj[k].getName();m_7=MyObj[k].getTotal( );m_8=MyObj[k].getAverage( );}UpdateData(FALSE); //将关联变量值传递给相应的Edit框}void CMy123Dlg::OnSchearButton2( ) //查找最高分{// TODO: Add your control notification handler code hereint n, k; // n做循环变量, k记录最高分对应的数组元素的下标double max; // max记录最高分max=-1.0; // max应该初始化一个尽量小的数(小于最低分)for (n=0;n<=i;n++)if (MyObj[n].getTotal()>max){max=MyObj[n].getTotal( ); k=n;}m_6=MyObj[k].getName();m_7=MyObj[k].getTotal( );m_8=MyObj[k].getAverage( );UpdateData(FALSE); //将关联变量值传递给相应的Edit框}void CMy123Dlg::OnSchearButton3( )//查找最低分{// TODO: Add your control notification handler code hereint n, k; //n做循环变量, k记录最低分对应的数组元素的下标double min; //min记录最低分min=301; //min应该初始化一个尽量大的数(比全部满分的和还要大的数)for (n=0;n<=i;n++)if (MyObj[n].getTotal( )<min){min=MyObj[n].getTotal( ); k=n;}m_6=MyObj[k].getName();m_7=MyObj[k].getTotal( );m_8=MyObj[k].getAverage( );UpdateData(FALSE); //将关联变量值传递给相应的Edit框}void CMy123Dlg::OnButton1( ) //从文件读取数据{// TODO: Add your control notification handler code herechar c[40];int n=0;while(1){fin>>m_1;fin>>c; //fin不能直接对字符串型对象变量m_2输入m_2=c; //可以将字符串直接赋给字符串型对象变量m_2fin>>m_3;fin>>m_4;fin>>m_5;if (strlen(c)>0){ MyObj[n].setNum(m_1);MyObj[n].setName(c);MyObj[n].setChi(m_3);MyObj[n].setMat(m_4);MyObj[n].setEng(m_5);UpdateData(FALSE); //将关联变量值传递给相应的Edit框n++;}elsebreak;};i=n-1; //最后一个对象元素的下标记录到i变量中}void CMy123Dlg::OnButton2( ) //将对象数组全部元素的数据输出到文件{// TODO: Add your control notification handler code hereint n;char *p;for (n=0;n<=i;n++) //全局变量i值记录了对象数组最后一个元素的下标{ m_1=MyObj[n].getNum();p=MyObj[n].getName();m_3=MyObj[n].getChi();m_4=MyObj[n].getMat();m_5=MyObj[n].getEng();fout<<m_1<<'\t'<<p<<'\t'<<m_3<<'\t'<<m_4<<'\t'<<m_5<<endl;//向文件中输出一组数据}}。