📄 ch15.htm
字号:
27: END_ADO_BINDING()28:29: public:30: LONG m_lAddressID;31: ULONG lAddressIDStatus;32: CHAR m_szFirstName[51];33: ULONG lFirstNameStatus;34: CHAR m_szLastName[51];35: ULONG lLastNameStatus; 36: CHAR m_szSpouseName[51];37: ULONG lSpouseNameStatus;38: CHAR m_szAddress[256];39: ULONG lAddressStatus;40: CHAR m_szCity[51];41: ULONG lCityStatus;42: CHAR m_szStateOrProvince[21];43: ULONG lStateOrProvinceStatus;44: CHAR m_szPostalCode[21];45: ULONG lPostalCodeStatus;46: CHAR m_szCountry[51];47: ULONG lCountryStatus;48: CHAR m_szEmailAddress[51];49: ULONG lEmailAddressStatus;50: CHAR m_szHomePhone[31];51: ULONG lHomePhoneStatus;52: CHAR m_szWorkPhone[31];53: ULONG lWorkPhoneStatus;54: CHAR m_szWorkExtension[21];55: ULONG lWorkExtensionStatus;56: CHAR m_szFaxNumber[31];57: ULONG lFaxNumberStatus;58: DATE m_dtBirthdate;59: ULONG lBirthdateStatus;60: VARIANT_BOOL m_bSendCard;61: ULONG lSendCardStatus;62: CHAR m_szNotes[65536];63: ULONG lNotesStatus;64: };</PRE><P>Once you've created this class, you need to add a variable to the document class.Add a new member variable to the document class, specifying the variable type asCCustomRs, the name as m_rsRecSet, and the access as private. You'll also need toinclude the custom record class header file in the document source code file, asin Listing 15.2.</P><P><H4>LISTING 15.2. THE DOCUMENT SOURCE CODE INCLUDES.</H4><PRE> 1: // dbadoDoc.cpp : implementation of the CDbAdoDoc class 2: //</PRE><H3><A NAME="Heading16"></A>3:</H3><PRE> 4: #include "stdafx.h" 5: #include "dbado.h" 6: 7: #include "CustomRs.h" 8: #include "dbadoDoc.h" 9: #include "dbadoView.h"</PRE><P>Another detail that you need to attend to before going any further is providinga way for the view to get a pointer to the record class from the document class.This function should return a pointer to the record class variable. To add this functionto your application, add a new member function to the document class, specifyingthe function type as CCustomRs*, the function declaration as GetRecSet, and the functionaccess as public. Edit this function, adding the code in Listing 15.3.</P><P><H4>LISTING 15.3. THE CDbAdoDoc GetRecSet FUNCTION.</H4><PRE> 1: CCustomRs* CDbAdoDoc::GetRecSet() 2: { 3: // Return a pointer to the record object 4: return &m_rsRecSet; 5: }</PRE><P>One last piece of functionality that you'll add before getting to the real heartof ADO programming is the function for reporting ADO and database errors. This functionwill display a message to the user, reporting that an error occurred and displayingthe error code and error message for the user. To add this function to your application,add a new member function to your document class. Specify the function type as void,the function declaration as GenerateError(HRESULT hr, PWSTR pwszDescription), andthe access as public. Edit the function, entering the code in Listing 15.4.</P><P><H4>LISTING 15.4. THE CDbAdoDoc GenerateError FUNCTION.</H4><PRE> 1: void CDbAdoDoc::GenerateError(HRESULT hr, PWSTR pwszDescription) 2: { 3: CString strError; 4: 5: // Format and display the error message 6: strError.Format("Run-time error `%d (%x)'", hr, hr); 7: strError += "\n\n"; 8: strError += pwszDescription; 9:10: AfxMessageBox(strError);11: }</PRE><H3><A NAME="Heading17"></A>Connecting and Retrieving Data</H3><P>You can perform all of the connecting to the database and retrieving the recordset in the OnNewDocument function in the document class. Before you can add thisfunctionality, you need to add a few more variables to the document class. You'llneed a Recordset object pointer, an IADORecordBinding interface pointer, a coupleof string variables for holding the database connection string, and the SQL commandto execute to populate the record set. Add all of these variables to the documentclass as specified in Table 15.3.</P><P><H4>TABLE 15.3. DOCUMENT CLASS MEMBER VARIABLES.</H4><P><TABLE BORDER="1"> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT"><I>Name</I></TD> <TD ALIGN="LEFT"><I>Type</I></TD> <TD ALIGN="LEFT"><I>Access</I></TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">m_pRs </TD> <TD ALIGN="LEFT">_RecordsetPtr </TD> <TD ALIGN="LEFT">Private </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">m_piAdoRecordBinding </TD> <TD ALIGN="LEFT">IADORecordBinding* </TD> <TD ALIGN="LEFT">Private </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">m_strConnection </TD> <TD ALIGN="LEFT">CString </TD> <TD ALIGN="LEFT">Private </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">m_strCmdText </TD> <TD ALIGN="LEFT">CString </TD> <TD ALIGN="LEFT">Private </TD> </TR></TABLE></P><P>In the OnNewDocument function, you'll perform a series of steps for connectingand retrieving the record set. First, you'll set the strings for the database connectionand the SQL command to be run. Next, you'll initialize the COM environment and initializethe two pointers so that they are both NULL. You'll create the Recordset object usingthe CreateInstance function. Open the Recordset, connecting to the database and runningthe SQL command at the same time. Bind the record class to the record set using theIADORecordBinding interface pointer. Finally, tell the view class to refresh thebound data, displaying the initial record for the user using a view class functionthat you'll add in a little while. To add all this functionality, edit the OnNewDocumentfunction in the document class, adding the code starting with line 8 in Listing 15.5.</P><P><H4>LISTING 15.5. THE CDbAdoDoc OnNewDocument FUNCTION.</H4><PRE> 1: BOOL CDbAdoDoc::OnNewDocument() 2: { 3: if (!CDocument::OnNewDocument()) 4: return FALSE; 5: 6: // TODO: add reinitialization code here 7: // (SDI documents will reuse this document) 8: // Set the connection and SQL command strings 9: m_strConnection = _T("Provider=MSDASQL.1;Data Source=TYVCDB");10: m_strCmdText = _T("select * from Addresses");11:12: // Initialize the Recordset and binding pointers13: m_pRs = NULL;14: m_piAdoRecordBinding = NULL;15: // Initialize the COM environment16: ::CoInitialize(NULL);17: try18: {19: // Create the record set object20: m_pRs.CreateInstance(__uuidof(Recordset));21:22: // Open the record set object23: m_pRs->Open((LPCTSTR)m_strCmdText, (LPCTSTR)m_strConnection, 24: adOpenDynamic, adLockOptimistic, adCmdUnknown);25:26: // Get a pointer to the record binding interface27: if (FAILED(m_pRs->QueryInterface(__uuidof(IADORecordBinding),28: (LPVOID *)&m_piAdoRecordBinding)))29: _com_issue_error(E_NOINTERFACE);30: // Bind the record class to the record set31: m_piAdoRecordBinding->BindToRecordset(&m_rsRecSet);32:33: // Get a pointer to the view34: POSITION pos = GetFirstViewPosition();35: CDbAdoView* pView = (CDbAdoView*)GetNextView(pos);36: if (pView)37: // Sync the data set with the form38: pView->RefreshBoundData();39: }40: // Any errors?41: catch (_com_error &e)42: {43: // Display the error44: GenerateError(e.Error(), e.Description());45: }46:47: return TRUE;48: }</PRE><P>Before moving any further, it's a good idea to make sure that you add all thecode necessary to clean up as your application is closing. You need to close therecord set and release the pointer to the record binding interface. You'll also shutdown the COM environment. To add all this functionality to your application, adda function to the DeleteContents event message in the document class. Edit this function,adding the code in Listing 15.6.</P><P><H4>LISTING 15.6. THE CDbAdoDoc DeleteContents FUNCTION.</H4><PRE> 1: void CDbAdoDoc::DeleteContents() 2: { 3: // TODO: Add your specialized code here and/or call the base class 4: // Close the record set 5: if (m_pRs) 6: m_pRs->Close(); 7: // Do we have a valid pointer to the record binding? 8: if (m_piAdoRecordBinding) 9: // Release it10: m_piAdoRecordBinding->Release();11: // Set the record set pointer to NULL12: m_pRs = NULL;13:14: // Shut down the COM environment15: CoUninitialize();16:17: CDocument::DeleteContents();18: }</PRE><H3><A NAME="Heading18"></A>Populating the Form</H3><P>To display the record column values for the user, you'll add a function for copyingthe values from the record class to the view variables. This function first needsto get a pointer to the record class from the document class. Next, it will checkthe status of each individual field in the record class to make sure that it's okayto copy, and then it will copy the value. Once all values have been copied, you cancall UpdateData to display the values in the controls on the form. To add this functionalityto your application, add a new member function to the view class. Specify the functiontype as void, the function declaration as RefreshBoundData, and the access as public.Edit this new function, adding the code in Listing 15.7.</P><P><H4>LISTING 15.7. THE CDbAdoView RefreshBoundData FUNCTION.</H4><PRE> 1: void CDbAdoView::RefreshBoundData() 2: { 3: CCustomRs* pRs; 4: 5: // Get a pointer to the document object 6: pRs = GetDocument()->GetRecSet(); 7: 8: // Is the field OK 9: if (adFldOK == pRs->lAddressIDStatus)10: // Copy the value11: m_lAddressID = pRs->m_lAddressID;12: else13: // Otherwise, set the value to 014: m_lAddressID = 0;15: // Is the field OK16: if (adFldOK == pRs->lFirstNameStatus)17: // Copy the value18: m_strFirstName = pRs->m_szFirstName;19: else20: // Otherwise, set the value to 021: m_strFirstName = _T("");22: if (adFldOK == pRs->lLastNameStatus)23: m_strLastName = pRs->m_szLastName;24: else25: m_strLastName = _T("");26: if (adFldOK == pRs->lSpouseNameStatus)27: m_strSpouseName = pRs->m_szSpouseName;28: else29: m_strSpouseName = _T("");30: if (adFldOK == pRs->lAddressStatus)31: m_strAddress = pRs->m_szAddress;32: else33: m_strAddress = _T("");34: if (adFldOK == pRs->lCityStatus)35: m_strCity = pRs->m_szCity;36: else37: m_strCity = _T("");38: if (adFldOK == pRs->lStateOrProvinceStatus)39: m_strStateOrProvince = pRs->m_szStateOrProvince;40: else41: m_strStateOrProvince = _T("");42: if (adFldOK == pRs->lPostalCodeStatus) 43: m_strPostalCode = pRs->m_szPostalCode;44: else45: m_strPostalCode = _T("");46: if (adFldOK == pRs->lCountryStatus)47: m_strCountry = pRs->m_szCountry;48: else49: m_strCountry = _T("");50: if (adFldOK == pRs->lEmailAddressStatus)51: m_strEmailAddress = pRs->m_szEmailAddress;52: else53: m_strEmailAddress = _T("");54: if (adFldOK == pRs->lHomePhoneStatus)55: m_strHomePhone = pRs->m_szHomePhone;56: else57: m_strHomePhone = _T("");58: if (adFldOK == pRs->lWorkPhoneStatus)59: m_strWorkPhone = pRs->m_szWorkPhone;60: else61: m_strWorkPhone = _T("");62: if (adFldOK == pRs->lWorkExtensionStatus)63: m_strWorkExtension = pRs->m_szWorkExtension;64: else65: m_strWorkExtension = _T("&q
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -