?? 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>
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 + -