Harbin Institute of Technology at Weihai
VC++课程设计报告
设计题目: 班级通讯录
院 系: 计算机科学与技术学院
班 级: 0904202
学 号: 090420202
设 计 者: 邢欣
哈尔滨工业大学(威海)
二零##年十二月
哈尔滨工业大学(威海)计算机学院
《VC++课程设计》验收及成绩评定表
哈尔滨工业大学(威海)课程设计任务书
目录
1 需求分析... 1
1.1 设计目的与背景... 1
1.2 功能概述... 1
2 开发环境... 1
3 程序设计... 2
3.1 程序结构... 2
3.2 数据库结构... 2
3.3 主要的困难... 3
4 功能实现... 3
4.1 涉及的主要技术... 3
4.2 主界面及浏览模块... 4
4.3 查询模块... 4
4.4 登录模块... 6
4.5 管理模块... 8
4.6 其他... 11
5 程序测试及运行界面... 13
5.1 主界面... 13
5.2 查询界面... 13
5.3 登录界面... 14
5.4 管理界面... 16
6 自我评价及总结... 17
7 参考资料... 19
“班级通讯录”设计报告
1 需求分析
1.1 设计目的与背景
人与人之间的联系不断加强,通讯录在日常生活中也发挥着越来越重要的作用。建立一个功能完备的通讯录,可以极大的方便我们对大量的联系人信息的管理,实现高效的添加、查找、修改、删除。通过数据库保存个人信息,并对信息的修改设置管理员权限,保证的信息的安全性。
同时,通过这次课程设计,还可以达到以下目的:巩固本学期所学的C++知识,学习并联系MFC的基本知识,学会简单的windows程序设计,练习对版本控制工具的使用
1.2 功能概述
本程序主要面向两类用户。
普通用户可以实现对通讯录所有内容的查看与搜索(提供按学号、按姓名两种方式)。
管理员用户除可使用普通用户可使用的功能外,还可以实现对用户信息的增加、删除、修改操作。同时,程序提供简单的音乐播放功能,用户可控制程序对内置音乐的播放与停止。
2 开发环境
操作系统:Microsoft Windows7旗舰版
开发环境:Microsoft Visual C++6.0中文版
数据库:Microsoft Office Access 2007
版本控制工具:TortoiseSVN 1.6.5及Subversion 1.6.13
3 程序设计
3.1 程序结构
本程序的结构如下图:
3.2 数据库结构
数据库结构如下表:
表 3?1 student
表 3?2 login
3.3 主要的困难
困难:只学习过C++的基础知识,对MFC程序设计不了解,对数据库的连接更是不了解。
解决方法:对照书本学习,利用MSDN学习,利用网络上的资源学习,向周围的人请教。
4 功能实现
4.1 涉及的主要技术
本程序采用了MFC方式进行编程,ODBC方式连接数据库,SVN方式进行开发过程中的版本控制。
4.2 主界面及浏览模块
采用标准Windows窗体,在主界面使用DBGrid和RemoteDateCtrl这两个ActiveX控件可以方便的显示联系人信息。同时,主界面提供“查询”“管理员登录”“退出”三个按钮。
关键代码如下:
//以模态形式显示“登录”对话框
void CAddressBookView::OnButtonLogin()
{
// TODO: Add your control notification handler code here
CLoginDlg dlg;
if(dlg.DoModal()==IDOK)
{
CManageDlg mdlg;
if(mdlg.DoModal()==IDOK)
OnPaint();//重绘主窗体
}
}
//以非模态形式显示“查找”对话框
void CAddressBookView::OnButtonSearch()
{
// TODO: Add your control notification handler code here
//因为CSearchView是基于CView类,CView类是基于CWnd类,不可以使用CDialog::Create()
CDialog *pdlg;
pdlg=(CDialog*)new CDialog;
pdlg->Create(IDD_SEARCH);
pdlg->ShowWindow(SW_SHOWNORMAL);
}
4.3 查询模块
利用好MFC中CRecordSet类的成员变量m_strFilter、m_strSort,成员函数Open,即可实现查询功能。
对需要实现查找功能的窗体,设置一个CRecordSet的派生类,选择好数据源,在将窗体类的基类设置为CDialog,在其声明中增加一个成员变量mSet。在需要进行查找时,先关闭mSet,设置好mSet.m_strFilter和mSet.m_strSort,执行mSet.Open,即可得到查询结果。结合CRecordSet::MoveNext()和CRecordSet::MovePrev()函数用CRecordSet.IsEOF()和CRecordSet.IsBOF()进行判断,即可实现对多查询结果的浏览。
将mSet.strSort分别设置为学号和姓名,即可实现对学号和姓名的查询。
将“按学号”一栏的Tab键顺序设为1,即可实现对它的默认选定状态。
在本模块中,本计划使用CRecordView类,使用它的指针m_pSet可以比较方便的实现查询结果的转换,但是实际操作失败。在尝试了利用指针转换的方式打开窗口后,窗口可以正常显示,但是什么功能都无法实现。通过网络搜索,可知其他人也出现了类似的结果。原因未知。
成功后的关键代码如下:
//执行查询操作的代码
void CSearchDlg::OnButtonSearch()
{
// TODO: Add your control notification handler code here
UpdateData(TRUE);
CSearchSet mSet;
int nID=GetCheckedRadioButton(IDC_RADIO_STUNUM,IDC_RADIO_STUNAM);
m_strSearch.TrimLeft();
if (m_strSearch.IsEmpty())//未输入数据,时,按下“搜索”按钮无效
{
return;
}
//if (nID==0)//未选择查询类型时,按下“搜索”按钮无效
//{
// return;
//}
if (mSet.IsOpen())
{
mSet.Close();
}
if (nID==IDC_RADIO_STUNAM)
{
mSet.m_strFilter.Format("stunam='%s'",m_strSearch);
mSet.m_strSort="stunum";
}else
{
mSet.m_strFilter.Format("stunum='%s'",m_strSearch);
mSet.m_strSort="stunam";
}
mSet.Open();
if (mSet.IsEOF())
{
MessageBox("没有你要找的对象");
}
else
{
//mSet
m_strStunum=mSet.m_stunum;
m_strStunam=mSet.m_stunam;
m_nStuage=mSet.m_stuage;
m_strCellnum=mSet.m_cellnum;
m_strStuadd=mSet.m_stuadd;
m_strStutel=mSet.m_stutel;
m_strStuqq=mSet.m_stuqq;
m_strStuem=mSet.m_stuem;
UpdateData(FALSE);//将查询结果在编辑栏中显示出来
nstate=1;
}
}
//关闭非模态窗口
void CSearchDlg::OnOK()
{
// TODO: Add extra validation here
DestroyWindow();
delete this;
}
4.4 登录模块
通过对登录框所属类添加成员变量,实现对用户的错误输入次数的记录,从而可以实现用户三次输入错误时退出登录界面。
通过对登录对话框关闭方式的判断(IDOK或IDCANCEL),主程序可以决定是否开启用户信息管理模块。
关键代码如下:
//用户输入错误时重建登录对话框,连续3次输入错误,则退出循环
void CLoginDlg::OnOK()
{
// TODO: Add extra validation here
CString strtemp;//该变量用于存放错误提示信息
m_nCount++;
UpdateData(TRUE);
CLoginSet m_Set;
if(m_Set.IsOpen())
m_Set.Close();//在进行查找操作之前先关闭记录集
m_Set.m_strFilter.Format("usernam='%s' AND userpass='%s'",m_strUsername,m_strPassword);//设置查询条件
m_Set.Open();//查找记录表login
if(m_Set.IsEOF())//对于没有找到记录时的处理。这里按照剩余输入次数分了两种处理方法
{
m_Set.Close();
if(m_nCount<3)
{
strtemp.Format("您的输入有误!\n您还有%d次机会,请重新输入!",3-m_nCount);
MessageBox(strtemp,"出错啦!",MB_ICONERROR);//MessageBox的题目为“出错啦!”,图标为ERROR
m_strUsername.Empty();
m_strPassword.Empty();
UpdateData(FALSE);
}
else
{
strtemp.Format("对不起,您的错误次数已达最大限制!");
MessageBox(strtemp,"出错啦!",MB_ICONERROR);
CDialog::OnCancel(); //这里不是OnOK()
}
}
else//如果找到了记录,
{
strtemp.Format("%s,欢迎回来!",m_strUsername);
MessageBox(strtemp,"登录成功!");
CDialog::OnOK();
}
}
//关闭窗口
void CLoginDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default
CDialog::OnClose();
}
4.5 管理模块
管理模块可在查询模块的基础上实现。正确利用CRecordSet::AddNew(),CRecordSet::Delete(),CRecordSet::Edit(),CRecordSet::Update()函数,即可实现对用户信息的增删改操作。
需要注意的是,在进行增加和修改操作之后,必须手动调用CRecordSet::Update()函数才可实现其功能。
在管理对话框所属类中增加一个成员变量ing nstate,以记录当前是否已有联系人信息显示。
关键代码如下:
//增加一个联系人
void CManageDlg::OnButtonAdd()
{
// TODO: Add your control notification handler code here
CEditDlg dlg;
if(dlg.DoModal()==IDOK)
{
if(!mSet.IsOpen())
mSet.Open();
mSet.AddNew();
mSet.m_stunum =dlg.m_strStunum;
mSet.m_stunam =dlg.m_strStunam;
mSet.m_stuage =dlg.m_nStuage;
mSet.m_cellnum =dlg.m_strCellnum;
mSet.m_stuadd =dlg.m_strStuadd;
mSet.m_stutel =dlg.m_strStutel;
mSet.m_stuqq =dlg.m_strStuqq;
mSet.m_stuem =dlg.m_strStuem;
mSet.Update();
mSet.Requery();
}
m_strStunum =dlg.m_strStunum;
m_strStunam =dlg.m_strStunam;
m_nStuage =dlg.m_nStuage;
m_strCellnum=dlg.m_strCellnum;
m_strStuadd =dlg.m_strStuadd;
m_strStutel =dlg.m_strStutel;
m_strStuqq =dlg.m_strStuqq;
m_strStuem =dlg.m_strStuem;
UpdateData(FALSE);//在编辑栏中显示出联系人信息
nstate=1;
}
//编辑联系人信息
void CManageDlg::OnButtonEdit()
{
// TODO: Add your control notification handler code here
if (!nstate)//当前没有记录显示,不执行编辑功能
{
MessageBox("请先选择一个联系人");
return;
}
CEditDlg dlg;
dlg.m_strStunum =m_strStunum;
dlg.m_strStunam =m_strStunam;
dlg.m_nStuage =m_nStuage;
dlg.m_strCellnum=m_strCellnum;
dlg.m_strStuadd =m_strStuadd;
dlg.m_strStutel =m_strStutel;
dlg.m_strStuqq =m_strStuqq;
dlg.m_strStuem =m_strStuem;
if(dlg.DoModal()==IDOK)
{
mSet.Edit();
mSet.m_stunum=dlg.m_strStunum;
mSet.m_stunam=dlg.m_strStunam;
mSet.m_stuage=dlg.m_nStuage;
mSet.m_cellnum=dlg.m_strCellnum;
mSet.m_stuadd=dlg.m_strStuadd;
mSet.m_stutel=dlg.m_strStutel;
mSet.m_stuqq=dlg.m_strStuqq;
mSet.m_stuem=dlg.m_strStuem;
mSet.Update();
UpdateData(FALSE);
}
}
//删除一个联系人
void CManageDlg::OnButtonDelete()
{
// TODO: Add your control notification handler code here
if (!nstate)//当前没有记录显示,不执行删除功能
{
MessageBox("请先选择一个联系人!");
return;
}
CRecordsetStatus status;
mSet.GetStatus(status);
mSet.Delete();
if(status.m_lCurrentRecord==0)
mSet.MoveNext();
else
mSet.MoveFirst();
UpdateData(FALSE);
nstate=0;
}
//查找上一条记录
void CManageDlg::OnButtonPrev()
{
// TODO: Add your control notification handler code here
if (!nstate)//当前没有记录显示,不执行查找上一条功能
{
MessageBox("请先选择一个联系人!");
return;
}
if(!mSet.IsBOF())
{
mSet.MovePrev();
m_strStunum=mSet.m_stunum;
m_strStunum=mSet.m_stunum;
m_strStunam=mSet.m_stunam;
m_nStuage=mSet.m_stuage;
m_strCellnum=mSet.m_cellnum;
m_strStuadd=mSet.m_stuadd;
m_strStutel=mSet.m_stutel;
m_strStuqq=mSet.m_stuqq;
m_strStuem=mSet.m_stuem;
UpdateData(FALSE);
}
else{
return;
}
}
//查找下一条记录
void CManageDlg::OnButtonNext()
{
// TODO: Add your control notification handler code here
if (!nstate)//当前没有记录显示,不执行查找下一条功能
{
MessageBox("请先选择一个联系人!");
return;
}
if(!mSet.IsEOF())
{
mSet.MoveNext();
m_strStunum=mSet.m_stunum;
m_strStunum=mSet.m_stunum;
m_strStunam=mSet.m_stunam;
m_nStuage=mSet.m_stuage;
m_strCellnum=mSet.m_cellnum;
m_strStuadd=mSet.m_stuadd;
m_strStutel=mSet.m_stutel;
m_strStuqq=mSet.m_stuqq;
m_strStuem=mSet.m_stuem;
UpdateData(FALSE);
}
else{
return;
}
}
4.6 其他
本程序在主界面在工具栏和控制栏中提供对音乐播放功能的控制。
对于音乐播放,使用了PlaySound()函数,在头文件中添加
#include "mmsystem.h"
#pragma comment(lib,"winmm.lib")
为了实现音乐播放按钮与菜单的一致性,将菜单与控制栏按钮设置为相同的ID。在其对应类CMainFrame中添加如下代码:
//音乐播放
void CMainFrame::OnEditPlay()
{
// TODO: Add your command handler code here
PlaySound(".\\song.wav",NULL,SND_FILENAME|SND_ASYNC|SND_LOOP);//使用了相对路径
m_bMainWork=FALSE;
}
//音乐停止
void CMainFrame::OnEditStop()
{
// TODO: Add your command handler code here
PlaySound("x.x",NULL,SND_FILENAME|SND_ASYNC);//这里不用LOOP
m_bMainWork=TRUE;
}
void CMainFrame::OnUpdateEditPlay(CCmdUI* pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable(TRUE);
pCmdUI->SetCheck(!m_bMainWork);
}
void CMainFrame::OnUpdateEditStop(CCmdUI* pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable(TRUE);
pCmdUI->SetCheck(m_bMainWork);
}
5 程序测试及运行界面
5.1 主界面
图 5?1 主界面
5.2 查询界面
图 5?2 查询界面
5.3 登录界面
图 5?3 登录界面
图 5?4 第一次登录失败
图 5?5 第二次登录失败
图 5?6 第三次登录失败
图 5?7 登录成功
5.4 管理界面
图 5?8 管理界面
图 5?9 添加/修改学生信息
图 5?10 删除/修改不存在的联系人
1.1
6 自我评价及总结
本程序的主要特色:
1. 对用户的角色进行了分类
2. 程序中添加了音乐播放功能
3. 原创程度高
通过这次课程设计,我自己动手完成了第一个较为完整的MFC程序。我的主要收获如下:
1. 短时间内学习了许多新知识
2. 熟悉了MFC的基本操作和功能实现。对一些常用类和函数的继承关系有了大体上的认识。在实际编程中,我需要反反复复地查阅《MFC类库详解》和MSDN,这些概念之间的关系的建立虽然缓慢,但是经过了一遍遍的巩固,也算是印象比较深刻了。
3. 对数据库有了初步的认识。之前我只是在高中时接触过一点关于FoxPro数据库的建立方法,跟本次课程设计基本上没有什么联系。关于数据库的调用,完全是现学现用。
4. 熟悉了VC++进行MFC编程的基本方法
5. 学会了用ODBC方式调用数据库的基本方法
6. 对C++中的一些概念有了更深层次的理解,特别是继承、多态和虚函数
7. 学会了利用MSDN这一强大的工具进行学习
8. 习惯了阅读一些简单的英文资料,比如MSDN,对一些计算机专业中常见的词语更熟悉了。这是在本次课程设计之前万万没有想到的
9. 第一次体会到了版本控制工具的带来的好处,在我的代码出现问题的时候,为我提供了退路。
我觉得,在以下几个方面,我还可以做的更好:
1. 可以在程序中添加一键发送电子邮件的功能
2. 可以利用QQ的临时会话功能,提供一键聊天的服务
3. 程序的界面粗糙,还可以继续美化
4. 我的学习效率有点低,还可以继续提高
5. 遇到问题的时候总是习惯自己一个人琢磨,尝试。虽然这样可以经常有意外的收获,但是对于课程设计这种时限性较强的任务,我的做法似乎有些欠妥。
课程设计的过程也是一个在短时间内大量学习新知识的过程,有压力,也有效率。或许我的效率比别人低,但是我可以问心无愧的说,在这一周中,我真的努力了,我的程序中除了由Visual C++6.0程序自动生成的代码,其余的都是自己一个字母一个字母敲出来的,原创程度99.99%。
程序的界面确实有点粗糙,但是我认为功能第一,界面的美观程度只能排第二。在我的功能还没有完全实现之前,实在是无法顾及太多界面美观的问题。
在自己刚刚开始接触一些陌生的知识时候,有一本合适的教材实在是太重要了。要能够把细节讲清楚,从最基本的概念讲起,屏蔽掉比较深的内容,有一步步的具体指导。否则,要完成的任务就只是一个高高在上的跨栏,除非有非常强的理解力,要想跨过去实在是太难了。
感受最深的,还是关于自己的学习效率。总是觉得晚上比白天的效率更高一些。生活作息基本处于混乱状态。时间足够,效率不高。在寻找问题的解决方法时,总是“众里寻他千百度,那人却在灯火阑珊处”。这些都是很无奈的事情,我目前也没有什么好的解决方法。
总之,这次设计提高了自己的动手实践能力,加深了对于专业课程的领悟,感触颇多,受益匪浅。
7 参考资料
1. 《Visual C++教程》,郑阿奇主编,机械工业出版社
2. 《C++大学基础教程》,徐惠民主编,人民邮电出版社
3. 《MFC类库详解》电子书
4. MSDN