📄 ch14.htm
字号:
<P><TABLE BORDER="1"> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT"><I>Object</I></TD> <TD ALIGN="LEFT"><I>Name</I></TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_CBCARD </TD> <TD ALIGN="LEFT">m_pSet->m_SendCard </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_EADDR </TD> <TD ALIGN="LEFT">m_pSet->m_Address </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_ECITY </TD> <TD ALIGN="LEFT">m_pSet->m_City </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_ECOUNTRY </TD> <TD ALIGN="LEFT">m_pSet->m_Country </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_EEMAIL </TD> <TD ALIGN="LEFT">m_pSet->m_EmailAddress </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_EFAX </TD> <TD ALIGN="LEFT">m_pSet->m_FaxNumber </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_EFNAME </TD> <TD ALIGN="LEFT">m_pSet->m_FirstName </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_EHPHONE </TD> <TD ALIGN="LEFT">m_pSet->m_HomePhone </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_EID </TD> <TD ALIGN="LEFT">m_pSet->m_AddressID </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_ELNAME </TD> <TD ALIGN="LEFT">m_pSet->m_LastName </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_ENOTES </TD> <TD ALIGN="LEFT">m_pSet->m_Notes </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_ESNAME </TD> <TD ALIGN="LEFT">m_pSet->m_SpouseName </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_ESTATE </TD> <TD ALIGN="LEFT">m_pSet->m_StateOrProvince </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_EWEXT </TD> <TD ALIGN="LEFT">m_pSet->m_WorkExtension </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_EWPHONE </TD> <TD ALIGN="LEFT">m_pSet->m_WorkPhone </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">IDC_EZIP </TD> <TD ALIGN="LEFT">m_pSet->m_PostalCode </TD> </TR></TABLE></P><P>You probably noticed when it was time to attach a database field to the birthdatecontrol that the birthday field is missing from the list of database fields. If youlook at the record set class in the class view and expand its tree, you'll noticethat the birthdate field is included as one of the database fields, but it's notavailable in the list of available columns for use with the controls. Double-clickon the birthdate field in the record set class to view its definition. You'll noticethat the m_Birthdate variable is declared as a CTime variable. This is the reasonthat it's not available in the list of database fields that can be attached to controls.There isn't a macro or function you can call for exchanging data between a controland a CTime variable. This is also a problem because the CTime variable type cannothandle dates before December 31, 1969. To use this database field, you'll need tochange its definition from a CTime to a COleDateTime variable type, as in line 17in Listing 14.1. Once you change the variable type of this database field, you willbe able to attach it to the IDC_EDOB control.</P><P><H4>LISTING 14.1. THE DATABASE FIELD VARIABLE DECLARATIONS.</H4><PRE>1: // Field/Param Data2: //{{AFX_FIELD(CTestdb5Set, CRecordset)3: long m_AddressID;4: CString m_FirstName;5: CString m_LastName;6: CString m_SpouseName;7: CString m_Address;8: CString m_City;9: CString m_StateOrProvince;10: CString m_PostalCode;11: CString m_Country;12: CString m_EmailAddress;13: CString m_HomePhone;14: CString m_WorkPhone;15: CString m_WorkExtension;16: CString m_FaxNumber;17: COleDateTime m_Birthdate;18: BOOL m_SendCard;19: CString m_Notes;20: //}}AFX_FIELD</PRE><BLOCKQUOTE> <P><HR><STRONG>NOTE:</STRONG> Normally, you do not want to edit the portions of code in your applications that are created and maintained by the various wizards. The change I outline here is one of the few exceptions to this rule. This obstacle could possibly be considered a bug in the Visual C++ AppWizard, although it's technically not a bug. You can convert the date/time database field to sev- eral variable types when creating a class variable to represent that field. CTime is one of these variable types; COleDateTime is another. Because these are both equally valid choices, and the functions that populate this variable can work with either, making this change is possible without dire consequences.<HR></BLOCKQUOTE><P>Once you make the change to the variable type for the m_Birthdate variable inthe record set class (CDbOdbcSet), and attach this database field to the Birthdatecontrol on the form, you might think that you are ready to compile and run your application.Unfortunately, your application will not compile. You'll get a compiler error statingthat the DDX_FieldText cannot convert the COleDateTime variable type. What you needto do is add the code to perform this conversion yourself. Return to the Class Wizardand delete the variable that you added to the IDC_EDOB control. Add a new variableto this control. Specify that the variable is type COleDateTime, and give the variablea name such as m_oledtDOB. Pull up the DoDataExchange function in the view class,CDbOdbcView, into the editor, and add lines 4 through 6 and lines 26 through 28 tothe function, as shown in Listing 14.2.</P><P><H4>LISTING 14.2. THE CDbOdbcView DoDataExchange FUNCTION.</H4><PRE>1: void CDbOdbcView::DoDataExchange(CDataExchange* pDX)2: {3: CRecordView::DoDataExchange(pDX);4: // Copy the DOB from the record set to the view variable5: if (pDX->m_bSaveAndValidate == FALSE)6: m_oledtDOB = m_pSet->m_Birthdate;7: //{{AFX_DATA_MAP(CTestdb5View)8: DDX_FieldText(pDX, IDC_EID, m_pSet->m_AddressID, m_pSet);9: DDX_FieldText(pDX, IDC_EFNAME, m_pSet->m_FirstName, m_pSet);10: DDX_FieldText(pDX, IDC_ELNAME, m_pSet->m_LastName, m_pSet);11: DDX_FieldText(pDX, IDC_ESNAME, m_pSet->m_SpouseName, m_pSet);12: DDX_FieldText(pDX, IDC_ESTATE, m_pSet->m_StateOrProvince, m_pSet);13: DDX_FieldText(pDX, IDC_ECITY, m_pSet->m_City, m_pSet);14: DDX_FieldText(pDX, IDC_EADDR, m_pSet->m_Address, m_pSet);15: DDX_FieldCheck(pDX, IDC_CBCARD, m_pSet->m_SendCard, m_pSet);16: DDX_FieldText(pDX, IDC_ECOUNTRY, m_pSet->m_Country, m_pSet);17: DDX_FieldText(pDX, IDC_EEMAIL, m_pSet->m_EmailAddress, m_pSet);18: DDX_FieldText(pDX, IDC_EFAX, m_pSet->m_FaxNumber, m_pSet);19: DDX_FieldText(pDX, IDC_EHPHONE, m_pSet->m_HomePhone, m_pSet);20: DDX_FieldText(pDX, IDC_ENOTES, m_pSet->m_Notes, m_pSet);21: DDX_FieldText(pDX, IDC_EWEXT, m_pSet->m_WorkExtension, m_pSet);22: DDX_FieldText(pDX, IDC_EWPHONE, m_pSet->m_WorkPhone, m_pSet);23: DDX_FieldText(pDX, IDC_EZIP, m_pSet->m_PostalCode, m_pSet);24: DDX_Text(pDX, IDC_EDOB, m_oledtDOB);25: //}}AFX_DATA_MAP26: // Copy the DOB variable back from the view variable to the record Âset27: if (pDX->m_bSaveAndValidate == TRUE)28: m_pSet->m_Birthdate = m_oledtDOB;29: }</PRE><P>In addition to the above change, you have to remove the initialization of them_Birthdate variable in the set class. This is also code that was added by the AppWizard,and once again you have to break the rules by modifying the code that you are neversupposed to touch. To make this change, you can take the simple approach by commentingout the initialization of this variable in the set class constructor, in line 19of Listing 14.3.</P><P><H4>LISTING 14.3. THE CDbOdbcSet CONSTRUCTOR.</H4><PRE>1: CDbOdbcSet::CDbOdbcSet(CDatabase* pdb)2: : CRecordset(pdb)3: {4: //{{AFX_FIELD_INIT(CTestdb5Set)5: m_AddressID = 0;6: m_FirstName = _T("");7: m_LastName = _T("");8: m_SpouseName = _T("");9: m_Address = _T("");10: m_City = _T("");11: m_StateOrProvince = _T("");12: m_PostalCode = _T("");13: m_Country = _T("");14: m_EmailAddress = _T("");15: m_HomePhone = _T("");16: m_WorkPhone = _T("");17: m_WorkExtension = _T("");18: m_FaxNumber = _T("");19: //m_Birthdate = 0;20: m_SendCard = FALSE;21: m_Notes = _T("");22: m_nFields = 17;23: //}}AFX_FIELD_INIT24: m_nDefaultType = dynaset;25: }</PRE><P>Now compile and run your application once again. You'll find that you have a fullyfunctioning database application that retrieves a set of records from the databaseand allows you to scroll through them and make changes to the data, as shown in Figure14.9.</P><P><H3><A NAME="Heading8"></A>Adding New Records</H3><P>You've already created a fully functioning database application without writinga single line of code. However, a few functions are missing. Most database applicationslet the user add new records to the database. To add a new record to the database,you'll want to figure out what the next ID number should be, so you'll scroll tothe last record in the set to get the ID and then increment it by one. Next, you'llcall the AddNew function to add a new record, set the ID field to the new ID youcalculated, and then call the Update function to save the new record. Finally, you'llcall the Requery function to refresh the set of records and then scroll to the lastrecord in the set to let the user enter data into the new record.</P><P><A HREF="javascript:popUp('14fig10.gif')"><B>FIGURE 14.9.</B></A><B> </B><I>Therunning appli- cation.</I></P><BLOCKQUOTE> <P><HR><STRONG>TIP:</STRONG> Because the ID field in the database in defined as an AutoIncrement field, you do not normally specify your own ID for the field. However, because the record set is creating a new record with the ID field, you need to assign a valid ID to the record or you won't be able to add it to the database. The method used in this application will not work with any multiuser database because each person would generate the same IDs for new records. In this situation, a centralized method for generating new IDs, such as a counter field in the database, is a better solution. The other option is to create a SQL statement to insert a new record into the database that was missing the ID field. This allows the auto-increment functionality to work correctly.<HR></BLOCKQUOTE><P>To add this functionality to your application, start by adding a function to yourrecord set class to determine the next ID number to be used. Add a member functionto the record set class, CDbOdbcSet. Specify the function type as long, the functiondeclaration as GetMaxID, and the access as public. Edit the function, adding thecode in Listing 14.4.</P><P><H4>LISTING 14.4. THE CDbOdbcSet GetMaxID FUNCTION.</H4><PRE>1: long CDbOdbcSet::GetMaxID()2: {3: // Move to the last record4: MoveLast();5: // return the ID of this record6: return m_AddressID;7: }</PRE><P>Next, you'll need a menu entry that the user can select to add a new record tothe database. Add a new menu entry to the Record menu. Configure the new menu entrywith the properties in Table 14.8.</P><P><H4>TABLE 14.8. MENU PROPERTY SETTINGS.</H4><P><TABLE BORDER="1"> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT"><I>Object</I></TD> <TD ALIGN="LEFT"><I>Property</I></TD> <TD ALIGN="LEFT"><I>Setting</I></TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">Menu Entry </TD> <TD ALIGN="LEFT">ID </TD> <TD ALIGN="LEFT">IDM_RECORD_NEW </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT"> <P> </TD> <TD ALIGN="LEFT">Caption </TD> <TD ALIGN="LEFT">N&ew Record </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT"> <P> </TD> <TD ALIGN="LEFT">Prompt </TD> <TD ALIGN="LEFT">Add a new record\nNew Record </TD> </TR></TABLE></P><P>Using the Class Wizard, add an event-handler function for the COMMAND event messagefor this menu to the view class, CDbOdbcView. Edit this function, adding the codein Listing 14.5.</P><P><H4>LISTING 14.5. THE CDbOdbcView OnRecordNew FUNCTION.</H4>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -