天天看點

VC++資料庫程式設計快速入門

VC++資料庫程式設計快速入門

大多數應用程式都使用資料庫,各種管理軟體、ERP、CRM系統均需要資料庫來儲存和維護應用程式的資料,在VC中提供了多種資料庫通路技術,不過目前最流行的是ODBC(開放式資料庫接口)和ADO(活動對象模型)。

一.資料庫技術初步

   1.ODBC基本概念

   ODBC(Open Database Connectivity,開放資料庫互連)是微軟公司開放服務結構(WOSA,Windows Open Services Architecture)中有關資料庫的一個組成部分,它建立了一組規範,并提供了一組對資料庫通路的标準API(應用程式程式設計接口)。這些API利用SQL來完成其大部分任務。ODBC本身也提供了對SQL語言的支援,使用者可以直接将SQL語句送給ODBC。

  一個基于ODBC的應用程式對資料庫的操作不依賴任何DBMS,不直接與DBMS打交道,所有的資料庫操作由對應的DBMS的ODBC驅動程式完成。也就是說,不論是FoxPro、Access還是Oracle資料庫,均可用ODBC API進行通路。由此可見,ODBC的最大優點是能以統一的方式處理所有的資料庫。

  一個完整的ODBC由下列幾個部件組成:

  應用程式(Application)。

  ODBC管理器(Administrator)。該程式位于Windows 95控制台(Control Panel)的32位ODBC内,其主要任務是管理安裝的ODBC驅動程式和管理資料源。

  驅動程式管理器(Driver Manager)。驅動程式管理器包含在ODBC32.DLL中,對使用者是透明的。其任務是管理ODBC驅動程式,是ODBC中最重要的部件。

  ODBC API。

  ODBC 驅動程式。是一些DLL,提供了ODBC和資料庫之間的接口。

  資料源。資料源包含了資料庫位置和資料庫類型等資訊,實際上是一種資料連接配接的抽象。

  應用程式要通路一個資料庫,首先必須用ODBC管理器注冊一個資料源,管理器根據資料源提供的資料庫位置、資料庫類型及ODBC驅動程式等資訊,建立起ODBC與具體資料庫的聯系。這樣,隻要應用程式将資料源名提供給ODBC,ODBC就能建立起與相應資料庫的連接配接。

  在ODBC中,ODBC API不能直接通路資料庫,必須通過驅動程式管理器與資料庫交換資訊。驅動程式管理器負責将應用程式對ODBC API的調用傳遞給正确的驅動程式,而驅動程式在執行完相應的操作後,将結果通過驅動程式管理器傳回給應用程式。

  在通路ODBC資料源時需要ODBC驅動程式的支援。用Visual C++ 5.0安裝程式可以安裝SQL Server、 Access、 Paradox、 dBase、 FoxPro、 Excel、 Oracle 和Microsoft Text等驅動程式.在預設情況下,VC5.0隻會安裝SQL Server、 Access、 FoxPro和dBase的驅動程式.如果使用者需要安裝别的驅動程式,則需要重新運作VC 5.0的安裝程式并選擇所需的驅動程式。

   2.ADO對象通路模型

   1)ADO是微軟整個COM戰略體系中的一個組成部分

  活動資料對象(ADO)是一組由微軟提供的COM元件。 ADO建立在微軟所提倡的COM體系結構之上,它的所有接口都是自動化接口,是以在C++、VisualBasic、Delphi等支援COM的開發語言中通過接口都可以通路到ADO。ADO通過使用OLE DB這一新技術實作了以相同方式可以通路關系資料庫、文本檔案、非關系資料庫、索引伺服器和活躍目錄服務等的資料,擴大了應用程式中可使用的資料源範圍,進而成為微軟整個COM戰略體系中通路資料源元件的首選,是ODBC的替代産品。  

2)ADO對象模型組成

 與微軟的其它資料通路模型DAO和RDO相比,ADO對象模型非常精煉,僅由三個主要對象Connection、Command、Recordset和幾個輔助對象組成。Connection對象提供OLE DB資料源和對話對象之間的關聯,它通過使用者名稱和密碼來處理使用者身份的鑒别,并提供事務處理的支援;它還提供執行方法,進而簡化資料源的連接配接和資料檢索的程序。Command對象封裝了資料源可以解釋的指令,該指令可以是SQL指令、存儲過程或底層資料源可以了解的任何内容。Record set用于表示從資料源中傳回的表格資料,它封裝了記錄集合的導航、記錄更新、記錄删除和新記錄的添加等方法,還提供了批量更新記錄的能力。其它輔助對象則分别提供封裝ADO錯誤、封裝指令參數和封裝記錄集合的列。

 3)ADO的特點分析

  (a)由于封裝了許多底層工作,使用ADO與使用ODBC幾乎是一樣友善。

  (b) ADO不僅具有ODBC的主要功能,而且ADO适用的資料源的範圍要大的多。

  (c)在定義ADO記錄集變量和資料庫表字段綁定類時,要求記錄集的字段變量、狀态變量與資料庫表字段的個數、順序必須相同。這一點比在FMC中使用ODBC要複雜一些。但在資料庫字段與ADO記錄集字段變量綁定的宏中,ADO 提供的資料類型要遠多于FMC中的RFX(如日期時間類型,在ODBC中隻能轉換為Cstring類型)。

  (d)ADO允許同一Connection執行個體下有多個Record set執行個體。

  (e)ADO允許進行批更新(使用的Update Batch方法),這樣将大大減輕網絡負擔,提高資料庫處理效率。

  4) ADO在Visual C++中的使用

  利用微軟在Micrsoft Studio 6中提供的ADO2,可以在Visual C++中使用ADO接口操縱SQL SERVER資料庫。在編譯型進階語言中使用ADO,比起在一些腳本語言(如Visual Basic Scropt和JavaScript)中使用ADO要困難一些。

  以下給出一個Visual C++下使用ADO的Connection對象及其Record set對象的基本步驟:

  (a) 使用import指令引入ADO2元件

  例:#import "C:/ADO/msado15.dll" no_namespace rename("EOF", "EndOfFile") 

  (b) 定義CADORecordBinding 的派生類,用于程式與資料庫表字段的互動,該類的定義可參見icrsint.h。

  例:

class CIntlive : public CADORecordBinding 

public:

DBTIMESTAMP m_datetime; //定義ADO記錄集字段變量(與資料庫表字段相對應)

long m_key;

long m_value;

long m_quality;

WORD m_stsdatetime; //定義ADO記錄集狀态變量

WORD m_stskey;

WORD m_stsvalue;

WORD m_stsquality;

BEGIN_ADO_BINDING(CIntlive) //将資料庫字段與ADO記錄集字段變量綁定

ADO_VARIABLE_LENGTH_ENTRY2(1,adDBTimeStamp,m_datetime,sizeof(m_datetime),m_stsdatetime,true)

ADO_NUMERIC_ENTRY(2,adInteger,m_key,10,0,m_stskey,true)

ADO_NUMERIC_ENTRY(3,adInteger,m_value,10,0,m_stsvalue,true)

ADO_NUMERIC_ENTRY(4,adInteger,m_quality,10,0,m_stsquality,true)

END_ADO_BINDING()

};

  (c) 調用CoInitialize初始化COM ::CoInitialize(NULL); 

  (d) 聲明ADO的Connection對象指針和Recordset對象指針并初始化。(類型名在 msado15.dll中已定義)

  例:

_ConnectionPtr pConnection1 = NULL;

_RecordsetPtr rstADO1 = NULL; 

  (e) 定義CADORecordBinding派生類的執行個體及其Bind接口指針。

  例:

CIntlive m_intdata; 

IADORecordBinding *rstADOBind1 = NULL; 

  (f) 産生Connection對象執行個體和Record set對象執行個體。

  例:

pConnection1.CreateInstance(_uuidof(Connection)); 

rstADO1.CreateInstance(__uuidof(Recordset)) ; 

  (g) 連接配接到資料庫并打開Record set對象,其中open函數的參數的使用方法可參見微軟MSDN中ADO 相應對象參數的Basic描述。

  例:

PConnection1->Open("driver={SQL server};server=servera;uid=sa;pwd=;database=pubs","","",NULL);

rstADO1->Open("data", _variant_t((IDispatch *)pConnection1,true), 

adOpenKeyset,adLockBatchOptimistic, adCmdTable); 

  (h) 将CADORecordBinding派生類的執行個體聯編到Record set對象的Bind接口。

  例:

RstADOBind1->BindToRecordset(&m_intdata); 

  (i) 對Record set對象執行個體進行操作。操作方法可參見微軟MSDN中ADO Record set對象相應方法的Basic描述。

   例:

rstADO1->Move Next(); //移動遊标到下一條記錄

rstADO1->Update(_variant_t("quality"),_variant_t("3"))); //修改記錄的quality字段的值為3

rstADO1->Update Batch(adAffectAll)); //将在Record set對象上的所有更新一次送入資料庫

  (j) 關閉Record set對象并釋放Bind接口。

  例:

RstADO1->Close(); 

RstADOBind2->Release(); 

  (k) 關閉連接配接 pConnection1->Close(); 

  (l) 調用CoUnitialize釋放COM資源 ::CoUninitialize(); 

 5) 結論

  作為ODBC的替代産品,ADO确實有其過人之處。由于ADO資料源幾乎覆寫了目前常見的資料源類型,對于ODBC所不支援的資料源,ADO無疑是唯一的選擇。而ADO的批更新功能,更是網絡環境下大資料量更新應用的重要因素。由于ADO缺乏大量的第三方廠商的支援,使得ADO目前遠不如ODBC普及,但其面向對象的特性将使ADO具有比較廣闊的發展前景。

3.ADO與ODBC的差別

   有很多種使用資料庫的方法,對大多數資料庫來說,選擇C++這種産品也許并不适宜。我們知道,像dBASE IV,FoxPro,Oracle和Access這樣的産品是完全以資料庫管理為中心的。事實上,這些産品非常善于建立資料庫管理器,以至于它們确實并不善于做太多其它的工作。即使要用更通用化而非更專用化的資料庫産品來執行一些類型的工作,在使程式設計更容易這一方面,像VisualBasic和Delphi這樣的RAD環境也要比Visual C++強很多。

  你是不是對我的說法感到很奇怪?下面我就要談一談,在談到使用資料庫管理系統(DBMS)這個話題時,用Visual C++實際上可以做些什麼。雖然上述其它語言使得編寫成熟的包括使用者界面和高速搜尋能力的DBMS就像孩子做遊戲一樣容易,但是,它們缺少Visual C++可以提供的某些重要東西。你不能為使用Access的資料庫輕松地編寫出實用程式。正像實用程式的定義所說的,實用程式應該很小并且具備可移植性——Access應用程式卻不是這樣。即使用Access這樣的産品建立的程式可以很小并且可以移植,你仍有其它方面的需求:底層的功能。

  注:編寫資料庫實用程式及驅動程式時,可以選擇Visual C++語言。

  想像一下,使用像Visual Basic這樣的語言來與實時資料采集裝置打交道的情況。在進行底層通路時,RAD的保護環境常常使程式員不能進行有效的處理。當然,資料采集裝置幾乎不依賴于簡明的連接配接。你打算如何把Visual Basic和外部的資料源連接配接起來呢?資料源甚至可能不了解Windows,DOS或類似的成熟的作業系統。

  隻要使用得當,很容易看到Visual C++是一種不可或缺的資料庫管理工具。針對大規模的應用程式,即使你仍想依賴于Visual Basic這樣的RAD語言,也請考慮一下Visual C++,它建立的程式規模小、提供底層通路并能提供實時通路。事實上,你可能還沒有想到,Visual C++資料庫應用程式的市場是很有潛力的。随着人們在旅途中越來越多地使用膝上型和掌上型電腦,這兩類電腦上的資料庫應用程式也變得越來越普通。你也許能夠适應今天的膝上型電腦上的Access應用程式,但談到硬碟大小或記憶體需求時,公司裡較老的膝上型電腦可能就達不到要求。運作Windows CE的掌上型電腦在運作這個Access應用程式時,肯定會發生故障。在這一資料庫市場的新領域,Visual C++提供了無價無限的工具。

  Web連結 談到使用Visual C++和資料庫,其實你并不孤單。從一開始就有資料庫專用新聞討論區提供有關資料庫建立技巧的幫助,比如microsoft.public.access。不過,這些新聞討論區提供的是通用資訊,對實際編寫應用程式并非全都那麼有用。專門針對Visual C++問題的新聞討論區是microsoft.public.vc.database和microsoft.public.vc.mfcdatabase。如果你決定用ODBC通路資料庫,可能還要檢視一下microsoft.public.odbc.sdk新聞討論區,它讨論的不僅僅是SDK。對最新技術感興趣的程式員可以查閱microsoft.public.ado新聞討論區,或者microsoft.public.oledb(對象連結和嵌入資料庫)新聞討論區,前者讨論 ADO,後者讨論ADO的基礎技術。在microsoft.public.ado.rds有一個ADO子組,它讨論遠端資料通路。

  既然所有的疑惑都消除了,大多數人的信心也就增強了,下面我們就介紹兩種使C++通路資料庫中的資料的主要方法:ODBC(開放資料庫互連)和ADO(ActiveX資料對象)。在本章中,将介紹這兩種類型的通路方法,但我想你會發現,ADO方法是針對新的程式設計情形而采用的。它克服了早期技術的諸多限制,依賴于Microsoft新的底層通路方法OLE-DB(對象連結和嵌入資料庫)。在本書的後面我們會看到,用ADO和Visual C++提供的各種向導來彙集資料庫工程,其速度有多快。

  注 ODBC通常用來通路不具備OLE-DB特性的非Microsoft資料庫中的資料;16位的ODBC驅動程式工作起來可能非常緩慢。

  ODBC素以最慢的資料通路方法而著稱,但是很可惜,當ADO或DAO都不支援某個資料庫管理器而ODBC支援這個資料庫管理器時,在這種特定的情形下,你仍然需要使用ODBC。在大多數情況下,這意味着要從資料庫廠商那裡獲得所需的驅動程式,雖然Visual C++确實附帶了一些産品的驅動程式(如果你正在使用資料庫管理器的某些神秘功能,那麼就需要建立自己的接口棗這并不是一件十分困難的事)。本質上講,你總是要使用ODBC來通路Microsoft産品之外的其它DBMS産品所建立的資料庫,這些資料庫并不具備OLE-DB功能。ODBC還要求做一些額外的工作棗為ADO調整Visual C++中的大部分向導。

  進階技巧

  除了使用ADO和ODBC外,你還可以使用像DAO(資料通路對象)這樣的早期技術,該技術包含在像Access這樣的Microsoft産品中。DAO依賴于用Microsoft Access自動獲得的Microsoft Jet資料庫引擎。DAO還是較早版的Visual Basic所使用的引擎(最新版的Visual Basic和Visual C++依賴于相同的ADO/OLE-DB組合),是以如果需要支援較早的Visual Basic應用程式,那麼DAO仍是一個不錯的選擇。

  盡管Microsoft檔案聲明,可以用DAO通路非Microsoft産品建立的資料庫,但你仍會發現,在這種情況下,使用ADO和ODBC要好得多。這樣的話,不但相容性問題會少一些,速度也将有所提高,因為資料請求經過的接口層減少了。有一條經驗要記住,DAO是設計用來處理MDB檔案的。

  ADO的一個問題是,它不支援遠端通信。這是Microsoft提出RDO(遠端資料對象)的原因之一。這種特别技術在Visual Basic應用程式中的使用,要比在Visual C++中的使用多得多,是以我猜想,你們中有很多人都在使用它。但是,記住RDO仍是一種生命力很強的技術,這一點很重要。ADO确實具有替代RDO的遠端資料服務(RDS)特征。換言之,ADO在一個軟體包中提供了DAO和RDO兩種功能性。

4.MFC中相關類和ADO類庫簡介

    1)單獨使用CRecordSet

    一般情況下AppWizard會在資料庫應用程式中自動産生CRecordset的派生類,并将派生類和某個資料源中的表聯系起來也可以和視圖上的子視窗聯系起來。但是有時這樣做會影響到程式的靈活性,這時候我們可以單獨使用CRecordSet類。利用CRecordSet類我們可以執行SQL語句,并可以讀出結果集中資料。

首先我們需要包含頭檔案afxdb.h,可以将#include 添加到stdafx.h檔案中。此外在使用CRecordset時必須有一個又一個CDatabase對象,該對象的作用是管理資料源連接配接。然後可以産生一個CRecordset對象,利用BOOL CRecordset::Open( UINT nOpenType = AFX_DB_USE_DEFAULT_TYPE, LPCTSTR lpszSQL = NULL, DWORD dwOptions = none )可以執行SQL語句。

但執行成功後,可以調用以下的函數滾動光标,讀取資料。

MoveFirst 移動光标到第一條記錄處 

MoveNext 移動光标到後一條記錄處 

MovePrev 移動光标到前一條記錄處 

MoveLast 移動光标到最後一條記錄處 

IsBOF 檢測光标是否在第一條記錄上 

IsEOF 檢測光标是否在最後一條記錄上 

GetFieldValue 得到結果中資料

下面是具體代碼:

void CYourClass::ConnectToDB()

{//連接配接資料庫

    BOOL fOK = m_dbConn.Open("test");

    TRACE("connect fOK=%d/n",m_dbConn);

void CYourClass::Select()

{//執行SELECT語句

    CRecordset rec(&m_dbConn);

    BOOL fOK = rec.Open(CRecordset::forwardOnly,"select loc_id from table1 order by loc_id");

    TRACE("select fOK = %d/n",fOK);

    TRACE("傳回的列數為:%d/n",rec.GetRowsetSize());

    CString szResult;

    while(!rec.IsEOF())

    {

          rec.GetFieldValue((int)0,szResult);

          rec.MoveNext();

          TRACE("fetch : %s/n",szResult);

     }

}

此外CRecordset::GetFieldValue有很多種原型,你可以通過指定列位置或是字段名來擷取資料:

void GetFieldValue( LPCTSTR lpszName, CDBVariant& varValue, short nFieldType = DEFAULT_FIELD_TYPE );

void GetFieldValue( short nIndex, CDBVariant& varValue, short nFieldType = DEFAULT_FIELD_TYPE );

void GetFieldValue( LPCTSTR lpszName, CString& strValue );

void GetFieldValue( short nIndex, CString& strValue );

如果使用CDBVariant類型變量來擷取結果,你可以得到任何類型的結果。在CDBVariant::m_dwType成員變量中記錄了該變量所包含的資料類型,根據該變量的值你可以确定資料類型并引用CDBVariant對象中的相應成員變量。

2)vc資料庫程式設計中CDatabase類的用法簡介

  要建立與資料源的連接配接,首先應構造一個CDatabase對象,然後再調用CDatabase的Open成員函數.Open函數負責建立連接配接,其聲明為

virtual BOOL Open( LPCTSTR lpszDSN, BOOL bExclusive = FALSE, BOOL bReadOnly = FALSE, LPCTSTR lpszConnect = “ODBC;”, BOOL bUseCursorLib = TRUE ); throw( CDBException, CMemoryException );

  參數lpszDSN指定了資料源名(構造資料源的方法将在後面介紹),在lpszConnect參數中也可包括資料源名,此時lpszDSN必需為NULL,若在函數中未提供資料源名且使lpszDSN為NULL,則會顯示一個資料源對話框,使用者可以在該對話框中選擇一個資料源.參數bExclusive說明是否獨占資料源,由于目前版本的類庫還不支援獨占方式,故該參數的值應該是FALSE,這說明資料源是被共享的.參數bReadOnly若為TRUE則對資料源的連接配接是隻讀的.參數lpszConnect指定了一個連接配接字元串,連接配接字元串中可以包括資料源名、使用者帳号(ID)和密碼等資訊,字元串中的"ODBC"表示要連接配接到一個ODBC資料源上.參數bUseCursorLib若為TRUE,則會裝載光标庫,否則不裝載,快照需要光标庫,動态集不需要光标庫. 若連接配接成功,函數傳回TRUE,若傳回FALSE,則說明使用者在資料源對話框中按了Cancel按鈕。若函數内部出現錯誤,則架構會産生一個異常。

  下面是一些調用Open函數的例子。

CDatabase m_db; //在文檔類中嵌入一個CDatabase對象

//連接配接到一個名為"Student Registration"的資料源

m_db.Open("Student Registration");

//在連接配接資料源的同時指定了使用者帳号和密碼

m_db.Open(NULL,FALSE,FALSE,"ODBC;DSN=Student Registration;UID=ZYF;PWD=1234");

m_db.Open(NULL); //将彈出一個資料源對話框

 

  要從一個資料源中脫離,可調用函數Close。在脫離後,可以再次調用Open函數來建立一個新的連接配接.調用IsOpen可判斷目前是否有一個連接配接,調用GetConnect可傳回目前的連接配接字元串。函數的聲明為

virtual void Close( );

BOOL IsOpen( ) const; //傳回TRUE則表明目前有一個連接配接

const CString& GetConnect( ) const;

  CDatabase的析構函數會調用Close,是以隻要删除了CDatabase對象就可以與資料源脫離。

3)vc資料庫程式設計中CRecordView類簡介

    CRecordView(記錄視圖)是CFormView的派生類,它提供了一個表單視圖來顯示目前記錄.使用者可以通過表單視圖顯示目前記錄.通過記錄視圖,可以修改、添加和删除資料.使用者一般需要建立一個CRecordView的派生類并在其對應的對話框模闆中加入控件.

    記錄視圖使用DDX資料交換機制在表單中的控件和記錄集之間交換資料。在前面介紹的DDX都是在控件和控件父視窗的資料成員之間交換資料,而記錄視圖則是在控件和一個外部對象(CRecordset的派生類對象)之間交換資料.清單10.3顯示了一個CRecordView的派生類的DoDataExchange函數,讀者可以看出,該函數是與m_pSet指針指向的記錄集對象的域資料成員交換資料的,而且,交換資料的代碼是ClassWizard自動加入的.在後面的例子中,将向讀者介紹用ClassWizard連接配接記錄視圖與記錄集對象的方法.

   用來與記錄集對象的域資料成員交換資料的DoDataExchange函數

void CSectionForm::DoDataExchange(CDataExchange* pDX)

{

    CRecordView::DoDataExchange(pDX);

//{{AFX_DATA_MAP(CSectionForm)

    DDX_FieldText(pDX, IDC_COURSE, m_pSet->m_CourseID, m_pSet);

    DDX_FieldText(pDX, IDC_SECTION, m_pSet->m_SectionNo, m_pSet);

    DDX_FieldText(pDX, IDC_INSTRUCTOR, m_pSet->m_InstructorID, m_pSet);

    DDX_FieldText(pDX, IDC_ROOM, m_pSet->m_RoomNo, m_pSet);

    DDX_FieldText(pDX, IDC_SCHEDULE, m_pSet->m_Schedule, m_pSet);

    DDX_FieldText(pDX, IDC_CAPACITY, m_pSet->m_Capacity, m_pSet);

//}}AFX_DATA_MAP

}

CRecordView本身提供了對下面四個指令的支援:

ID_RECORD_FIRST //滾動到記錄集的第一個記錄

ID_RECORD_LAST //滾動到記錄集的最後一個記錄

ID_RECORD_NEXT //前進一個記錄

ID_RECORD_PREV //後退一個記錄

CRecordView提供了OnMove成員函數處理這四個指令消息,OnMove函數對使用者是透明的,下面列出了OnMove的源代碼.

BOOL CRecordView::OnMove(UINT nIDMoveCommand)

{

    CRecordset* pSet = OnGetRecordset();

    if (pSet->CanUpdate())

    {

         pSet->Edit();

         if (!UpdateData())

              return TRUE;

         pSet->Update();

     }

     switch (nIDMoveCommand)

    {

    case ID_RECORD_PREV:

         pSet->MovePrev();

         if (!pSet->IsBOF())

             break;

    case ID_RECORD_FIRST:

         pSet->MoveFirst();

         break;

    case ID_RECORD_NEXT:

         pSet->MoveNext();

         if (!pSet->IsEOF())

             break;

         if (!pSet->CanScroll())

         {

         // clear out screen since we're sitting on EOF

             pSet->SetFieldNull(NULL);

             break; 

         }

     case ID_RECORD_LAST:

          pSet->MoveLast();

          break;

     default:

     // Unexpected case value

          ASSERT(FALSE);

}

// Show results of move operation

    UpdateData(FALSE);

    return TRUE;

}

   在函數的開頭先調用CRecordset::Edit進入編輯模式,接着調用UpdateData将控件中的資料更新到記錄集對象的域資料成員中,然後調用CRecordset::Update将域資料成員的值寫入資料源.這說明OnMove在滾動記錄的同時會完成對原來記錄的修改.

  在函數的中間有一個分支語句用來處理四個不同的指令,在這個分支語句中調用了CRecordset的各種用于滾動記錄的成員函數,這些函數在滾動到一個新的記錄時會把該記錄的内容設定到域資料成員中.在函數的末尾調用UpdateData(FALSE)把新的目前記錄的内容設定到表單的控件中。

  由此可見,OnMove一來一回完成了兩次表單控件和資料源的資料交換過程.通過分析該函數,讀者可以學會在浏覽記錄時如何控制DDX和DFX資料交換.