欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于C++MFC的学生成绩管理系统

程序员文章站 2022-05-06 18:10:08
...

1.题目要求

学期末,班级要统计该学期考试成绩,计算每个人的学分绩点,并按照学分绩点、平均分进行排序。假设本学期开设的课程共有n门课程,每门课程均有名称、学分数;学生考试成绩的学分绩点根据考试成绩核对如下:
100分 5绩点;95-99分 4.5绩点;90-94分 4个绩点;
85-89分 3.5绩点 80-84分 3绩点,以此类推。

学生学期绩点计算方法如下:
∑(课程获取的绩点数 * 学分数)/∑学分数

提示:
1、学期所学课程的记录文件为lesson.txt,该文件共三列,第一列为课程编号,第二列为课程名称,第三列为课程学分;
2、考试成绩记录文件为grade.txt,该文件第一行为标题行,记录考生学号、姓名、考试课程;从第二行开始记录考生对应课程的考试成绩,形式如下:
基于C++MFC的学生成绩管理系统
请编写程序完成如下界面与功能:
基于C++MFC的学生成绩管理系统
1)读取课程时,对课程文件进行读取;
2)读取成绩时对成绩文件进行读取;
3)在课程信息列表中显示所有课程的基本信息;
4)在学生考试成绩信息列表中显示学生考试信息;
5)在学会考试信息列表中可对学生成绩进行修改,修改完成后,点击修改,则完成修改工作;点击保存时,将学生考试信息保存;
6)当点击查询时,对学生考试情况进行查询,弹出如下对话框
基于C++MFC的学生成绩管理系统
可以以学号或姓名查询,在学号或者姓名编辑框中输入学号或者姓名,点击查询,在下方的列表框中显示学生考试信息。

7)点击上个界面中的计算绩点按钮时,弹出如下界面
基于C++MFC的学生成绩管理系统
计算出每个学生的绩点;选择一种排序方法(生序排序或者降序排序),点击排序后,对列表中的数据进行排序;
点击保存按钮时,将排序后的结果输出到一个文件中,该文件*有三列数据,如列表框中所示。

2.需求分析

用户利用此学生成绩管理系统,应得以实现以下主要功能:
(1)、“读取课程”:将既定lesson.txt中的课程信息读入系统,并在课程信息列表中显示出来;
(2)、“读取成绩”:将既定grade.txt中的学生成绩读入系统,并在学生考试信息列表中显示出来;
(3)、“修改”:对考试信息列表中学生成绩进行修改,并将改动保存至grade.txt;
(4)、“计算绩点”:对各学生平均绩点进行计算,在绩点统计列表中显示出来,并能根据需要,依照平均绩点进行升序或降序排列;
(5)、“查询”:能通过学生的姓名或学号,查询其各科考试成绩,并在信息查询列表中显示出来。

3.总体设计

为实现上述功能,应按照以下流程图构建系统:
基于C++MFC的学生成绩管理系统
主要的类:

(1)欢迎界面对话框的类

// WelcomeDlg 对话框

class WelcomeDlg : public CDialogEx
{
	DECLARE_DYNAMIC(WelcomeDlg)

public:
	WelcomeDlg(CWnd* pParent = nullptr);   // 标准构造函数
	virtual ~WelcomeDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_DIALOG_WELCOME };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()
};

(2)登录界面对话框的类

// LoginDlg 对话框

class LoginDlg : public CDialogEx
{
	DECLARE_DYNAMIC(LoginDlg)

public:
	LoginDlg(CWnd* pParent = nullptr);  // 标准构造函数
	virtual ~LoginDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_DIALOG_LOGIN };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()
public:
	CString m_szUsername;//Edit control控件值的变量,用于存储用户输入的“用户名”
	CString m_szPassward;//Edit control控件值的变量,用于存储用户输入的“密码”

(3)课程的类

class course
{
public:
	//cstr
	course() :code(""),cname(""), credit(0)
	{}
	course(CString _cname, CString _code, float _credit) :cname(_cname), code(_code), credit(_credit)
	{}
	//copy-cstr
	course(const course & c)
	{
		cname = c.cname;
		code = c.code;
		credit = c.credit;
	}
	//dstr
	virtual~course()
	{}
	//member of data
public:
	CString code; //课程编号
	CString cname; //课程名称
	float credit; //课程学分
};

(4)学生的类

class student
{
public:
	//cstr
	student() :sname(""), number(""), averpoint(0.0f), spoint(0.0f)
	{
		grade[3] = { 0 };
	}
	student(CString _sname, CString _number, int _grade[3],float _averpoint, float _spoint) :sname(_sname), number(_number), averpoint(_averpoint), spoint(_spoint)
	{
		grade[0] = _grade[0];
		grade[1] = _grade[1];
		grade[2] = _grade[2];
	}
	//dstr
	virtual~student()
	{}
	//member of data
public:
	CString sname;//学生姓名
	CString number;//学生学号
	int grade[3];//假设共有三门考试科目,存储成绩的数组
	float averpoint;//学生的平均学分绩点GPA
	float spoint;//学生所有考试科目的总学分
};

(5)系统主界面对话框的类

// MainDlg 对话框

class MainDlg : public CDialogEx
{
	DECLARE_DYNAMIC(MainDlg)

public:
	MainDlg(CWnd* pParent = nullptr);   // 标准构造函数
	virtual ~MainDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_DIALOG_MAIN };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()
public:
	CListCtrl CourseList;//课程信息列表Listctrl的控件变量
	CListCtrl GradeList;//学生考试信息列表Listctrl的控件变量
CEdit m_edit;//供成绩修改用的Edit control
protected:
	CalPointDlg*cdlg;
	SearchDlg*sdlg;
public:
virtual BOOL OnInitDialog();
	afx_msg void OnBnClickedButtonGetcourse();//按“读取课程”时的操作
	afx_msg void OnBnClickedButtonGetscore();//按“读取成绩”时的操作
	afx_msg void OnBnClickedButtonCalculatepoint();//按“计算绩点”时的操作
	afx_msg void OnBnClickedButtonSearch();//按“查询”时的操作
afx_msg void OnBnClickedButtonChange();//按“修改”时的操作
	afx_msg void OnBnClickedButtonSave();//按“保存”时的操作
	afx_msg void OnLvnItemchangedListDisplayscore(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnNMDblclkListDisplayscore(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnEnKillfocusEditChange();//使供成绩修改用的Edit control失焦的操作
};

(6)“计算绩点”子界面对话框的类

// CalPointDlg 对话框

class CalPointDlg : public CDialogEx
{
	DECLARE_DYNAMIC(CalPointDlg)

public:
	CalPointDlg(CWnd* pParent = nullptr);   // 标准构造函数
	virtual ~CalPointDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_DIALOG_CALPOINT };
#endif

public:
	void calaverpoint();//计算学生平均学分绩点的函数
	float calgradepoint(int i);//计算单科绩点的函数
protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()
public:
	CListCtrl PointList;//绩点统计列表Listctrl的控件变量
	virtual BOOL OnInitDialog();
	afx_msg void OnBnClickedButtonSaverank();//按“保存”时的操作
	afx_msg void OnBnClickedRadioUp();//选择“升序排列”时的操作
	afx_msg void OnBnClickedRadioDown();//选择“降序排列”时的操作
	afx_msg void OnBnClickedButtonRamk();//按“排序”时的操作
};

(7)“查询”子界面对话框的类

// SearchDlg 对话框

class SearchDlg : public CDialogEx
{
	DECLARE_DYNAMIC(SearchDlg)

public:
	SearchDlg(CWnd* pParent = nullptr);   // 标准构造函数
	virtual ~SearchDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_DIALOG_SEARCH };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()
public:
	CListCtrl SearchList;//信息查询列表Listctrl的控件变量
	virtual BOOL OnInitDialog();
	afx_msg void OnBnClickedButtonSearch();//按“查询”时的操作
public:
	CString SearchNumber;//Edit control控件值的变量,用于存储待查询学生的“学号”
	CString SearchName;//Edit control控件值的变量,用于存储待查询学生的“姓名”
};

4.详细设计

具体函数代码:

(1)MFCApplication(学生成绩管理系统).cpp

BOOL CMFCApplication学生成绩管理系统App::InitInstance()
{
// 自己添加部分
	WelcomeDlg wdlg;//建一个欢迎对话框类的对象
	if (wdlg.DoModal() == IDOK)//程序运行时弹出对话框,选择“登录”时执行以下操作
	{
		LoginDlg ldlg;//建一个登录对话框类的对象
		if (ldlg.DoModal() != IDOK)return FALSE;程序运行时弹出对话框,选择“离开”时退出系统
		if (ldlg.m_szUsername != TEXT("Snoopy") || ldlg.m_szPassward != TEXT("981210"))
		{
			MessageBox(NULL, TEXT("用户名或密码错误"), TEXT("信息错误"), MB_OK | MB_ICONERROR);
			return FALSE;
		}//如用户名、密码均正确,按“登录”时执行以下操作
		MainDlg mdlg;//建一个主界面对话框的类
		MessageBox(NULL, TEXT("请先读取课程及成绩哦~"), TEXT("温馨提示"), MB_OK);
		mdlg.DoModal();//模态对话框,主界面弹出
		MessageBox(NULL, TEXT("欢迎您再次使用本系统!"), TEXT("THANKS"), MB_OK);
		return FALSE;
	}
	return FALSE;选择“退出”时,退出系统
}

(2)MainDlg.cpp//主界面对话框类中主要函数的具体实现

// MainDlg 对话框

IMPLEMENT_DYNAMIC(MainDlg, CDialogEx)

MainDlg::MainDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DIALOG_MAIN, pParent)
{
	cdlg = NULL;//初始时,将课程信息列表Listctrl的指针变量指向空
	sdlg = NULL;//初始时,将学生考试信息列表Listctrl的指针变量指向空
}

MainDlg::~MainDlg()
{
	if (cdlg)//析构时,释放课程信息列表Listctrl的指针指向空间
	{
		delete cdlg;
		cdlg = NULL;
	}
	if (sdlg)//析构时,释放学生考试信息列表Listctrl的指针指向空间
	{
		delete sdlg;
		sdlg = NULL;
	}
}

void MainDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST_DISPLAYCOURSE, CourseList);
	DDX_Control(pDX, IDC_LIST_DISPLAYSCORE, GradeList);
	DDX_Control(pDX, IDC_EDIT_CHANGEGRADE, m_edit);
}

BEGIN_MESSAGE_MAP(MainDlg, CDialogEx)
	ON_BN_CLICKED(IDC_BUTTON_GETCOURSE, &MainDlg::OnBnClickedButtonGetcourse)
	ON_BN_CLICKED(IDC_BUTTON_GETSCORE, &MainDlg::OnBnClickedButtonGetscore)
	ON_BN_CLICKED(IDC_BUTTON_CALCULATEPOINT, &MainDlg::OnBnClickedButtonCalculatepoint)
	ON_BN_CLICKED(IDC_BUTTON_SEARCH, &MainDlg::OnBnClickedButtonSearch)
	ON_BN_CLICKED(IDC_BUTTON_SAVE, &MainDlg::OnBnClickedButtonSave)
	ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_DISPLAYSCORE, &MainDlg::OnLvnItemchangedListDisplayscore)
	ON_NOTIFY(NM_DBLCLK, IDC_LIST_DISPLAYSCORE, &MainDlg::OnNMDblclkListDisplayscore)
	ON_EN_KILLFOCUS(IDC_EDIT_CHANGEGRADE, &MainDlg::OnEnKillfocusEditChange)
	ON_BN_CLICKED(IDC_BUTTON_CHANGE, &MainDlg::OnBnClickedButtonChange)
END_MESSAGE_MAP()

// MainDlg 消息处理程序

BOOL MainDlg::OnInitDialog()//主界面对话框初始化时的操作
{
	CDialogEx::OnInitDialog();

	// TODO:  在此添加额外的初始化
	m_edit.ShowWindow(SW_HIDE);

	CourseList.InsertColumn(0, _T("课程编号"));//课程信息列表显示的表头
	CourseList.SetColumnWidth(0, 80);
	CourseList.InsertColumn(1, _T("课程名称"));
	CourseList.SetColumnWidth(1, 80);
	CourseList.InsertColumn(2, _T("课程学分"));
	CourseList.SetColumnWidth(2, 80);

	CourseList.SetExtendedStyle(CourseList.GetExtendedStyle() | LVS_EX_GRIDLINES );//课程信息列表网格线风格

	GradeList.InsertColumn(0, _T("学号"));//学生考试信息列表显示的表头
	GradeList.SetColumnWidth(0, 70);
	GradeList.InsertColumn(1, _T("姓名"));
	GradeList.SetColumnWidth(1, 60);
	GradeList.InsertColumn(2, _T("C++成绩"));
	GradeList.SetColumnWidth(2, 70);
	GradeList.InsertColumn(3, _T("大学物理C1成绩"));
	GradeList.SetColumnWidth(3, 100);
	GradeList.InsertColumn(4, _T("高等数学A2成绩"));
	GradeList.SetColumnWidth(4, 100);

	GradeList.SetExtendedStyle(GradeList.GetExtendedStyle() | LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT);//学生考试信息列表网格线及选择后整行高亮风格

	return TRUE;  // return TRUE unless you set the focus to a control
				  // 异常: OCX 属性页应返回 FALSE
}

void MainDlg::OnBnClickedButtonGetcourse()//按“读取课程”时的操作
{
	lessonfile = "C:\\Users\\Lenovo\\Desktop\\C++\\MFCApplication(学生成绩管理系统)\\lesson.txt";
	if (lessonfile.empty())//如未能找到制定的lesson.txt文件
	{
		MessageBox(TEXT("课程文件读取过程有误"), TEXT("错误提示"), MB_OK);
	}
	ifstream infile;
	infile.open(lessonfile, ios::in);
	if (!infile)//如与lesson.txt建立联系失败
	{
		MessageBox(TEXT("课程文件读取过程有误"), TEXT("错误提示"), MB_OK);
	}
	char buffer[256];
	infile.getline(buffer, 256);//储存第一行的内容
	CourseList.RemoveAllGroups();
	veccourse.resize(3);
	string temcode, temcname;
	for (int i = 0; i<3; i++)
	{
		infile >>temcode >> temcname >> veccourse[i].credit;
		veccourse[i].code = temcode.c_str();//string类型转CString类型
		veccourse[i].cname = temcname.c_str();
	}
	infile.close();
	CourseList.DeleteAllItems();//先清除课程信息列表Listctrl中的内容
	for (int i = 0; i < 3; i++)//将课程信息显示在课程信息列表中
	{
		CourseList.InsertItem(i, _T(""));
		CourseList.SetItemText(i, 0, veccourse[i].code);
		CourseList.SetItemText(i, 1, veccourse[i].cname);
		CString str;
		str.Format(_T("%f"), veccourse[i].credit);//float类型转CString类型
		CourseList.SetItemText(i, 2, str);
	}
}

void MainDlg::OnBnClickedButtonGetscore()//按“读取成绩”时的操作
{
	// TODO: 在此添加控件通知处理程序代码
	gradefile = "C:\\Users\\Lenovo\\Desktop\\C++\\MFCApplication(学生成绩管理系统)\\grade.txt";
	if (gradefile.empty())
	{
		MessageBox(TEXT("课程文件读取过程有误"), TEXT("错误提示"), MB_OK);
	}
	ifstream infile;
	infile.open(gradefile, ios::in);
	if (!infile)
	{
		MessageBox(TEXT("课程文件读取过程有误"), TEXT("错误提示"), MB_OK);
	}
	char buffer[256];
	infile.getline(buffer, 256);
	GradeList.RemoveAllGroups();
	string temnumber, temsname;
	student st;
	vecstudent.clear();//每次执行“读取成绩”时,都先清除掉容器原有的学生信息
	while(!infile.eof())//一直从grade.txt中读入,直到文件尾
	{
		temnumber = "";
		infile >> temnumber >> temsname >> st.grade[0]>>st.grade[1]>>st.grade[2];
		st.number = temnumber.c_str();
		st.sname = temsname.c_str();
		if(temnumber!="")//避免光标在最后一行下一行初保存,造成的最后一行内容显示两次
		vecstudent.push_back(st);
	}
	infile.close();
	GradeList.DeleteAllItems();
	int n = vecstudent.size();
	for (int i = 0; i < n; i++)
	{
		GradeList.InsertItem(i, _T(""));
		GradeList.SetItemText(i, 0, vecstudent[i].number);
		GradeList.SetItemText(i, 1, vecstudent[i].sname);
		CString str;
		str.Format(_T("%d"), vecstudent[i].grade[0]);//int类型转CString类型
		GradeList.SetItemText(i, 2, str);
		str.Format(_T("%d"), vecstudent[i].grade[1]);
		GradeList.SetItemText(i, 3, str);
		str.Format(_T("%d"), vecstudent[i].grade[2]);
		GradeList.SetItemText(i, 4, str);
	}
}

void MainDlg::OnBnClickedButtonCalculatepoint()//按“计算绩点”时的操作

{
	// TODO: 在此添加控件通知处理程序代码
	if (cdlg == NULL)//非模态对话框,弹出“计算绩点”的子界面
	{
		cdlg = new CalPointDlg;
		cdlg->Create(IDD_DIALOG_CALPOINT);
		cdlg->ShowWindow(SW_SHOWNORMAL);
		cdlg->UpdateWindow();
	}
	else
	{
		cdlg->ShowWindow(SW_SHOWNORMAL);
		cdlg->UpdateWindow();
	}
}

void MainDlg::OnBnClickedButtonSearch()//按“查询”时的操作
{
	// TODO: 在此添加控件通知处理程序代码
	MessageBox(TEXT("请输入学号或姓名进行查询~"), TEXT("温馨提示"), MB_OK);
	if (sdlg == NULL)
	{
		sdlg = new SearchDlg;
		sdlg->Create(IDD_DIALOG_SEARCH);
		sdlg->ShowWindow(SW_SHOWNORMAL);
		sdlg->UpdateWindow();
	}
	else
	{
		sdlg->ShowWindow(SW_SHOWNORMAL);
		sdlg->UpdateWindow();
	}
}

void MainDlg::OnNMDblclkListDisplayscore(NMHDR *pNMHDR, LRESULT *pResult)//鼠标左键双击学生考试信息列表时的操作,对Listctrl中学生成绩进行修改
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	CRect rc;
	m_Row = pNMListView->iItem;//获得选中的行
	m_Col = pNMListView->iSubItem;//获得选中列

	if (pNMListView->iSubItem>1) //如果选择的是成绩子项;
	{
		GradeList.GetSubItemRect(m_Row, m_Col, LVIR_LABEL, rc);//获得子项的RECT;
		m_edit.SetParent(&GradeList);//转换坐标为列表框中的坐标
		m_edit.MoveWindow(rc);//移动Edit到RECT坐在的位置;
		m_edit.SetWindowText(GradeList.GetItemText(m_Row, m_Col));//将该子项中的值放在Edit控件中;
		m_edit.ShowWindow(SW_SHOW);//显示Edit控件;
		m_edit.SetFocus();//设置Edit焦点
		m_edit.ShowCaret();//显示光标
		m_edit.SetSel(-1);//将光标移动到最后
	}

	*pResult = 0;
}

void MainDlg::OnEnKillfocusEditChange()使供成绩修改用的Edit control失焦的操作
{
	// TODO: 在此添加控件通知处理程序代码
	CString tem;
	m_edit.GetWindowText(tem);    //得到用户输入的新的内容
	GradeList.SetItemText(m_Row, m_Col, tem);   //设置编辑框的新内容
	m_edit.ShowWindow(SW_HIDE);                //应藏编辑框
}

void MainDlg::OnBnClickedButtonChange()//按“修改”时的操作
{
	// TODO: 在此添加控件通知处理程序代码
	if(m_Row==-1&&m_Col==-1)
		MessageBox(TEXT("您未进行成绩修改"), TEXT("温馨提示"), MB_OK);
	else
	{
		int temgrade;
		temgrade = _ttoi(GradeList.GetItemText(m_Row, m_Col));//CString类型转int类型
		vecstudent[m_Row].grade[m_Col - 2] = temgrade;//将修改后的成绩存入容器中对应位置
		MessageBox(TEXT("修改成功"), TEXT("成功提示"), MB_OK);
	}
}

void MainDlg::OnBnClickedButtonSave()//按“保存”时的操作
{
	// TODO: 在此添加控件通知处理程序代码
	MessageBox(TEXT("此功能仅适用于修改后的保存~"), TEXT("温馨提示"), MB_OK);
	gradefile = "C:\\Users\\Lenovo\\Desktop\\C++\\MFCApplication(学生成绩管理系统)\\grade.txt";
	ofstream outfile;
	outfile.open(gradefile, ios::out);
	if (!outfile)
	{
		MessageBox(TEXT("文件保存过程有误"), TEXT("错误提示"), MB_OK);
	}
	outfile << "学号	姓名	C++成绩	大学物理C1成绩	高等数学A2成绩" << endl;
	size_t n = vecstudent.size();
	string temsname, temnumber;
	for (size_t i = 0; i<n; i++)
	{
		temsname = CT2A(vecstudent[i].sname);//CString类型转string类型
		temnumber = CT2A(vecstudent[i].number);
		outfile << temnumber << "	" << temsname <<"	" << vecstudent[i].grade[0] << setw(16) << vecstudent[i].grade[1] << setw(30) << vecstudent[i].grade[2]<< endl;
	}
	outfile.close();
	MessageBox(TEXT("文件保存成功"), TEXT("成功提示"), MB_OK);
}

(3)CalPointDlg.cpp//“计算绩点”子界面对话框类中主要函数的具体实现

extern vector<student> vecstudent;
extern vector<course> veccourse;
string rankfile;
int ranktype = 0;//用于判断依据绩点排序类型(升序排列、降序排列)的变量

// CalPointDlg 对话框

IMPLEMENT_DYNAMIC(CalPointDlg, CDialogEx)

CalPointDlg::CalPointDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DIALOG_CALPOINT, pParent)
{
}

CalPointDlg::~CalPointDlg()
{
}

void CalPointDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST_POINT, PointList);
}

BEGIN_MESSAGE_MAP(CalPointDlg, CDialogEx)
	ON_BN_CLICKED(IDC_BUTTON_SAVERANK, &CalPointDlg::OnBnClickedButtonSaverank)
	ON_BN_CLICKED(IDC_RADIO_UP, &CalPointDlg::OnBnClickedRadioUp)
	ON_BN_CLICKED(IDC_RADIO_DOWN, &CalPointDlg::OnBnClickedRadioDown)
	ON_BN_CLICKED(IDC_BUTTON_RAMK, &CalPointDlg::OnBnClickedButtonRamk)
END_MESSAGE_MAP()

// CalPointDlg 消息处理程序
float CalPointDlg::calgradepoint(int i)//计算单科绩点的函数
{
	if (i == 100)
		return 5.0;
	else if (i >= 95)
		return 4.5;
	else if (i >= 90)
		return 4.0;
	else if (i >= 85)
		return 3.5;
	else if (i >= 80)
		return 3.0;
	else if (i >= 75)
		return 2.5;
	else if (i >= 70)
		return 2.0;
	else if (i >= 65)
		return 1.5;
	else if (i >= 60)
		return 1.0;
	else
		return 0.0;
}

void CalPointDlg::calaverpoint() //计算学生平均学分绩点的函数
{
	float scredit = veccourse[0].credit + veccourse[1].credit + veccourse[2].credit;
	size_t n = vecstudent.size();
	for (size_t t = 0; t<n; t++)
	{
		for (int i = 0; i < 3; i++)
		{
			vecstudent[t].spoint += calgradepoint(vecstudent[t].grade[i])* veccourse[i].credit;
		}
		vecstudent[t].averpoint = vecstudent[t].spoint / scredit;
		vecstudent[t].averpoint = ((float)((int)(vecstudent[t].averpoint * 100))) / 100;//使平均绩点只保留小数点后两位
	}
}

BOOL CalPointDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// TODO:  在此添加额外的初始化

	PointList.InsertColumn(0, _T("学号"));//绩点统计列表显示的表头
	PointList.SetColumnWidth(0, 70);
	PointList.InsertColumn(1, _T("姓名"));
	PointList.SetColumnWidth(1, 60);
	PointList.InsertColumn(2, _T("绩点"));
	PointList.SetColumnWidth(2, 70);

	PointList.SetExtendedStyle(PointList.GetExtendedStyle() | LVS_EX_GRIDLINES);

	calaverpoint();//计算所有学生各自的平均绩点

	PointList.DeleteAllItems();
	int n = vecstudent.size();//获取总学生数
	for (int i = 0; i < n; i++)
	{
		PointList.InsertItem(i, _T(""));
		PointList.SetItemText(i, 0, vecstudent[i].number);
		PointList.SetItemText(i, 1, vecstudent[i].sname);
		CString str;
		str.Format(_T("%f"), vecstudent[i].averpoint);
		PointList.SetItemText(i, 2, str);
	}

	return TRUE;  // return TRUE unless you set the focus to a control
				  // 异常: OCX 属性页应返回 FALSE
}

void CalPointDlg::OnBnClickedRadioUp()选择“升序排列”时的操作
{
	// TODO: 在此添加控件通知处理程序代码
	ranktype = 1;
}

void CalPointDlg::OnBnClickedRadioDown()选择“降序排列”时的操作
{
	// TODO: 在此添加控件通知处理程序代码
	ranktype = 2;
}

void CalPointDlg::OnBnClickedButtonRamk()//按“排序”时的操作
{
	// TODO: 在此添加控件通知处理程序代码
	size_t n = vecstudent.size();
	for (size_t t = 0; t<n - 1; t++)
	{
		for (int i = 0; i < n - t - 1; i++)
		{
			if (ranktype == 1)//依据平均绩点升序排列并使存储学生信息的容器同步更新
			{
				if (vecstudent[i].averpoint < vecstudent[i + 1].averpoint)
				{
					student s = vecstudent[i];;
					vecstudent[i] = vecstudent[i + 1];
					vecstudent[i + 1] = s;
				}
			}
			else if (ranktype == 2)//依据平均绩点降序排列并使存储学生信息的容器同步更新
			{
				if (vecstudent[i].averpoint > vecstudent[i + 1].averpoint)
				{
					student s = vecstudent[i];;
					vecstudent[i] = vecstudent[i + 1];
					vecstudent[i + 1] = s;
				}
			}
		}
	}
	PointList.DeleteAllItems();//清除绩点统计列表中原有内容
	for (int i = 0; i < n; i++)//在绩点统计列表中显示出排序后的各学生平均绩点信息
	{
		PointList.InsertItem(i, _T(""));
		PointList.SetItemText(i, 0, vecstudent[i].number);
		PointList.SetItemText(i, 1, vecstudent[i].sname);
		CString str;
		str.Format(_T("%f"), vecstudent[i].averpoint);
		PointList.SetItemText(i, 2, str);
	}
}

void CalPointDlg::OnBnClickedButtonSaverank()//按“排序”时的操作
{
	// TODO: 在此添加控件通知处理程序代码
	rankfile = "C:\\Users\\Lenovo\\Desktop\\C++\\MFCApplication(学生成绩管理系统)\\rank.txt";//将按已选择的排序方式排列后的学生信息输出至rank.txt
	ofstream outfile;
	outfile.open(rankfile, ios::out);
	if (!outfile)
	{
		MessageBox(TEXT("文件保存过程有误"), TEXT("错误提示"), MB_OK);
	}
	outfile << "姓名	学号	平均绩点" << endl;
	size_t n = vecstudent.size();
	string temsname, temnumber;
	for (size_t i = 0; i<n; i++)
	{
		temsname=CT2A(vecstudent[i].sname);
		temnumber=CT2A(vecstudent[i].number);
		outfile << temsname << "	" << temnumber << setw(11) << vecstudent[i].averpoint << endl;
	}
	outfile.close();
	MessageBox(TEXT("文件保存成功"), TEXT("成功提示"), MB_OK);
}

(4)SearchDlg.cpp//“查询”子界面对话框类中主要函数的具体实现

extern vector<student> vecstudent;

// SearchDlg 对话框

IMPLEMENT_DYNAMIC(SearchDlg, CDialogEx)

SearchDlg::SearchDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DIALOG_SEARCH, pParent)
	, SearchNumber(_T(""))//初始时,待查询学生的“学号”置为空
	, SearchName(_T(""))//初始时,待查询学生的“姓名”置为空
{
}

SearchDlg::~SearchDlg()
{
}

void SearchDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST_SEARCH, SearchList);
	DDX_Text(pDX, IDC_EDIT_NUMBERSEARCH, SearchNumber);
	DDX_Text(pDX, IDC_EDIT_NAMESEARCH, SearchName);
}

BEGIN_MESSAGE_MAP(SearchDlg, CDialogEx)
	ON_BN_CLICKED(IDC_BUTTON_SEARCH, &SearchDlg::OnBnClickedButtonSearch)
END_MESSAGE_MAP()

// SearchDlg 消息处理程序

BOOL SearchDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// TODO:  在此添加额外的初始化

	SearchList.InsertColumn(0, _T("考试科目"));//信息查询列表显示的表头
	SearchList.SetColumnWidth(0, 80);
	SearchList.InsertColumn(1, _T("成绩"));
	SearchList.SetColumnWidth(1, 80);

	SearchList.SetExtendedStyle(SearchList.GetExtendedStyle() | LVS_EX_GRIDLINES);

	SearchList.InsertItem(0, _T("C++"));//信息查询列表第一列显示的内容
	SearchList.InsertItem(1, _T("大学物理C1"));
	SearchList.InsertItem(2, _T("高等数学A2"));

	return TRUE;  // return TRUE unless you set the focus to a control
				  // 异常: OCX 属性页应返回 FALSE
}

void SearchDlg::OnBnClickedButtonSearch()//按“查询”时的操作
{
	// TODO: 在此添加控件通知处理程序代码
	int searchednumber = -1;//用于记录待查找学生是容器中第几个的变量
	UpdateData(TRUE);//将Edit control中的值随时更新到对应变量中
	int n = vecstudent.size();
	if (SearchNumber != ""&& SearchName == "")//如果只输入了“学号”
	{
		for (int i = 0; i < n; i++)
		{
			if (SearchNumber == vecstudent[i].number) searchednumber = i;
		}
	}
	else if (SearchNumber == "" && SearchName != "")//如果只输入了“姓名”
	{
		for (int i = 0; i < n; i++)
		{
			if (SearchName == vecstudent[i].sname) searchednumber = i;
		}
	}
	else if (SearchNumber != ""&& SearchName != "")//如果“学号”和“姓名”都输入了
	{
		searchednumber = -2;
		for (int i = 0; i < n; i++)
		{
			if (SearchNumber == vecstudent[i].number)
			{
				if (SearchName == vecstudent[i].sname) searchednumber = i;
			}
		}
	}
	if (searchednumber == -1)
		MessageBox(TEXT("您要找的学生不存在"), TEXT("错误提示"), MB_OK);
	else if (searchednumber == -2)
		MessageBox(TEXT("您输入的学号与姓名不匹配"), TEXT("错误提示"), MB_OK);
	Else//将查找到的学生各科考试成绩信息显示到信息查询列表中
	{
		CString str;
		str.Format(_T("%d"), vecstudent[searchednumber].grade[0]);
		SearchList.SetItemText(0, 1, str);
		str.Format(_T("%d"), vecstudent[searchednumber].grade[1]);
		SearchList.SetItemText(1, 1, str);
		str.Format(_T("%d"), vecstudent[searchednumber].grade[2]);
		SearchList.SetItemText(2, 1, str);
	}
}

5.上机操作

(1)、程序运行,弹出欢迎界面
基于C++MFC的学生成绩管理系统
(2)、按“登录”键,弹出登录界面
基于C++MFC的学生成绩管理系统
(3)、正确输入用户名、密码后,按“登录”进入系统(用户这部分我没有做过多拓展,仅提供了一个用户用于测试,可以根据实际需要再完善鸭)
基于C++MFC的学生成绩管理系统
点击“确定”后,弹出主界面
基于C++MFC的学生成绩管理系统
(4)、按“读取课程”,lesson.txt中的课程信息在右侧列表中显示
基于C++MFC的学生成绩管理系统基于C++MFC的学生成绩管理系统
(5)、按“读取成绩”,grade.txt中的课程信息在下方列表中显示
基于C++MFC的学生成绩管理系统基于C++MFC的学生成绩管理系统
(6)、选择“计算绩点”,子界面弹出
基于C++MFC的学生成绩管理系统
选择“升序排列”或“降序排列”后,按“排序”,列表中信息按排序后的顺序显示
基于C++MFC的学生成绩管理系统基于C++MFC的学生成绩管理系统
排序后,按“保存”(以升序排列为例),提示保存成功
基于C++MFC的学生成绩管理系统
此时打开rank.txt,发现信息已存入
基于C++MFC的学生成绩管理系统
(7)、选择“查询”,出现温馨提示
基于C++MFC的学生成绩管理系统
点击“确定”,子界面弹出
基于C++MFC的学生成绩管理系统
输入学生学号或姓名,按“查询”,此学生成绩在下方列表中显示
基于C++MFC的学生成绩管理系统基于C++MFC的学生成绩管理系统
(8)、双击要修改的成绩,弹出编辑框进行修改
基于C++MFC的学生成绩管理系统
修改后单击其它地方,列表中学生成绩信息更新
基于C++MFC的学生成绩管理系统
按“修改”,弹出成功提示
基于C++MFC的学生成绩管理系统
(9)、按“保存”,弹出温馨提示、成功提示
基于C++MFC的学生成绩管理系统基于C++MFC的学生成绩管理系统
再次打开grade.txt,发现信息已同步修改
基于C++MFC的学生成绩管理系统
(10)、按“退出”,退出系统
基于C++MFC的学生成绩管理系统
(画外音:测试数据都是自己编哒,然后数据量比较小hhh,可以自行多搞些数据康康效果~)

参考博文:
[1]:CSDN 《VC 双击编辑ListControl中的数据》 :原文地址