Lab 6: Working with Persistent Data

In this lab, you will enable the STUpload application to work with persistent data contained in files on your local hard disk. You will modify the application in two sections. First, you will finish implementing the CSTUploadDoc::LoadData() function that you created in Chapter 5. The CSTUploadDoc::OnDataImport() function calls the LoadData() function to load data from a text file into the application.

Second, you will implement standard MFC serialization for the STUpload application so that the application data imported from the text file can be saved as an STUpload document file.

Importing Data from a Text File

In Lab 5, you created a temporary implementation of the CSTUploadDoc::LoadData() function. Currently, this function simply adds some hard-coded stock price records to the CStockDataList object CSTUploadDoc::m_DocList. You will finish implementing the LoadData() function to load records into m_DocList from the CStdioFile object passed in from the CSTUploadDoc::OnDataImport() function. Your first task is to use the CStdioFile object to open the text file selected by the user.

  • To open the text file
    1. Edit the CSTUploadDoc::OnDataImport() function. Locate the code branch that begins with the line of code:
    2. if(nID == IDOK)

    3. After the CStdioFile object declaration, add the following code:
    4. (This code can be found in CH6_01.cpp, installed from the companion CD.)

      CFileException fx; if(!aFile.Open(aFileDialog.GetPathName(), CFile::modeRead |       CFile::typeText, &fx)) {      TCHAR buf[255];      fx.GetErrorMessage(buf, 255);      CString strPrompt(buf);      AfxMessageBox(strPrompt);      return; }

      The entire function should now look as follows:

      void CSTUploadDoc::OnDataImport() {      // String to customize File Dialog      CString strFilter =            Data Files (*.dat)|*.dat|All Files (*.*)|*.*||";      CFileDialog aFileDialog(TRUE, NULL, NULL, OFN_HIDEREADONLY |            OFN_OVERWRITEPROMPT, strFilter);      int nID = aFileDialog.DoModal();      if(nID == IDOK)      {           CStdioFile aFile;           CFileException fx;           if(!aFile.Open(aFileDialog.GetPathName(),                 CFile::modeRead | CFile::typeText, &fx))           {                TCHAR buf[255];                fx.GetErrorMessage(buf, 255);                CString strPrompt(buf);                AfxMessageBox(strPrompt);                return;           }           LoadData(aFile);      } }

    Before you replace the old LoadData() function with a new version, you will make a couple of changes to enable the new function to use the Conflicting Records dialog box. First, you must call the AfxInitRichEdit() function to initialize rich edit controls for your application.

  • To enable the use of rich edit controls by an application
    1. Edit the CSTUploadApp::InitInstance() function. Add the following line just before the return statement near the end of the function:
    2. AfxInitRichEdit();

    3. Add a DDX CConflictDialog::m_REditText member variable. This vari-able will be a CString variable used to set the data displayed in the rich edit control.

  • To add the CConflictDialog::m_REditText variable
    1. Open ClassWizard.
    2. Click the Member Variables tab.
    3. Add a CString variable m_REditText to the CConflictDialog class, which is associated with the IDC_DUPL_RICHEDIT resource ID.

    Now you are ready to implement the new LoadData() function.

  • To replace the LoadData() function
    1. To the top of the STUploadDoc.cpp file, with the other #include statements, add the following line:
    2. #include "ConflictDialog.h"

    3. Locate the CSTUploadDoc::LoadData() function. Delete the entire function and replace it with the following code:
    4. (This code can be found in CH6_02.cpp, installed from the companion CD.)

       BOOL CSTUploadDoc::LoadData(CStdioFile &infile) {      // Check for NULL      ASSERT(infile.m_hFile != NULL);      // Hold data in temporary list of CStockData objects,      // which we assign to CSTUploadDoc::m_DocList only      // when we are sure load has been completed successfully      CStockDataList TempList;      // Additions are cumulative, so we need to copy in existing data      TempList.AddHead(&m_DocList);      // Line buffer      CString strTemp;      // Today's date      COleDateTime Today = COleDateTime::GetCurrentTime();      COleDateTime FileDate;      CString strFileHeader;      int addedCtr = 0;     // Count added items      int discardedCtr = 0;     // Count discarded items      BOOL bFirstLine = TRUE;      while(infile.ReadString(strTemp))      {           BOOL bValidDate = FALSE;           CString strFund;           CString strDate;           // Exclude blank lines           if(strTemp.GetLength() == 0) continue;           if(bFirstLine)           {                // Get Header information                strFileHeader = strTemp.Left(18);                strFileHeader.TrimRight();                strDate = strTemp.Mid(18, 10);           }           else           {                strFund = strTemp.Left(8);                strFund.TrimRight();                strDate = strTemp.Mid(8, 10);           }           int nYear = atoi(strDate.Right(4));           int nMonth = atoi(strDate.Left(2));           int nDay = atoi(strDate.Mid(3, 2));           COleDateTime aDate(nYear, nMonth, nDay, 0, 0, 0);      if(aDate.GetStatus() != COleDateTime::valid)           {                if(bFirstLine)                {                     // Cannot read file date - assume invalid                     AfxMessageBox("Invalid File Format");                     return FALSE;                }                else                {                     // Cannot read record date - discard line                     discardedCtr++;                     continue;                }           }           if(bFirstLine)           {                // Get file date - loop back to top                FileDate = aDate;                bFirstLine = FALSE;                continue;           }           double dPrice = atof(strTemp.Mid(19));           // Make a CStockData object and add it           // to our temporary array           CStockData aStData(strFund, aDate, dPrice);           CStockDataList::errorstatus err;           POSITION CurPos = TempList.AddSorted(aStData, err);           switch(err)           {                // Discard identical entry                case CStockDataList::duplicate_entry :                     discardedCtr ++ ;                     continue;                // Same record, different price value                case CStockDataList::conflicting_entry :                {                     // Query if user wants to discard duplicate,                      // replace, or abort                     CConflictDialog aDialog;                     // Construct text to appear in rich edit                      // control                     CString strText = "Existing entry:\n\n";                     CStockData SDTemp = TempList.GetAt(CurPos);                     strText += SDTemp.GetAsString();                     strText += "\n\nReplacement entry:\n\n";                     strText += aStData.GetAsString();                     // Assign text to control variable                     aDialog.m_REditText = strText;                     switch(aDialog.DoModal())                     {                          case IDABORT : // Abandon                          return FALSE;                          case IDCANCEL : // Discard new record                          discardedCtr++ ;                          continue;                          case IDOK : // Replace existing record                          TempList.SetAt(CurPos, aStData);                     }                }           default:  // Ok                addedCtr++ ;      } } // If we got this far then this is a valid record CString strPrompt; strPrompt.Format(      "Import of file %s complete:\nRecords loaded: %d \      \nRecords discarded: %d  \      \n\nHit OK to load data into document.",      strFileHeader, addedCtr, discardedCtr); if(AfxMessageBox(strPrompt, MB_OKCANCEL) == IDOK) {      // Update document data      m_DocList.RemoveAll();      m_DocList.AddHead(&TempList);      // Update fund view      CMainFrame * pWnd =           dynamic_cast<CMainFrame *> (AfxGetMainWnd());      if(pWnd)      {           pWnd->UpdateFundList(m_DocList);           // Show fund window after loading new funds           pWnd->SetFundsVisible(TRUE);      }      return TRUE; } else      return FALSE; } 

    5. Look through the code, and make sure that you understand how:
      • A temporary list is used to hold the data that is loaded so that the application data is not modified until the load process has completed successfully, and the user has approved the import.
      • The routine distinguishes between a header line and a data line.
      • The header line is parsed to ensure that the correct type of document is being loaded.
      • CString member functions are used to extract data from a data line.
      • The routine deals with invalid lines, duplicate lines and conflicting entries (same fund and date, different price). You might want to pay close attention to the CStockDataList::AddSorted() function.

    6. Build and run the STUpload application. Try using the Import option from the Data menu to load the Ch6Test.dat file from the ..\Chapter 6\Data folder on the companion CD-ROM. Make sure that the data loads as expected. Close the application and run it again. This time load the conflict.dat file from the ..\Chapter 6\Data folder. This file contains a conflicting record to allow you to test the conflict handling routine.

    Implementing STUpload Serialization

    STUpload application data consists of a single CStockDataList object that is a collection of CStockData objects. The CStockData object encapsulates a fund name, a date and a price. The document also contains a CString variable that records the currently selected fund name. This variable should also be serialized, so that the fund that was currently selected when the document file was saved will still be selected when the file is restored.

    To serialize the STUpload application data, you will need to:

    • Make CStockData a serializable class.
    • Provide an implementation of the SerializeElements() function template for the CStockData element type (This is necessary because CStockData contains a CString).
    • Implement the CSTUploadDoc::Serialize() function.
    • Implement the CSTUploadDoc::DeleteContents() function to clear out the data held in the document object before it is reused.
    • Add calls to CDocument::SetModifiedFlag() wherever the application data is altered so that the framework will prompt the user to save changes before closing a modified document.

    Making CStockData a Serializable Class

    The CStockData class that we have provided is directly derived from the CObject class, and it includes a default constructor. You will need to add the serialization macros and the Serialize() function.

  • To add the serialization macros
    1. Open the StockData.h file to edit the CStockData class definition.
    2. To the top of the class declaration, in the public section, add the following line:
    3. DECLARE_SERIAL(CStockData)

    4. Open the StockData.cpp file. At the top of the file, below the preprocessor statements, add the following line:
    5. IMPLEMENT_SERIAL(CStockData, CObject, 1)

  • To add the Serialize() function
    1. Switch back to the StockData.h file.
    2. Add the following declaration to the public section of the CStockData class definition:
    3. virtual void Serialize(CArchive& ar);

    4. Switch back to the StockData.cpp file. At the end of the file, add the following code:
    5. (This code can be found in CH6_03.cpp, installed from the companion CD.)

      void CStockData::Serialize(CArchive& ar) {      if (ar.IsStoring())      {           ar << m_strFund;           ar << m_date;           ar << m_dblPrice;      }      else      {           ar >> m_strFund;           ar >> m_date;           ar >> m_dblPrice;      } }

    Overriding SerializeElements()

    You will now implement the function template SerializeElements() for the CStockData element type. The function will simply iterate across the collection data and call Serialize() for each CStockData object.

  • To override the SerializeElements() function
    1. Open the StockDataList.h file.
    2. At the end of the file, before the #endif statement and after the end of the CStockDataList class declaration, add the following line:
    3. template <> void AFXAPI SerializeElements <CStockData>      (CArchive& ar, CStockData* pNewSD, int nCount);

    4. Open the StockDataList.cpp file.
    5. At the end of the file, add the following code:
    6. (This code can be found in CH6_04.cpp, installed from the companion CD.)

      template <> void AFXAPI SerializeElements <CStockData>      (CArchive& ar, CStockData* pNewSD, int nCount) {      for (int i = 0; i < nCount; i++, pNewSD++)      {           // Serialize each CStockData object           pNewSD->Serialize(ar);      } }

    Implementing the CSTUploadDoc::Serialize() Function

    At this point, you are ready to implement the document serialization code.

  • To implement the CSTUploadDoc::Serialize() function
    1. Locate the CSTUploadDoc::Serialize() function.
    2. Replace the function with the following version:
    3. (This code can be found in CH6_05.cpp, installed from the companion CD.)

      void CSTUploadDoc::Serialize(CArchive& ar) {      m_DocList.Serialize(ar);      if (ar.IsStoring())      {           ar << m_strCurrentFund;      }      else      {           ar >> m_strCurrentFund;           // Update Select Fund window           CMainFrame* pWnd = dynamic_cast<CMainFrame *>                 (AfxGetMainWnd());           if(pWnd)           // Will fail if running from icon or from           // command line with file name argument           {                // Update and show fund window                pWnd->UpdateFundList(m_DocList, m_strCurrentFund);                pWnd->SetFundsVisible(TRUE);           }      } }

    All the stock data records are serialized in a single call to CStockDataList:: Serialize(). The CSTUploadDoc::m_strCurrentFund data member is also serialized.

    Displaying the Select Fund Window

    Take note of the code that displays the fund window when a document file is loaded. This is not possible here when the application is launched by double-clicking a document file icon, as the pointer to the main window is not available at this point. However, you can instruct the application to display the fund window after the main window has been created, if it detects that a document has already been loaded.

  • To display the Select Fund window at application startup
    1. Locate the CSTUploadApp::InitInstance() function.
    2. At the end of the function, just before the return statement, add the following code:
    3. (This code can be found in CH6_06.cpp, installed from the companion CD.)

      CMainFrame * pFrameWnd =      dynamic_cast<CMainFrame *> (m_pMainWnd); ASSERT_VALID(pFrameWnd); CSTUploadDoc * pDoc =      dynamic_cast<CSTUploadDoc *>(pFrameWnd->GetActiveDocument()); ASSERT_VALID(pDoc); if(pDoc->GetDocList().GetCount() > 0) // Non-empty document at main window creation time means we are // running from icon or from command line with file name argument {      pFrameWnd->UpdateFundList(pDoc->GetDocList(),            pDoc->GetCurrentFund());      pFrameWnd->SetFundsVisible(TRUE); }

    Implementing DeleteContents()

    Because STUpload is an SDI application, any data contained in the document object must be cleared out by the DeleteContents() function.

  • To implement the CSTUploadDoc::DeleteContents() function
    1. Use ClassWizard to overload the DeleteContents() function for the CSTUploadDoc class.
    2. Edit the function code. Replace the //TODO comment line with the following code:
    3. (This code can be found CH6_07.cpp, installed from the companion CD.)

      m_DocList.RemoveAll(); CMainFrame * pWnd =      dynamic_cast<CMainFrame *> (AfxGetMainWnd()); if(pWnd) {      pWnd->UpdateFundList(m_DocList);      // No funds on file, so hide fund window      pWnd->SetFundsVisible(FALSE);      // And reset current fund value      SetCurrentFund(""); }

    Notifying Document Data Modification

    Finally you must add calls to CDocument::SetModifiedFlag() whenever the persistent document data is changed. In the STUpload application the data is modified in two places:

    • In the CSTUploadDoc::OnDataImport() function, after the LoadData() function has returned with a TRUE value, to indicate that records have been successfully imported from a text file.
    • In the CFundDialog::OnSelchangeFundlist() function, when the user changes the currently selected fund.

  • To modify the CSTUploadDoc::OnDataImport() function
    1. Locate the line in the OnDataImport() function that reads:
    2. LoadData(aFile);

    3. Replace this line with the following code:
    4. if(LoadData(aFile)) {      SetModifiedFlag();      UpdateAllViews(NULL); }

  • To modify the CFundDialog::OnSelchangeFundlist() function
  • Locate the CFundDialog::OnSelchangeFundlist() function. At the end of the function string, before the closing brace, add the line:

    pDoc->SetModifiedFlag();

    Testing Serialization

  • To build and test the STUpload application
    1. Run the application, then use the Import option from the Data menu to load data from the ..\Chapter 6\Data\Test.dat file on the companion CD.
    2. Select a fund to view, then close the application. When prompted, save the document as MyFile.stu.
    3. Restart the application. From the File menu, choose Open, then select and open the MyFile.stu file.
    4. Select a new fund to view and close the application, saving your changes.
    5. Use Windows Explorer to locate the MyFile.stu file, and launch the STUpload application by double-clicking the file icon.
    6. Check that the file is restored correctly.


    Microsoft Press - Desktop Applications with Microsoft Visual C++ 6. 0. MCSD Training Kit
    Desktop Applications with Microsoft Visual C++ 6.0 MCSD Training Kit
    ISBN: 0735607958
    EAN: 2147483647
    Year: 1999
    Pages: 95

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net