📄 chap10_7.htm
字号:
<html>
<head>
<title>10.7 学习Enroll例程</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<meta name="GENERATOR" content="Microsoft FrontPage 3.0">
<link rel="stylesheet" href="../../../cpcw.css"></head>
<body link="#3973DE" alink="#3973DE" background="../../bg.gif">
<div align="center"><center>
<table width="85%" border="0">
<tr bgcolor="#FFFFFF">
<td>
<div align="center">
<center>
</center>
</div>
<p align="CENTER"><font color="red"><b>10.7 学习Enroll例程</b></font></p>
<p align="JUSTIFY"> Visual C++提供了一个名为Enroll的例子来作为学习MFC数据库编程的教程.Enroll分为四步,本节的任务就是指导读者完成前三步的Enroll例程,并对其进行较彻底的剖析.通过学习这三步例程,读者将掌握用AppWizard和ClassWizard创建MFC数据库应用程序的方法.</p>
<p align="JUSTIFY"> 在开始学习Enroll例程时,读者也许会感到用AppWizard创建数据库应用很容易,似乎不用学习前面几节的内容.诚然,AppWizard自动地为应用程序加入了许多与数据库有关的代码,大大简化了数据库应用的开发.但AppWizard不是万能的,它建立的数据库应用往往不能满足用户的需要.用户真正想知道的是如何不依赖AppWizard而编写自己的数据库应用程序,这也正是本章的宗旨所在.事实上,前面几节的分析以及后面进行的对Enroll例程的分析正是为这一宗旨服务的.</p>
<p align="JUSTIFY"> 在学习Enroll以前,请读者先从Visual
C++ 5.0的光盘上将Enroll(在samples
\ mfc \ tutorial \ enroll目录下)在前三步的例程拷到硬盘上,以供参考.另外,Enroll要用到Access数据库STDRED32.MDB,该文件在VC5.0的Stdreg例程中(在samples
\ mfc \ database \ stdreg目录下),请读者将该例子也拷贝到硬盘上.</p>
<p align="JUSTIFY"> <b></b>10.7.1 注册数据源</p>
<p align="JUSTIFY"> ODBC应用程序不能直接使用数据库,用户必需为要使用的数据库注册数据源.注册数据源的工作由ODBC管理器完成,该管理器位于Windows
95控制面板的32位ODBC内.现在让我们为Access数据库STDREG32.MDB注册数据源.</p>
<p align="JUSTIFY"> 打开控制面板,双击“32位ODBC”图标,则会显示一个“ODBC数据源管理器”,如图10.5所示。在管理器中选择“用户DSN”页,用户DSN只对用户可见而且只能用户当前机器。</p>
<p align="center"> <img src="T10_5.gif" alt="T10_5.tif (174104 bytes)" width="460" height="355"></p>
<p align="center"> 图10.5 ODBC数据源管理器</p>
<p align="JUSTIFY">点击“添加”按钮,则会弹出一个“创建新数据源”对话框。在该对话框中选择Microsoft
Access Driver(*.mdb),然后按完成按钮。接下来会显示一个ODBC
Microsoft Access 97 Setup对话框,如图10.6所示,该对话框用来把数据库与一个数据源名连接起来。在Data
Source Name:栏中输入Student
Registration,然后点击Select...按钮,在随后弹出的对话框中找到并选择STDREG32.MDB。连按两个OK按钮后,一个名为Student
Registration的新数据源就被注册到了管理器中。</p>
<p align="center"> <img src="T10_6.gif" alt="T10_6.tif (156780 bytes)" width="471" height="312"></p>
<p align="center"> 图10.6
ODBC Microsoft Access 97 Setup对话框</p>
<div align="center">
<center>
<table border="2" cellpadding="2" cellspacing="0" width="100%" bgcolor="#80D6FF">
<tr>
<td width="100%"> <b></b>提示:如果要为dBase或FoxPro数据库注册数据源,则应该选择一个目录而不是文件作为数据源。这是因为在Access中,一个MDB文件可以包含多张表,因此可以认为一个MDB文件就是一个数据库。而在dBase或FoxPro中,一个DBF文件只能对应一张表,一个数据库可能会包含好几个DBF文件,所以可以认为某一个包含了若干DBF文件的目录是一个数据库。<b></b></td>
</tr>
</table>
</center>
</div>
<p align="JUSTIFY">10.7.2 Enroll的第一个版本</p>
<p align="JUSTIFY"> Enroll第一个版本如图10.3所示,该程序具有浏览记录集和修改记录这两个基本功能。在修改了表单中的记录后,移动到一个新的记录上就可以保存对原记录的修改。注意在表单中Course和Section编辑框都是只读的,这是因为这两个字段的内容较重要,用户不能随便修改。Enroll使用了STDREG32.MDB的Section表,该表是一张课程表,其内容如表10.2所示。</p>
<p align="JUSTIFY"> 现在让我们来建立Enroll应用程序。首先,用AppWizard来完成Enroll程序框架的建立,请读者按下面几步进行:</p>
<blockquote>
<p align="JUSTIFY">启动AppWizard,指定一个名为Enroll的MFC工程。</p>
<p align="JUSTIFY">在MFC AppWizard的第一步选择Single
document。</p>
<p align="JUSTIFY">在MFC AppWizard的第二步选择Database
view without file support,然后点击Data
Source...按钮,在Database
Options对话框中的ODBC组合框中选择Student
Registration数据源,如图10.7所示。然后按“OK”按钮,则会打开一个Select
Database Table对话框,如图10.8所示。在该对话框中选择Section表。按OK按钮退出。</p>
<p align="JUSTIFY">在MFC AppWizard的第六步中,将类CEnrollSet改名为CSectionSet,将类CEnrollView改名为CSectionForm。</p>
<p align="JUSTIFY">按Finish按钮,建立Enroll工程。</p>
</blockquote>
<p align="center"> <img src="T10_7.gif" alt="T10_7.tif (155892 bytes)" width="470" height="248"></p>
<p align="center"> 图10.7
Database Options对话框</p>
<p align="center"> <img src="T10_8.gif" alt="T10_8.tif (135432 bytes)" width="452" height="279"></p>
<p align="center"> 图10.8
Select Database Table对话框</p>
<p align="JUSTIFY"> 打开工作区的类视图,可以发现AppWizard自动创建了一个记录集类CSectionSet和一个记录视图类CSectionForm,这两个类分别是CRecordset和CRecordView的派生类。AppWizard也为CSectionSet类自动创建了域数据成员。</p>
<p align="JUSTIFY"> 打开工作区的资源视图,读者不难找到一个ID为IDD_ENROLL_FORM的对话框模板,该模板将被记录视图用来显示表单。清除该模板中的所有控件,并把模板的尺寸扩大到183×110,然后按图10.3的式样放置控件。这里可以采取一个偷懒的方法:打开VC5.0已作好的第一个版本Enroll的资源文件(Enroll.rc),找到并打开IDD_ENROLL_FORM对话框模板,按住Ctrl键并用鼠标选择模板中的所有控件,然后按Ctrl+Insert键拷贝所选的控件。切换到自己的IDD_ENROLL_FORM对话框模板,然后按Shift+Insert键把刚才拷贝的控件粘贴到模板中。</p>
<p align="JUSTIFY"> 接下来的任务是用ClassWizard把表单中的控件与记录集的域数据成员连接起来,以实现控件与当前记录的DDX数据交换。请读者按如下步骤操作:</p>
<blockquote>
<p align="JUSTIFY">进入ClassWizard,选择Member
Variables页并且选择CSectionForm类。</p>
<p align="JUSTIFY">在变量列表中双击IDC_CAPACITY项,则会显示Add
Member Variable对话框。注意该对话框的Member
variable name栏显示的是一个组合框,而不是平常看到的编辑框。如图10.9所示,在组合框的列表中选择m_pSet->m_Capacity。按OK按钮确认。</p>
<p align="JUSTIFY">仿照第2步,为其他控件连接记录集的域数据成员。根据控件的ID,不难确定对应的域数据成员。</p>
</blockquote>
<p align="center"> <img src="T10_9.gif" alt="T10_9.tif (159244 bytes)" width="424" height="349"></p>
<p align="center"> 图10.9
Add Member Variable对话框</p>
<p align="JUSTIFY"> </p>
<p align="JUSTIFY"> 在CSectionForm类的定义内可以找到下面一行:</p>
<p align="JUSTIFY">CSectionSet* m_pSet;</p>
<p align="JUSTIFY">可见m_pSet是CSectionForm类的成员,它指向一个CSectionSet对象。用ClassWizard可以把控件与象记录集这样的“外部数据”连接起来,这是ClassWizard在数据库编程方面的一个特殊应用。</p>
<p align="JUSTIFY"> 编译并运行Enroll,读者会惊奇的发现Enroll居然是一个相当不错的记录浏览器,并且用户可以对记录进行修改。</p>
<p align="JUSTIFY"> 现在,让我们来分析一下AppWizard和ClassWizard为Enroll干了哪些事情。</p>
<p align="JUSTIFY"> 在文档类CEnrollDoc的定义中,有如下一行:</p>
<p align="JUSTIFY">CSectionSet m_sectionSet;</p>
<p align="JUSTIFY">可见AppWizard在CEnrollDoc类中嵌入了一个CSectionSet对象。这相当于调用了构造函数CSectionSet(NULL),CSectionSet类的构造函数的声明如下:<br>
CSectionSet(CDatabase* pDatabase
= NULL);</p>
<p align="JUSTIFY"> 函数的定义在清单10.5中列出。可以看出,构造函数调用了基类的构造函数,并对域数据成员进行了初始化。通过10.5.4我们知道,若传递NULL参数给CRecordset的构造函数,那么CRecordset::Open函数将自动构建一个CDatabase对象,并根据CRecordset::
GetDefaultConnect返回的连接字符串建立与数据源的连接。CSectionSet提供了虚拟函数GetDefaultConnect的新版本,如清单10.6所示,在该函数中提供了数据源Student
Registration。</p>
<p align="JUSTIFY"> <b> </b></p>
<p align="JUSTIFY"> <b>清单10.5
CSectionSet的构造函数</b></p>
<p align="JUSTIFY">CSectionSet::CSectionSet(CDatabase* pdb)</p>
<p align="JUSTIFY">: CRecordset(pdb)</p>
<p align="JUSTIFY">{</p>
<p align="JUSTIFY">//{{AFX_FIELD_INIT(CSectionSet)</p>
<p align="JUSTIFY">m_CourseID = _T("");</p>
<p align="JUSTIFY">m_SectionNo = _T("");</p>
<p align="JUSTIFY">m_InstructorID = _T("");</p>
<p align="JUSTIFY">m_RoomNo = _T("");</p>
<p align="JUSTIFY">m_Schedule = _T("");</p>
<p align="JUSTIFY">m_Capacity = 0;</p>
<p align="JUSTIFY">m_nFields = 6;</p>
<p align="JUSTIFY">//}}AFX_FIELD_INIT</p>
<p align="JUSTIFY">m_nDefaultType = snapshot;</p>
<p align="JUSTIFY">}</p>
<p align="JUSTIFY"> </p>
<p align="JUSTIFY"> <b> </b></p>
<p align="JUSTIFY"> <b>清单10.6
派生类的GetDefaultConnect函数</b></p>
<p align="JUSTIFY">CString CSectionSet::GetDefaultConnect()</p>
<p align="JUSTIFY">{</p>
<p align="JUSTIFY">return _T("ODBC;DSN=Student Registration");</p>
<p align="JUSTIFY">}</p>
<p align="JUSTIFY"> </p>
<p align="JUSTIFY"> </p>
<p align="JUSTIFY"> 至于记录集的建立,实际上是在CRecordView::
OnInitialUpdate中完成的,这部分代码对用户是透明的,这里在清单10.7中列出。在该函数中调用CRecordset::Open来建立记录集。在函数的开头调用了OnGetRecordset函数来获取与记录视图相连的记录集对象。CSectionForm提供了虚拟函数OnGetRecordset的新版本,如清单10.8所示,该函数把m_pSet提交给调用者。至于m_pSet的初始化,则是在CSectionForm::OnInitialUpdate函数中完成的,如清单10.9所示。</p>
<p align="JUSTIFY"> <b> </b></p>
<p align="JUSTIFY"> <b>清单10.7
CRecordView:: OnInitialUpdate函数</b></p>
<p align="JUSTIFY">void CRecordView::OnInitialUpdate()</p>
<p align="JUSTIFY">{</p>
<p align="JUSTIFY">CRecordset* pRecordset = OnGetRecordset();</p>
<p align="JUSTIFY">// recordset must be allocated already</p>
<p align="JUSTIFY">ASSERT(pRecordset != NULL);</p>
<p align="JUSTIFY"> </p>
<p align="JUSTIFY">if (!pRecordset->IsOpen())</p>
<p align="JUSTIFY">{</p>
<p align="JUSTIFY">CWaitCursor wait;</p>
<p align="JUSTIFY">pRecordset->Open();</p>
<p align="JUSTIFY">}</p>
<p align="JUSTIFY"> </p>
<p align="JUSTIFY">CFormView::OnInitialUpdate();</p>
<p align="JUSTIFY">}</p>
<p align="JUSTIFY"> </p>
<p align="JUSTIFY"> <b> </b></p>
<p align="JUSTIFY"> <b>清单10.8
派生类的OnGetRecordset函数</b></p>
<p align="JUSTIFY">CRecordset* CSectionForm::OnGetRecordset()</p>
<p align="JUSTIFY">{</p>
<p align="JUSTIFY">return m_pSet;</p>
<p align="JUSTIFY">}</p>
<p align="JUSTIFY"> </p>
<p> <b> </b></p>
<b>
<p align="JUSTIFY"> </p>
</b>
<p align="JUSTIFY"><b>清单10.9 派生类的OnInitialUpdate函数</b></p>
<p align="JUSTIFY">void CSectionForm::OnInitialUpdate()</p>
<p align="JUSTIFY">{</p>
<p align="JUSTIFY">m_pSet = &GetDocument()->m_sectionSet;</p>
<p align="JUSTIFY">CRecordView::OnInitialUpdate();</p>
<p align="JUSTIFY">}</p>
<p align="JUSTIFY"> </p>
<p> <b> </b></p>
<b>
<p align="JUSTIFY"> </p>
</b>
<p align="JUSTIFY"><b> </b>注意到在CRecordView::
OnInitialUpdate中调用CRecordset::Open时未提供任何参数,这意味着Open函数将从CRecordset::GetDefaultSQL中获取SQL信息。CSectionSet提供了虚拟函数GetDefaultSQL的新版本,如清单10.10所示,该函数返回了“Section”表名。</p>
<p align="JUSTIFY"> <b> </b></p>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -