MFC ODBC 学生成绩管理系统 示例

目录

摘要

实验重点

程序流程图

数据库准备

本地SQL Server建表

远程MySQL建表

界面开发

登陆对话框

1. 为对话框添加图标

2. 窗体打开时自动定位焦点到第一个编辑框

3. 登录验证的具体代码实现

4. 在主窗体前弹出的功能实现

小结

主对话框

学生管理窗口

各控件初始化

List Control从数据库获取数据并显示

点击列表项自动填充到右侧编辑框中

插入数据的代码实现

删除数据的代码实现

修改数据的代码实现

排序(按生日)的代码实现

搜索的代码实现

清空编辑框和显示所有

总结

完整项目


摘要

本文章是展示本人《C++实践》课程中 MFC学生信息管理系统 的示例,基础性教程,详细地展示如何一步步开发一个基础功能的 MFC学生管理系统。

实验重点

1. 本地的ODBC数据库连接机简单使用

2. MFC基础控件的使用

程序流程图

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_20,color_FFFFFF,t_70,g_se,x_16

数据库准备

本示例中用到三张表,分别是:学生表(Stu)、科目表(Course)、成绩表(Score)

这里展示在MySQLSQL Server两种数据库中的建表过程

本地SQL Server建表

打开SSMS,新建查询,建立一个新的数据库SMSDB用于本实验

 1c2a623364254b3fa10f34487f48e0f6.png          watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_20,color_FFFFFF,t_70,g_se,x_16

CREATE DATABASE SMSDB
use SMSDB
/*
新建并使用数据SMSDB
*/

新建三张表,注意这里有几处约束以及主外键的指定

CREATE TABLE Stu(
学号 NCHAR(10) PRIMARY KEY NOT NULL,
姓名 NCHAR(5),
专业 NCHAR(20) NOT NULL,
性别 NCHAR(2) default '男' check (性别 in ('男','女'))NOT NULL,
出生日期 DATE NOT NULL
);

CREATE TABLE Course(
课程号 NCHAR(10) PRIMARY KEY NOT NULL,
课程名称 NCHAR(20) NOT NULL,
学分 SMALLINT NOT NULL
);

CREATE TABLE Score(
学号 NCHAR(10) FOREIGN KEY REFERENCES Stu(学号) NOT NULL,
课程号 NCHAR(10) FOREIGN KEY REFERENCES Course(课程号) NOT NULL,
成绩 SMALLINT CHECK(成绩>=0 AND 成绩<=100) NOT NULL
);

/*
新建三张表
*/

插入原始示例数据(每张表各插两条)

INSERT INTO Stu VALUES(312021001,'张三','计算机科学与技术','男','2000-1-12')
INSERT INTO Stu VALUES(312020001,'aaa','建筑环境与能源应用','女','1999-4-26')

INSERT INTO Course VALUES('9001','数据结构与算法',2.0);
INSERT INTO Course VALUES('9002','线性代数',1.0);


INSERT INTO Score VALUES(312021001,9001,87);
INSERT INTO Score VALUES(312020001,9002,58);

检查原始数据

SELECT Stu.学号,姓名,性别,出生日期,专业,Course.课程号,课程名称,学分,成绩 FROM Stu,Course,Score
WHERE Stu.学号=Score.学号 AND Course.课程号=Score.课程号

结果

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_20,color_FFFFFF,t_70,g_se,x_16

远程MySQL建表

1. Putty远程登陆到服务器并启动MySQL

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_20,color_FFFFFF,t_70,g_se,x_16

2. 为MySQL添加一个远程登录用户,并为之授权(这里其实有些步骤的,就没展开讲了)

3. 新建数据库和相应表

4. 插入原始数据并检查

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_20,color_FFFFFF,t_70,g_se,x_16

(其实直接用cmd也可以,新版的Terminal还很漂亮)

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_20,color_FFFFFF,t_70,g_se,x_16

 关于本地MySQL建表,其实上面的SQL语句也有了,实现并不困难,就略过

界面开发

本项目中是基于对话框的MFC项目,共包含5个主要的对话框:主对话框、登录对话框(LoginDlg)、学生管理对话框(StuDlg)、科目管理对话框(CourseDlg)、成绩管理对话框(ScoreDlg)

登陆对话框

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_18,color_FFFFFF,t_70,g_se,x_16

 密码编辑框为password属性

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_13,color_FFFFFF,t_70,g_se,x_16

值得一提的有: 

1. 为对话框添加图标

先在头文件中定义一个变量

HICON m_hIcon;

然后在窗体对应的cpp文件的构造函数中添加(IDI_ICON2为添加的ICON资源文件ID)

m_hIcon = AfxGetApp()->LoadIcon(IDI_ICON2);

最后在窗体的OnInitDlg()函数中添加(没有这个函数可以通过类向导添加此虚函数)

	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

2. 窗体打开时自动定位焦点到第一个编辑框

	// GetDlgItem(USERNAME)->SetFocus();
	// 不知道为什么没用
	// 哈哈哈哈,ctrl+D设置tab order为1获取焦点,简直简单粗暴

这里设置tad order还能方便有使用Tab键切换编辑项习惯的用户使用

3. 登录验证的具体代码实现

“登录”按钮的消息响应函数

void Login::OnBnClickedOk()
{
	// 对用户输入的账号密码进行验证
	GetDlgItemText(USERNAME, m_username);
	GetDlgItemText(PASSWORD, m_password);
	if (m_username==L"" || m_password==L"")
	{
		MessageBox(L"用户名 或 密码 不能为空!",L"注意");
		GetDlgItem(USERNAME)->SetFocus();
	}else if (m_username.Compare(username)!=0 || m_password.Compare(password)!=0)
	{
		MessageBox(L"用户名 或 密码 有误,请重新输入!", L"注意");
		SetDlgItemText(USERNAME, L"");
		SetDlgItemText(PASSWORD, L"");
		GetDlgItem(USERNAME)->SetFocus();
	}
	else if (m_username.Compare(username) == 0 || m_password.Compare(password) == 0)
	{
		CDialogEx::OnOK();
	}
}

“取消”按钮的消息响应函数

void Login::OnBnClickedCancel()
{
	exit(0);
	// CDialogEx::OnCancel();
}

其实这一句还挺重要,点击“红叉”也会调用OnCancel()函数,确保了用户不管是点“退出”还是“红叉”都会直接结束程序,确保程序只有验证通过一个入口

4. 在主窗体前弹出的功能实现

	Login login;
	login.DoModal();

在主对话框的初始化函数最前面弹一个模态对话框

小结

本例中正确的“用户名”和“密码”都是在“头文件”中写死了的,事实上本窗体还有很多可以拓展的点,例如:绘制窗口背景、选择登录类型、记住账号密码、账号密码信息从数据库进行验证

主对话框

左上三个按钮分别为三个子对话框的入口,实现也是点击即创建一个模态对话框

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_20,color_FFFFFF,t_70,g_se,x_16

 值得注意的是List Control控件中的数据是来自先前建立的三张表的集合,在这里仅作展示,而三个子模块则本别实现对每张表的操作

本窗口的代码实现均留到后面的“学生管理”模块详解

学生管理窗口

学生管理窗口是本程序中最重要的窗体(科目和成绩管理其实没写)

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_20,color_FFFFFF,t_70,g_se,x_16

右侧列表为List Control,性别是两个Radio Button,出生日期为Date Time Picker,专业为Combo Box

接下来依次讲解实现

各控件初始化

在窗体的初始化函数OnInitDlg中接着写

	Profess.AddString(L"计算机科学与技术");
	Profess.AddString(L"道路与桥梁工程");
	Profess.AddString(L"文化与新闻传播");
	Profess.AddString(L"物流与交通运输");
	Profess.AddString(L"理论物理");
	// Profess为专业下拉框绑定的控件变量,以上代码为下拉框中添加字段

	stu_list.SetExtendedStyle(LVS_EX_CHECKBOXES);
	// stu_list为列表绑定的控件变量,这一句是设置list风格:最前面带可以打勾的复选框

	stu_list.InsertColumn(0, _T("学号"), 0, 125);
	stu_list.InsertColumn(1, _T("姓名"), 0, 80);
	stu_list.InsertColumn(2, _T("专业"), 0, 240);
	stu_list.InsertColumn(3, _T("性别"), 0, 50);
	stu_list.InsertColumn(4, _T("出生日期"), 0, 120);
	// 为列表插入表头

效果截图(其实这时候列表中因该没数据的)

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_13,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_20,color_FFFFFF,t_70,g_se,x_16

List Control从数据库获取数据并显示

重头戏,ODBC操作数据库

1. 先配置数据源

Win+S搜索ODBC数据源(没有的话要安装,这里不展开,可以参考其他博文)

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_20,color_FFFFFF,t_70,g_se,x_16

 添加数据源(MySQL为例)

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_20,color_FFFFFF,t_70,g_se,x_16

 需要填写的字段从上至下依次为:数据源名(随意)、数据库主机IP地址(本地为127.0.0.1)、端口(MySQL是3306)、MySQL用户名、MySQL用户密码、指定数据库

填完了可以点击“Test”按钮测试一下

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAWWFvc18=,size_16,color_FFFFFF,t_70,g_se,x_16

 这就算配置成功了

2. 在头文件中定义

	CDatabase m_db;

在初始化函数中初始化数据库连接(这里给的是远程连接MySQL的语句,本地连接SQL Server的注释掉了)

	try
	{
		// m_db.Open(NULL);//弹出数据源选择对话框
		// m_db.Open(NULL, FALSE, FALSE, _T("DSN=SQL_Server_ODBC;UID=sa;PWD=Ue!p41SQL;DATABASE=SMSDB"));
        // 本地连接SQL Server数据库
		// 还是ODBC的数据源

		m_db.Open(NULL, FALSE, FALSE, _T("DSN=MySQL_ODBC;UID=remote;PWD=MySQL.123;DATABASE=SMSDB"));
		// 远程连接MySQL数据库
	}
	catch (const CDBException& e)
	{
		MessageBox(e.m_strError);
	}

	InitList();// 单独写的刷新列表数据的函数

解析一下SQL连接字段:数据源名、数据库用户名、密码、指定使用的数据库

3. 从数据库获取查询到的数据,并插入到List中

// 刷新列表数据
void StuManageDlg::InitList() {

	stu_list.DeleteAllItems();// 清空列表控件上的旧数据

	// 数据集对象,传入连接对象
	CRecordset rs(&m_db);
	BOOL  ret = rs.Open(AFX_DB_USE_DEFAULT_TYPE, _T("SELECT * FROM Stu"));
	int row = 0;

	CString id, name, sex, birth, profess;
	while (!rs.IsEOF())
	{
		//获取数据集中的字段数据
		rs.GetFieldValue((short)0, id);
		rs.GetFieldValue((short)1, name);
		rs.GetFieldValue((short)2, sex);
		rs.GetFieldValue((short)3, birth);
		rs.GetFieldValue((short)4, profess);

		//向列表控件中插入一行
		stu_list.InsertItem(row, id);
		stu_list.SetItemText(row, 1, name);
		stu_list.SetItemText(row, 2, sex);
		stu_list.SetItemText(row, 3, birth);
		stu_list.SetItemText(row, 4, profess);

		//移动下一行数据
		rs.MoveNext();
		row++;
	}

	//关闭记录集
	rs.Close();
}

点击列表项自动填充到右侧编辑框中

这么说可能不是很直观,插一段演示视频好了

cd45992acab14d239e479ab465ed07c6.gif

 这里先用类向导生成一个消息响应函数(列表被点击NM_Click事件),然后编辑代码

// 选中将记录插入到编辑框内
void StuManageDlg::OnClickList1(NMHDR* pNMHDR, LRESULT* pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);

	if (-1 != pNMItemActivate->iItem)        // 如果iItem不是-1,就说明有列表项被选择   
	{
		// 将选中的信息赋给选中变量(显示到对应的输入框中)
		stu_id = stu_list.GetItemText(pNMItemActivate->iItem, 0);
		stu_name = stu_list.GetItemText(pNMItemActivate->iItem, 1);
		profess = stu_list.GetItemText(pNMItemActivate->iItem, 2);
		CString stu_sex = stu_list.GetItemText(pNMItemActivate->iItem, 3);
		CString stu_bir = stu_list.GetItemText(pNMItemActivate->iItem, 4);

		// 清空按钮的选中状态,然后判断再选中
		((CButton*)GetDlgItem(MALE))->SetCheck(0);
		((CButton*)GetDlgItem(FEMALE))->SetCheck(0);
		if (stu_sex.Compare(L"男")==0)
		{
			((CButton*)GetDlgItem(MALE))->SetCheck(1);
			OnBnClickedMale();
		}
		else if (stu_sex.Compare(L"女") == 0)
		{
			((CButton*)GetDlgItem(FEMALE))->SetCheck(1);
			OnBnClickedFemale();
		}

		//解析生日字符串 然后初始化控件
		int iIndex = stu_bir.Find('-');

		int iReverseIndex = stu_bir.ReverseFind('-');

		CString strYear = stu_bir.Left(iIndex);
		CString strDay = stu_bir.Right(stu_bir.GetLength() - iReverseIndex - 1);
		CString strMonth = stu_bir.Mid(iIndex + 1, (iReverseIndex - iIndex - 1));

		COleDateTime time(_ttoi(strYear), _ttoi(strMonth), _ttoi(strDay), 0, 0, 0);
		Date.SetTime(time);//    CDateTimeCtrl m_DateCtrl;

		// 将变量值刷新到控件
		UpdateData(false);
	}

	*pResult = 0;
}

补两个性别按钮被点击事件

void StuManageDlg::OnBnClickedMale()
{
	stu_sex = L"男";
}

void StuManageDlg::OnBnClickedFemale()
{
	stu_sex = L"女";
}

插入数据的代码实现


// 插入数据
void StuManageDlg::OnBnClickedInsert()
{
	Profess.GetWindowTextW(profess);
	CTime t;
	Date.GetTime(t);
	stu_bir = t.Format("%Y-%m-%d");
	/*int nIndex = Profess.GetCurSel();
	Profess.GetLBText(nIndex, profess);*/

	//让控件的值同步到变量上
	UpdateData(true);

	if (stu_id.IsEmpty() || stu_name.IsEmpty() || stu_sex.IsEmpty()) {
		MessageBox(_T("插入的数据不能为空!"));
		return;
	}

	try {
		CString sql;
		sql.Format(_T("INSERT INTO Stu VALUES(%s,'%s','%s','%s','%s')"), stu_id, stu_name, profess,stu_sex,stu_bir);
		m_db.ExecuteSQL(sql);

		//刷新一下查询
		InitList();

		MessageBox(_T("插入数据成功!"));

	}
	catch (CDBException* e) {
		MessageBox(e->m_strError);
	}
}

删除数据的代码实现

// 删除信息
void StuManageDlg::OnBnClickedDeleteb()
{
	//让控件的值同步到变量上
	UpdateData(true);

	if (stu_id.IsEmpty()) {
		MessageBox(_T("学号不能为空!"));
		return;
	}

	try {
		CString sql;
		sql.Format(_T("DELETE FROM Stu WHERE 学号=%s"), stu_id);
		m_db.ExecuteSQL(sql);

		//刷新一下查询
		InitList();

		MessageBox(_T("删除数据成功!"));

	}
	catch (CDBException* e) {
		MessageBox(e->m_strError);
	}

	OnBnClickedClear();
}

修改数据的代码实现

// 修改信息
void StuManageDlg::OnBnClickedUpdate()
{
	Profess.GetWindowTextW(profess);
	CTime t;
	Date.GetTime(t);
	stu_bir = t.Format("%Y-%m-%d");
	CString new_id;
	GetDlgItemTextW(ID, new_id);
	GetDlgItemTextW(NAME, stu_name);
	//让控件的值同步到变量上
	UpdateData(true);

	if (stu_id.IsEmpty() || stu_name.IsEmpty() || profess.IsEmpty() || stu_sex.IsEmpty() || stu_bir.IsEmpty()) {
		MessageBox(_T("修改的数据不能为空!"));
		return;
	}

	if (stu_id != new_id)
	{
		MessageBox(_T("学号不能被修改!"));
		return;
	}

	try {
		CString sql;
		sql.Format(_T("UPDATE Stu SET 姓名='%s',专业='%s',性别='%s' ,出生日期='%s' WHERE 学号=%s "), stu_name, profess, stu_sex,stu_bir,stu_id);
		m_db.ExecuteSQL(sql);

		//刷新一下查询
		InitList();

		MessageBox(_T("修改数据成功!"));

	}
	catch (CDBException* e) {
		MessageBox(e->m_strError);
	}
}

排序(按生日)的代码实现

// 排序
void StuManageDlg::OnBnClickedSort()
{
	stu_list.DeleteAllItems();// 清空列表控件上的旧数据

// 数据集对象,传入连接对象
	CRecordset rs(&m_db);
	BOOL  ret = rs.Open(AFX_DB_USE_DEFAULT_TYPE, _T("SELECT * FROM Stu ORDER by 出生日期"));
	int row = 0;

	CString id, name, sex, birth, profess;
	while (!rs.IsEOF())
	{
		//获取数据集中的字段数据
		rs.GetFieldValue((short)0, id);
		rs.GetFieldValue((short)1, name);
		rs.GetFieldValue((short)2, sex);
		rs.GetFieldValue((short)3, birth);
		rs.GetFieldValue((short)4, profess);

		//向列表控件中插入一行
		stu_list.InsertItem(row, id);
		stu_list.SetItemText(row, 1, name);
		stu_list.SetItemText(row, 2, sex);
		stu_list.SetItemText(row, 3, birth);
		stu_list.SetItemText(row, 4, profess);

		//移动下一行数据
		rs.MoveNext();
		row++;
	}

	//关闭记录集
	rs.Close();
}

搜索的代码实现

// 搜索
void StuManageDlg::OnBnClickedSearch()
{
	GetDlgItemText(SearchInput, search_input);
	if (search_input == L"")
	{
		MessageBox(L"搜索项不能为空!",L"提示");
		GetDlgItem(SearchInput)->SetFocus();
	}
	else
	{
		stu_list.DeleteAllItems();// 清空列表控件上的旧数据
		CString sql = L"SELECT * FROM Stu WHERE 学号 LIKE '" + search_input + L"'";
		// 数据集对象,传入连接对象
		CRecordset rs(&m_db);
		BOOL  ret = rs.Open(AFX_DB_USE_DEFAULT_TYPE, sql);
		int row = 0;

		CString id, name, sex, birth, profess;
		while (!rs.IsEOF())
		{
			//获取数据集中的字段数据
			rs.GetFieldValue((short)0, id);
			rs.GetFieldValue((short)1, name);
			rs.GetFieldValue((short)2, sex);
			rs.GetFieldValue((short)3, birth);
			rs.GetFieldValue((short)4, profess);

			//向列表控件中插入一行
			stu_list.InsertItem(row, id);
			stu_list.SetItemText(row, 1, name);
			stu_list.SetItemText(row, 2, sex);
			stu_list.SetItemText(row, 3, birth);
			stu_list.SetItemText(row, 4, profess);

			//移动下一行数据
			rs.MoveNext();
			row++;
		}

		//关闭记录集
		rs.Close();

		// MessageBox(sql);

这里的排序和搜索都写得非常简单和单一

清空编辑框和显示所有

// 清空编辑框
void StuManageDlg::OnBnClickedClear()
{
	GetDlgItem(ID)->SetWindowText(L"");
	CWnd::SetDlgItemTextW(NAME, L"");
	Profess.SetWindowTextW(L"");

	((CButton*)GetDlgItem(MALE))->SetCheck(0);
	((CButton*)GetDlgItem(FEMALE))->SetCheck(0);

	COleDateTime Now = COleDateTime::GetCurrentTime();
	Date.SetTime(Now);
}
void StuManageDlg::OnBnClickedShow()
{
	InitList();
}

总结

当然,本项目仍旧很浅显、很基本,甚至说没能体现出MFC的精髓,但本身也作为学习者,记录沉淀也为后来的同学提供一个参考。

如有高见,抑或是讨论、问题,欢迎在评论区提出。

完整项目

当然,就算详详细细的写了这么多,为了更好的理解,实际跑一下还是必要的,所以在这里放出完整的项目文件夹供感兴趣的同学运行调试

注意,数据库在非本人主机上是没法访问的(本地数据源的配置),需要自行配置方能正常使用

MFC 简易学生成绩管理系统https://download.csdn.net/download/m0_53610390/83825115

CSDN资源下载挺坑的,这边补一个百度云盘链接

链接:https://pan.baidu.com/s/1B_pMeecw_7gD6WcQJ6dXvQ?pwd=yaos 
提取码:yaos

  • 9
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yaos_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值