DOCUMENT:Q110719 07-MAY-2001 [visualc] TITLE :PRB: CTime DDX Routine for CRecordView Date Fields PRODUCT :Microsoft C Compiler PROD/VER:WINDOWS:1.0,1.5,1.51,1.52 OPER/SYS: KEYWORDS:kbDatabase kbMFC kbODBC kbVC ====================================================================== ------------------------------------------------------------------------------- The information in this article applies to: - The Microsoft Foundation Class, version 1.0, used with: - Microsoft Visual C++ for Windows, 16-bit edition, versions 1.5, 1.51, 1.52 - Microsoft Visual C++, 32-bit Editions, versions 2.0, 2.1, 4.0, 4.1 - Microsoft Visual C++, 32-bit Enterprise Edition, version 4.2 - Microsoft Visual C++, 32-bit Professional Edition, version 4.2 ------------------------------------------------------------------------------- SYMPTOMS ======== Create a Microsoft Foundation Classes (MFC) Open Database Connectivity (ODBC) application associated with an ODBC data source which contains a date or time field. The generated application's CRecordSet derived class will contain a CTime member variable associated with the date/time field through an RFX_Date() call in the class's DoFieldExchange() member function. Edit the dialog box template associated with the CRecordView derived class of the application and create a new edit control on the dialog template. With focus on the edit control, start ClassWizard and then press CTRL+W, choose the Member Variables tab, and click "Add Variable," or by just holding the CTRL key down and double-clicking the edit control. This will bring you to the Add Member Variable dialog box. In this dialog box the Member Variable Name combo box will not contain any of the CRecordSet derived class's CTime member variables associated with the date/time fields of the data source, and therefore no date/time field can be associated with the edit control. A potential source of errors can be attempting to force a CTime object into an edit control using a previously existing DDX_FieldText() function call within the CRecordView derived class's DoDataExchange(). For example, calling CTime::Format() inside of the call to DDX_FieldText() so that its returned CString is passed to DDX_FieldText() can result in one of the following errors: Maximum number of characters is 38. Please enter no more than 38 characters. Please enter no more than 7 characters. General Protection Fault. Debug Assertion Failed! File: dbcore.cpp Line: 2325 If a CString returned from CTime::Format() is passed to DDX_FieldText(), make sure that a non local member CString variable is set to the return value of CTime::Format(). Otherwise the CString returned will be considered temporary and will be destroyed when the DoDataExchange() function returns, possibly causing the functions accessing it to corrupt memory. CAUSE ===== There is a pre-defined Record Field Exchange (RFX) routine, RFX_DATE(), that can read a date from an ODBC data source into a CTime object. However, there is no predefined dialog data exchange (DDX) routine for CTime objects. The overloaded DDX_FieldText() function does not support CTime objects. This is because a CTime object represents both a date and a time of day and there is no simple conversion that represents both of these in an edit control. RESOLUTION ========== DDX_FieldText() DDX routines can be written for any data type. These functions are overloaded, and new editions of the function can be created to provide the same functionality for any data type. This is necessary for an application to support the use of date fields. To support date fields, implement a user-defined DDX routine for CTime objects. A call to this function can be included in the CRecordView::DoDataExchange() method of your record view class. A sample implementation of such a function is included at the end of this article. More information on DDX routines can be found in the Microsoft Foundation Classes Technical Note #26, "DDX and DDV Routines," and in the online documentation for the CDataExchange class. NOTE: DDX_FieldText is for use with CRecordView class derived objects, but the following routine can easily be modified for use with CFormView and CDialog derived classes. DISCLAIMER: This sample code is NOT for international aware applications. It is meant as an example only, and will only validate dates of the mm/dd/yy format. Sample Code ----------- /* Compile options needed: Default AppWizard Foundation Classes options. */ /////////////////////////////////////////////////////////////////////// // Example RecordView Data Exchange #include void DDX_FieldText( CDataExchange *pDX, UINT nID, CTime& datevar, CRecordset *pSet ); void CMyRecordView::DoDataExchange(CDataExchange* pDX) { CRecordView::DoDataExchange(pDX); //{{AFX_DATA_MAP(CTranView) // ... ( class wizard / app wizard generated code ) //}}AFX_DATA_MAP // Date DDX DDX_FieldText(pDX, IDC_EDIT_TRAN_DATE, m_pSet->m_Database_Date, m_pSet ); } /////////////////////////////////////////////////////////////////////// // Example Date DDX_FieldText function void DDX_FieldText( CDataExchange *pDX, UINT nID, CTime& datevar, CRecordset *pSet ) { if ( pDX->m_bSaveAndValidate ){CString strDate; pDX->PrepareEditCtrl( nID ); pDX->m_pDlgWnd->GetDlgItem( nID )->GetWindowText( strDate ); int nMonth, nDay, nYear; int nLen = strDate.GetLength(); // Parse the date string for mm/dd/yy format. BOOL status = FALSE; while ( !status ) { int i = 0; int nMarker = 0; // Note that sscanf() could be used to read // these formated strings. // Find first slash. while( i < nLen && strDate[i++] != '/' ); if ( i == nLen ) break; // Month nMonth = atoi( strDate.Left( i ) ); if ( nMonth < 1 || nMonth > 12 ) break; // Find next slash. nMarker = i++; while( i < nLen && strDate[i++] != '/' ); if ( i == nLen ) break; // Day nDay = atoi( strDate.Mid( nMarker, i - nMarker ) ); if ( nDay < 1 || nDay > 31 ) break; // Year if ( nLen - i < 2 || ! isdigit( (int) strDate[i] ) || ! isdigit( (int) strDate[i+1] ) ) break; nYear = atoi( strDate.Right( nLen - i ) ); nYear += ( nYear < 37 ? 2000 : 1900 ); // Valid years for CTime object if ( nYear < 1970 || nYear > 2036 ) break; CTime tTemp( nYear, nMonth, nDay, 0, 0, 0 ); datevar = tTemp; status = TRUE; } if ( !status ){AfxMessageBox( "Incorrect date field format", MB_OK | MB_ICONEXCLAMATION ); pDX->Fail(); } } else { // NOTE: no internationalization: mm/dd/yy format only. CString strDate = datevar.Format( "%m/%d/%y" ); pDX->m_pDlgWnd->GetDlgItem( nID )->SetWindowText( strDate ); } } REFERENCES ========== Microsoft Foundation Classes Technical Note #26: "DDX and DDV Routines" Microsoft Foundation Classes Technical Note #43: "RFX Routines" Documentation for CDataExchange class and Documentation for the CRecordView::DoDataExchange() function can be found by querying in the Microsoft Visual C++ "Books Online" documentation. Additional query words: database recordview recordset ====================================================================== Keywords : kbDatabase kbMFC kbODBC kbVC Technology : kbAudDeveloper kbMFC Version : WINDOWS:1.0,1.5,1.51,1.52 Issue type : kbprb ============================================================================= THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY. Copyright Microsoft Corporation 2001.