一、目的:此文通过一个GPA计算器的制作,介绍基于对话框的应用程序的编程方法、常用控件的编程技巧以及控件外观的更改技巧。

二、功能描述:所谓GPA计算器,即进行GPA换算。

  功能要求由如下几点:

  1.学生成绩可以由文件导入。

  2.GPA标准可以选择,也可以手动输入。

  3.GPA的计算过程易于监督控制。

  4.计算结果的可读性强。

三、关键技术与算法:

  1.CFile和CArchive

  CFile是MFC的文件操作基本类,它直接支持无缓冲的二进制磁盘I/O操作,并通过其派生类支持文本文件、内存文件和Socket文件。

  打开文件方法:

CFile(LPCTSTR lpszFileName, UINT nOpenFlags);

其中,lpszFileName表示要打开的文件名,可以是相对路径或绝对路径。

  一个CArchive对象与一个文件相连,充当了文件与要读写的数据结构之间的桥梁和管道的角色。它在文件和要读写的数据结构之间设置了一个缓冲区,提供数据缓冲机制。CArchive的构造函数为:

CArchive(CFile *pFile, UINT nMode, int nBufSize=, void *lpBuf=NULL)

其中,pFile表示CArchive所基于的文件,nMode制定CArchive的存取模式。

  本实例用上述两个类完成了对成绩文件的读取。

  2.公共对话框

  本实例用到了CFileDialog,它是文件对话框,提供从磁盘目录结构中选择一个文件的对话框界面(经常用于打开文件或保存文件)。

四、程序实现:

  1.创建项目:项目名为GPACalculator,基于对话框,其他默认。

  2.界面设计:打开对话框资源IDD_GPACALCULATOR_DIALOG,该对话框将是程序运行时的主界面。

  2.1 下面按要求添加控件:

控件类型             ID                          属性设置
Button IDC_BUTTON_OPEN Caption设为“浏览...”
Button IDC_BUTTON_OK Caption设为“统计”
Static Text 默认 Caption设为“参比标准”
Static Text 默认 Caption设为“标准分”
Static Text 默认 Caption设为“原始分”
Static Text 默认 Caption设为“统计信息”
Static Text 默认 Caption设为“分数列表”
Static Text 默认 Caption设为“加权平均分”
Static Text 默认 Caption设为“GPA”
Static Text 默认 Caption设为“总学分”
Edit Box IDC_EDIT_PATHNAME 默认
Edit Box IDC_EDIT_S1 ReadOnly
Edit Box IDC_EDIT_S2 ReadOnly
Edit Box IDC_EDIT_S3 ReadOnly
Edit Box IDC_EDIT_S4 ReadOnly
Edit Box IDC_EDIT_S5 ReadOnly
Edit Box IDC_EDIT_S6 ReadOnly
Edit Box IDC_EDIT_S7 ReadOnly
Edit Box IDC_EDIT_O1 ReadOnly
Edit Box IDC_EDIT_O2 ReadOnly
Edit Box IDC_EDIT_O3 ReadOnly
Edit Box IDC_EDIT_O4 ReadOnly
Edit Box IDC_EDIT_O5 ReadOnly
Edit Box IDC_EDIT_O6 ReadOnly
Edit Box IDC_EDIT_O7 ReadOnly
Edit Box IDC_EDIT_O8 ReadOnly
Edit Box IDC_EDIT_AVERAGE ReadOnly
Edit Box IDC_EDIT_GPA ReadOnly
Edit Box IDC_EDIT_TOTAL ReadOnly
Group Box 默认 Caption设为“第一步:载入分数文件”
Group Box 默认 Caption设为“第二步:设置参比标准”
Group Box 默认 Caption设为“第三步:统计并显示”
Combo Box IDC_COMBO_STANDARD DropList
List Box IDC_LIST_SCORE 默认

看下效果图:

这里需要说明几点:

1>控件配置表与图片还是比较好对应的。两个按钮及一些静态文本框及三个组框、一个组合框、一个列表框,编辑框有一堆:第一个对应第一步里的那个pathname,然后IDC_EDIT_Sx对应标准分的那七个框框,IDC_EDIT_Ox对应原始分的那八个框框,后面是第三步里的三个编辑框,对应清晰就ok了。

2>至于那个List Box,默认属性里不选择sort(分类),即对于列表框的数据不分类排序。

3>这里得提点下Combo Box的用法。注意在设计ComboBox时,点下向下箭头(小三角形),即(注意看那蓝色的小正方形是否空心):

然后将控件下边(那个实心的小正方形)向下拉,即:

这样才能显示出ComboBox的下拉的项。

  2.2 打开ClassWizard,针对IDD_GPACALCULATOR_DIALOG所指向的类CGPACalculatorDlg,为控件添加类成员变量,按下面的要求:

控件ID                     变量名                 数据类型
IDC_EDIT_PATHNAME m_sPathName CString
IDC_EDIT_S1 m_dS1 double
IDC_EDIT_S2 m_dS2 double
IDC_EDIT_S3 m_dS3 double
IDC_EDIT_S4 m_dS4 double
IDC_EDIT_S5 m_dS5 double
IDC_EDIT_S6 m_dS6 double
IDC_EDIT_S7 m_dS7 double
IDC_EDIT_O1 m_dO1 double
IDC_EDIT_O2 m_dO2 double
IDC_EDIT_O3 m_dO3 double
IDC_EDIT_O4 m_dO4 double
IDC_EDIT_O5 m_dO5 double
IDC_EDIT_O6 m_dO6 double
IDC_EDIT_O7 m_dO7 double
IDC_EDIT_O8 m_dO8 double
IDC_EDIT_AVERAGE m_strAverage CString
IDC_EDIT_GPA m_strGPA CString
IDC_EDIT_TOTAL m_dTotal double
IDC_COMBO_STANDARD m_cmbStandard CComboBox
IDC_LIST_SCORE m_ListScore CListBox

  3.代码编写:

  3.1 添加菜单控制

  3.1.1 制作菜单:vc菜单->插入->资源->选择menu,点击新建->制作如下(选中虚线框,鼠标右键点击属性):

  

其中,“退出”的ID设为ID_MENUITEM_EXIT,“使用说明”的ID设为ID_MENUITEM_MAN,“关于”的ID设为ID_MENUITEM_ABOUT。菜单名字设为IDR_MAIN_MENU。

  3.1.2 装载菜单:打开主界面IDD_GPACALCULATOR_DIALOG的属性对话框,在menu的列表框中选中相应的菜单资源。如下:

  3.1.3 编写菜单代码。使用ClassWizard针对菜单项向对话框类中增加函数。

  1>【使用说明】菜单:

void CGPACalculatorDlg::OnMenuitemMan()
{
// TODO: Add your command handler code here
WinExec("notepad.exe EXPLAIN.HEP",SW_SHOW);
}

  2>【关于】菜单:

void CGPACalculatorDlg::OnMenuitemAbout()
{
// TODO: Add your command handler code here
CAboutDlg dlg;
dlg.DoModal();
}

  3>【退出】菜单:

void CGPACalculatorDlg::OnMenuitemExit()
{
// TODO: Add your command handler code here
DestroyWindow();
}

  3.2 分数存储结构

  在GPACalculatorDlg.h里添加读取分数的数据结构:

typedef struct stScore{
double dOPoint;//原始分
double dSPoint;//标准分
double dNum;//学分
}stScore;

  在StdAfx.h中添加下列头文件以支持CArray的使用:

#include <afxtempl.h>

至于位置,可以写这里:

#include <afxwin.h>         // MFC core and standard components
#include <afxext.h> // MFC extensions
#include <afxdisp.h> // MFC Automation classes
#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
#include <afxtempl.h>
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h> // MFC support for Windows Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT

  在GPACalculatorDlg.h的类GPACalculatorDlg的定义中添加私有对象arScore,用作分数存储:

CArray<stScore,stScore&> arScore;

  3.3 为类GPACalculatorDlg添加一些自定义功能函数(都是私有的。在类GPACalculatorDlg.h文件里添加函数声明,在类GPACalculatorDlg.cpp文件里添加函数实现):

  1>GetSDPoint:根据参比标准,将原始分转化为标准分。

double GetSDPoint(double dPoint);
double CGPACalculatorDlg::GetSDPoint(double dPoint){
UpdateData();
CArray<double,double&> arOPoint;
CArray<double,double&> arSPoint;
arOPoint.Add(m_dO1);
arOPoint.Add(m_dO2);
arOPoint.Add(m_dO3);
arOPoint.Add(m_dO4);
arOPoint.Add(m_dO5);
arOPoint.Add(m_dO6);
arOPoint.Add(m_dO7);
arOPoint.Add(m_dO8); arSPoint.Add(m_dS1);
arSPoint.Add(m_dS2);
arSPoint.Add(m_dS3);
arSPoint.Add(m_dS4);
arSPoint.Add(m_dS5);
arSPoint.Add(m_dS6);
arSPoint.Add(m_dS7); if(dPoint>=arOPoint[])
return arSPoint[];
for(int i=;i<arOPoint.GetSize();i++){
if(dPoint>=arOPoint[i])
return arSPoint[i-];
}
return ;
}

  2>SpliterString:分数文件是以“原始分 学分”的方式给出的,需要一个功能函数来将这个字符串拆分成两个double型的原始分和学分。

void SpliterString(CString str,double &a,double &b);
void CGPACalculatorDlg::SpliterString(CString str,double &a,double &b){
CString strOne,strTwo;
int ifind; str.TrimLeft(' ');
str.TrimRight(' '); ifind=str.Find(' ');
strOne=str.Left(ifind);
strTwo=str.Right(str.GetLength()-ifind-); a=atof(LPCTSTR(strOne));
b=atof(LPCTSTR(strTwo));
}

  3>GatherData:将分数文件的信息导入到分数存储结构,当m_sPathName="",即无分数文件时,则导入失败。

void GatherData();
void CGPACalculatorDlg::GatherData(){
CString str;
stScore temp; if(m_sPathName=="")
return; arScore.RemoveAll();
CFile file(m_sPathName,CFile::modeRead);
CArchive ar(&file,CArchive::load);; ar.ReadString(str);
while(str!=""){
SpliterString(str,temp.dOPoint,temp.dNum);
temp.dSPoint=GetSDPoint(temp.dOPoint);
arScore.Add(temp); ar.ReadString(str);
}
}

  4>ChangeType:根据不同的输入值,转变成不同的参比标准。

void ChangeType(int nType);
void CGPACalculatorDlg::ChangeType(int nType){
switch(nType){
case :
case :
m_dS1=m_dS2=m_dS3=m_dS4=m_dS5=m_dS6=m_dS7=;
m_dO1=m_dO2=m_dO3=m_dO4=m_dO5=m_dO6=m_dO7=m_dO8=;
break;
case :
m_dS1=;
m_dS2=;
m_dS3=;
m_dS4=;
m_dS5=m_dS6=m_dS7=;
m_dO1=;
m_dO2=;
m_dO3=;
m_dO4=;
m_dO5=;
m_dO6=m_dO7=m_dO8=;
break;
case :
m_dS1=4.3;
m_dS2=;
m_dS3=3.7;
m_dS4=3.3;
m_dS5=3.0;
m_dS6=2.7;
m_dS7=2.3;
m_dO1=;
m_dO2=;
m_dO3=;
m_dO4=;
m_dO5=;
m_dO6=;
m_dO7=;
m_dO8=;
break;
case :
m_dS1=;
m_dS2=;
m_dS3=;
m_dS4=m_dS5=m_dS6=m_dS7=;
m_dO1=;
m_dO2=;
m_dO3=;
m_dO4=;
m_dO5=m_dO6=m_dO7=m_dO8=;
break;
}
}

  5>SetEditRead:输入为TRUE时,参比标准的编辑框将被设为只读模式;而输入为FALSE时,参比标准的编辑框将被设为可写模式。

void SetEditRead(bool bReadOnly);
void CGPACalculatorDlg::SetEditRead(bool bReadOnly){
((CEdit *)GetDlgItem(IDC_EDIT_S1))->SetReadOnly(bReadOnly);
((CEdit *)GetDlgItem(IDC_EDIT_S2))->SetReadOnly(bReadOnly);
((CEdit *)GetDlgItem(IDC_EDIT_S3))->SetReadOnly(bReadOnly);
((CEdit *)GetDlgItem(IDC_EDIT_S4))->SetReadOnly(bReadOnly);
((CEdit *)GetDlgItem(IDC_EDIT_S5))->SetReadOnly(bReadOnly);
((CEdit *)GetDlgItem(IDC_EDIT_S6))->SetReadOnly(bReadOnly);
((CEdit *)GetDlgItem(IDC_EDIT_S7))->SetReadOnly(bReadOnly); ((CEdit *)GetDlgItem(IDC_EDIT_O1))->SetReadOnly(bReadOnly);
((CEdit *)GetDlgItem(IDC_EDIT_O2))->SetReadOnly(bReadOnly);
((CEdit *)GetDlgItem(IDC_EDIT_O3))->SetReadOnly(bReadOnly);
((CEdit *)GetDlgItem(IDC_EDIT_O4))->SetReadOnly(bReadOnly);
((CEdit *)GetDlgItem(IDC_EDIT_O5))->SetReadOnly(bReadOnly);
((CEdit *)GetDlgItem(IDC_EDIT_O6))->SetReadOnly(bReadOnly);
((CEdit *)GetDlgItem(IDC_EDIT_O7))->SetReadOnly(bReadOnly);
((CEdit *)GetDlgItem(IDC_EDIT_O8))->SetReadOnly(bReadOnly);
}

  3.4 控件事件:借助ClassWizard添加控件对应的处理函数。

  1>组合框:可以通过下拉菜单选择不同的参比标准。

  需要做两步:

  首先,为组合框设初始值,否则下拉菜单里是空的。在CGPACalculatorDlg.cpp文件里的OnInitDialog()初始化函数里添加代码为组合框设初始值:

BOOL CGPACalculatorDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// IDC_COMBO_STANDRD
m_cmbStandard.AddString("0");
m_cmbStandard.AddString("1");
m_cmbStandard.AddString("2");
m_cmbStandard.AddString("3");
m_cmbStandard.AddString("用户自定义");
// 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);
......

  然后,还需要为组合框添加处理函数:

void CGPACalculatorDlg::OnSelchangeComboStandard()
{
// TODO: Add your control notification handler code here
int nIndex=m_cmbStandard.GetCurSel();
if(==nIndex)
SetEditRead(false);
else
SetEditRead(true);
ChangeType(nIndex);
UpdateData(false);
}

  2>【浏览...】按钮:弹出“打开”对话框以供选择磁盘上的分数文件。添加的处理函数如下:

void CGPACalculatorDlg::OnButtonOpen()
{
// TODO: Add your control notification handler code here
CFileDialog fdlg(true,NULL,NULL,OFN_OVERWRITEPROMPT,"成绩文件|*.dat;*.txt|",NULL);
if(fdlg.DoModal()==IDOK){
m_sPathName=fdlg.GetPathName();
}
UpdateData(false);
}

  3>【统计】按钮:单击该按钮计算各项参数值,并把分数显示在列表框。添加的处理函数如下:

void CGPACalculatorDlg::OnButtonOk()
{
// TODO: Add your control notification handler code here
double dGPAall=;
double dAvgall=;
GatherData();
int i=;
int size=arScore.GetSize();
if(size==)
return;
m_ListScore.ResetContent();
m_ListScore.AddString("序号 成绩 学分 标准");
m_ListScore.AddString("-------------------------------------------");
m_dTotal=;
while(i<size){
CString str;
m_dTotal+=arScore.GetAt(i).dNum;
dGPAall+=arScore.GetAt(i).dSPoint*arScore.GetAt(i).dNum;
dAvgall+=arScore.GetAt(i).dOPoint*arScore.GetAt(i).dNum;
str.Format("%.2d %0.2f %0.2f %0.2f",i+,arScore.GetAt(i).dOPoint,arScore.GetAt(i).dNum,arScore.GetAt(i).dSPoint);
i++;
m_ListScore.AddString(str);
} m_strGPA.Format("%.2f",dGPAall/m_dTotal);
m_strAverage.Format("%.2f",dAvgall/m_dTotal);
UpdateData(false);
}

  3.5 界面外观

  若对界面控件默认的外观感到不满意,可以截取消息加以控制。本程序截取VM_CTLCOLOR消息来达到修改控件外观颜色的目的。操作如下:

处理函数为:

HBRUSH CGPACalculatorDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: Change any attributes of the DC here
switch(pWnd->GetDlgCtrlID()){
case IDC_EDIT_S1:
case IDC_EDIT_S2:
case IDC_EDIT_S3:
case IDC_EDIT_S4:
case IDC_EDIT_S5:
case IDC_EDIT_S6:
case IDC_EDIT_S7:
case IDC_EDIT_O1:
case IDC_EDIT_O2:
case IDC_EDIT_O3:
case IDC_EDIT_O4:
case IDC_EDIT_O5:
case IDC_EDIT_O6:
case IDC_EDIT_O7:
case IDC_EDIT_O8:
pDC->SetBkColor(RGB(,,));
pDC->SetTextColor(RGB(,,));
break;
case IDC_LIST_SCORE:
pDC->SetBkColor(RGB(,,));
pDC->SetTextColor(RGB(,,));
break;
case IDC_EDIT_AVERAGE:
case IDC_EDIT_GPA:
case IDC_EDIT_TOTAL:
pDC->SetBkColor(RGB(,,));
pDC->SetTextColor(RGB(,,));
}
// TODO: Return a different brush if the default is not desired
return hbr;
}

五、运行结果:

  1.使用说明:

  1.1 GPA计算器要求文件输入,输入格式:[spaces]课程成绩<spaces>学分数[spaces]

  eg:

 6.5
1.5
2.5 1.5 1.5

  1.2 GPA计算器提供了几种GPA参考分数标准,可通过下拉列表选择。若程序中提供的标准(0/1/2/3)不能满足需要的话,可选择“用户自定义”,可以自行在编辑框里填入自己所需的标准。

  1.3 选择分数文件和参比标准后,单击【统计】按钮即可求出相应的加权平均分、GPA及总学分等。程序会显示分数文件中的分数,可据此核对计算结果是否正确。

  2.程序演示:

六、小结

  麻雀虽小,五脏俱全。

第二个MFC实例:GPA计算器的更多相关文章

  1. MFC原理第二讲.MFC的初始化过程

    MFC原理第二讲MFC的初始化过程 一丶简介 通过上一讲.我们手工模拟了一个MFC程序. 但是上一讲留下了疑问. 就是WinMain在哪.以及消息处理在哪里. 这一节主要就是讲解Winmain在哪里. ...

  2. Python版GPA计算器

    最近在网申投简历时遇到一个需要计算GPA的问题,想起自己在上学时写的Excel公式版GPA计算器略显low,而且操作也比较复杂,于是一时兴起,写了个Python版的,在此分享给大家,也算是跟上时代的浪 ...

  3. 第一个MFC实例:计算圆周长和圆面积

    一.基于Microsoft MFC的编程方法 MFC是微软基础类库(Microsoft Foundation Class)的缩写.与API不同,MFC不是Windows操作系统的组成部分,而是微软公司 ...

  4. 2012-11-17 12:28 用MFC实现的计算器(详细版)

    这篇文章里通过计算器的简单实现,让大家能够了解一般对话框应用程序开发的基本流程 要求:编写简单的计算器应用程序,要求利用按钮进行加.减.乘,除操作,在对话框输出计算机结果. 下面结合操作步骤,讲解对话 ...

  5. JavaScript实例之计算器

    代码实例: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...

  6. MFC之简单计算器

    1.界面 2.变量 combobox的变量类型是CComBoBox类型,三个输入框是double类型: 它的type是Drop List 3.代码 (1).初始化combobox BOOL Ccalc ...

  7. [妙味Ajax]第二课:实例:留言板、瀑布流

    知识点总结 瀑布流原理(固定布局) 总宽度大小固定 每列宽度固定,比如LI,高度自动计算,每列使用float:left来布局 计算最短的一列,将Div插到最短的一列处(li里面包div)(getSho ...

  8. 原生javascript开发计算器实例

    计算器的主要作用是进行数字运算,开发一个计算器功能的web实例,有助于更好的掌握js基础的数字运算能力. 本实例详细分析一个js计算器的开发步骤,学习本教程时最好先具备一些基础的js知识. 计算器包括 ...

  9. MFC 框架技术简单研讨

    引用:http://www.cnblogs.com/chinazhangjie/archive/2011/09/20/2181986.html 正文: 第一讲 Win32 App  和  MFC Fr ...

随机推荐

  1. WPF 自定义消息框(转)

    相信很多人用过MessageBox.show(),是不是觉得这个消息框有点丑呢,反正我是觉得有点丑的,所以我自己重写了一个.先不说,上两幅图对比先: 当然,也不是很好看,不过比原有的好多了. 不多说了 ...

  2. WINDOWS下kill进程的命令

    相信大家都有用命令行(CMD)解决问题的习惯,起码我感觉自己在处理Windows系统故障时越来越离不开Windows PE了,今天我想介绍两个很实用的命令:Tasklist与Tskill.命令:Tas ...

  3. Android Activity活动状态及生存周期

    1.活动状态 每个活动在其生命周期中最多可能会有4中状态. (1)运行状态 当一个活动位于返回栈的栈顶时,此时活动就处于运行状态.系统不会回收处于运行状态的活动. (2)暂停状态 当一个活动不再处于栈 ...

  4. Android 系统四大组件

    Android 系统四大组件分别是活动(Activity).服务(Service).广播接收器(Broadcast Receiver)和内容提供器(Content Provider). 活动是所有 A ...

  5. Android JNI访问Java成员

    在 JNI 调用中,不仅仅 Java 可以调用本地方法,本地方法也可以调用 Java 中的方法和成员变量. Java 中的类封装了属性和方法,想要访问 Java 中的属性和方法,首先要获得 Java ...

  6. C++代码规范之命名

    C++代码规范之命名 一.命名的两个基本原则 1.含义清晰,不易混淆: 2.不和其它模块.系统API的命名空间相冲突. 二.命名通则 1.在所有命名中,都应使用标准的英文单词或缩写:不得使用拼音或拼音 ...

  7. JSch基本使用

    JSch基本使用 JSch 是SSH2的一个纯Java实现.它允许你连接到一个sshd 服务器,使用端口转发,X11转发,文件传输等等.你可以将它的功能集成到你自己的 程序中.同时该项目也提供一个J2 ...

  8. oracle 中GROUP BY的用法

    转自:http://blog.csdn.net/basenet855x/article/details/6694150 问题: select item.itemnum,item.in1,item.in ...

  9. Java - 将 List 等分(最后一部分处理多余部分)

    背景 今天由于要使用多线程,所以事先需要确定启动线程个数.于是需要先将集合进行分配,确定线程的个数. 解决方案 首先是实现 public static <T> List<List&l ...

  10. SQL Server Management Studio (SSMS)

    最新的SQLServer数据库已经不集成SQL Server Management Studio需要单独下载安装. https://docs.microsoft.com/zh-cn/sql/ssms/ ...