目录

摘要

实验重点

程序流程图

数据库准备

本地SQL Server建表

远程MySQL建表

界面开发

登陆对话框

1. 为对话框添加图标

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

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

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

小结

主对话框

学生管理窗口

各控件初始化

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

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

插入数据的代码实现

删除数据的代码实现

修改数据的代码实现

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

搜索的代码实现

清空编辑框和显示所有

总结

完整项目


摘要

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

实验重点

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

2. MFC基础控件的使用

程序流程图

数据库准备

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

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

本地SQL Server建表

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

          

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.课程号

结果

远程MySQL建表

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

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

3. 新建数据库和相应表

4. 插入原始数据并检查

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

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

界面开发

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

登陆对话框

密码编辑框为password属性

值得一提的有: 

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();

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

小结

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

主对话框

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

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

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

学生管理窗口

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

右侧列表为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);// 为列表插入表头

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

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

重头戏,ODBC操作数据库

1. 先配置数据源

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

添加数据源(MySQL为例)

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

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

这就算配置成功了

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();
}

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

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

这里先用类向导生成一个消息响应函数(列表被点击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

MFC ODBC 学生成绩管理系统 示例相关推荐

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

    1.题目要求 学期末,班级要统计该学期考试成绩,计算每个人的学分绩点,并按照学分绩点.平均分进行排序.假设本学期开设的课程共有n门课程,每门课程均有名称.学分数:学生考试成绩的学分绩点根据考试成绩核对 ...

  2. MFC课程设计 --学生成绩管理系统

    MFC课程设计 ,C++课程设计 --学生成绩管理系统 ps:因为课设完成的过程大家都不太一样,以下的代码仅供学习一下在MFC下各个控件的用法,有问题欢迎留言讨论. 实验目的 使用MFC类库编制应用程 ...

  3. MFC超市商品管理系统学生成绩管理系统学生信息管理系统通讯录管理系统图书管理系统

    MFC超市商品管理系统学生成绩管理系统学生信息管理系统通讯录管理系统图书管理系统 序号 题目 数组保存数据 文件保存数据 数据库保存数据 1 超市商品管理系统 2 学生成绩管理系统 3 学生信息管理系 ...

  4. (MFC)广州大学大一下课程设计实验报告-学生成绩管理系统

    程序设计 课程设计实验报告 学院: 计算机科学与网络工程学院 专业班级: XXX 姓名: XXX 学号: 190XXXXXXX 指导老师:张艳玲 2020.6.29 目录 一.课程设计题目及内容 二. ...

  5. 广州大学MFC实验报告——学生成绩管理系统

    资料下载 实验报告的压缩包(操作txt文件,可以看到文字): 下载链接 课程设计的压缩包(操作dat文件,只能看到乱码): 下载链接 这里更新的操作方法主要是最快最方便的.内容都在课程设计压缩包里. ...

  6. MFC学生成绩管理系统

    MFC学生成绩管理系统 程序使用MFC框架编写,开发工具是VC6.0. 要求完成以下功能: 添加学生信息(学生信息包含学号.姓名.班级.密码.各科成绩等): 删除学生信息(指定学号删除): 修改学生信 ...

  7. 哈工大慕课 学生成绩管理系统V1.0~5.0

    文章目录 学生成绩管理系统V1.0 学生成绩管理系统V2.0 学生成绩管理系统V3.0 学生成绩管理系统V4.0 学生成绩管理系统V5.0 本文提供测试数据哦~ 运行时请将数据粘贴到输入框中. 由于自 ...

  8. 第12章实验1:学生成绩管理系统V5.0(c语言)

    第12章实验1:学生成绩管理系统V5.0 某班有最多不超过30人(具体人数由键盘输入)参加期末考试,最多不超过6门(具体门数由键盘输入).参考学生成绩管理系统V4.0,定义结构体类型,用结构体数组作函 ...

  9. 第12章实验1:学生成绩管理系统V5.0

    第12章实验1:学生成绩管理系统V5.0 第12章实验1:学生成绩管理系统V5.0 某班有最多不超过30人(具体人数由键盘输入)参加期末考试,最多不超过6门(具体门数由键盘输入).参考学生成绩管理系统 ...

最新文章

  1. appium1.6在mac上环境搭建启动ios模拟器上Safari浏览器 转自:上海-悠悠
  2. 开始报名丨CCF C³-05@亚马逊云科技:未来云计算之旅
  3. 阿里云IoT何云飞:智物Cloud AIoT Native 为何能让设备智能更快一步
  4. 烟袋斜街-后海,印象已模糊
  5. CTS(23)---Camera Media CTS GTS VTS 记录
  6. CI/CD——适合你吗?
  7. Linux C 实现改变输出字的颜色。
  8. 创 业 项 目 计 划 书 样 本
  9. 小米笔记本pro 双硬盘双系统 opencore引导安装黑苹果
  10. Word中无法插入公式的解决方案
  11. 帮我写一个无数爱心滑落的html
  12. 贝塞尔曲线最小二乘法拟合(随意切向/切向方向统一)------路适用于绝大多数的最小二乘法拟合
  13. 使用css形变实现一个立方体
  14. pysot-toolkit--eval.py笔记(读取算法结果,根据评价指标计算结果并可视化)
  15. 安装redis时被意外攻击 newinit
  16. 调用企业微信API,ios端异常
  17. 端午假期整理了仿天猫H5 APP项目vue.js+express+mongo
  18. 计算机硬件加速怎么开,启用硬件加速是什么 是如何进行的【详解】
  19. 绕过tp路由器管理密码_怎么用手机设置路由器?TPlink无线路由器安装设置方法...
  20. 图(Graph),也称网络(Network)

热门文章

  1. 【正点原子FPGA连载】 第二十九章TFT LCD画板实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
  2. 关于傅里叶分析与香农采样定理
  3. php爬虫小说网站源码,基于php的cli模式小说爬虫案例
  4. 民间秘方,里面的方子都是一个老中医几十年的心血~~很强~~身体不好一定要看
  5. 2021年消防工程师证报考条件是什么?
  6. 【第9期】自动驾驶出租车到底“破坏力”几何?麦肯锡这份报告告诉你...
  7. php中smarty模板的优点,php的smarty模板引擎有哪些特点
  8. Debian7.1下Broadcom 4312无线网卡驱动安装
  9. php 银行卡,PHP实现根据银行卡号判断银行,php银行卡判断银行_PHP教程
  10. Winrar命令行解压带密码的压缩文件