Tutorials

 

Home
About
How it Works
Documentation
Tutorials

Examples

Links
Contact

 

 

Menu of tutorials

Tutorial 1:   The Simplest Window
Tutorial 2:   Using Classes and Inheritance
Tutorial 3:   Using Messages to Create a Scribble Window
Tutorial 4:   Repainting the Window
Tutorial 5:   Wrapping a Frame around our Scribble Window
Tutorial 6:   Customising Window Creation
Tutorial 7:   Customising the ToolBar
Tutorial 8:   Loading and Saving Files
Tutorial 9:   Printing
Tutorial 10: Print Previewing
Tutorial 11: Finishing Touches

Tutorial 8:  Loading and Saving Files

In this tutorial we will demonstrate how to save our data to a file, and load it back again.

Transfering Data between CView and CDoc

In the previous tutorial our plotpoint data was stored in a vector member variable of CView. In this tutorial we move this data to the CDoc class. The code to load and save our data to a disc file is added to CDoc as well. Separating the data from the view like this is often referred to as a document/view architecture.

Our CDoc class is made a member variable of the CView class.

class CMainFrame : public CFrame
{
public:
  CMainFrame();
  virtual ~CMainFrame();

  CDoc& GetDoc() { return m_view.GetDoc(); }
  void LoadFile(LPCTSTR str);
  
  LRESULT OnDropFile(WPARAM wparam);

  void OnFileExit();
  void OnFileNew();
  void OnFileOpen();
  void OnFilePrint();
  void OnFileSave();
  void OnFileSaveAs();
  void OnPenColor();

protected:
  virtual BOOL OnCommand(WPARAM wparam, LPARAM lparam);
  virtual void SetupToolBar();
  virtual LRESULT WndProc(UINT msg, WPARAM wparam, LPARAM lparam);

private:
  CView m_view;
  CString m_pathName;

};

Our CView class stores the PlotPoint data in CDoc. It uses the following functions to access CDoc. CView::GetDoc returns a reference to CDoc, and CView::GetPoints returns a reference to the vector containing the PlotPoints

CDoc& CView::GetDoc()
{
  return m_doc;
}

std::vector& CView::GetAllPoints()
{ 
  return GetDoc().GetAllPoints(); 
}

Saving Data

To save the data we perform the following steps:

  • Use CFileDialog to open a dialog and choose the filename to save the data to.
  • Use CArchive to write the data to the file.

This code segment shows how to use the SaveFileDialog function to choose the file to write to.

void CMainFrame::OnFileSaveAs()
{
  CFileDialog fileDlg(FALSE, _T("dat"), 0, OFN_OVERWRITEPROMPT, _T("Scribble Files (*.dat)\0*.dat\0\0"));
  fileDlg.SetTitle(_T("Save File"));

  // Bring up the file open dialog retrieve the selected filename.
  if (fileDlg.DoModal(*this) == IDOK)
  {
    CString str = fileDlg.GetPathName();

    // Save the file.
    if (GetDoc().FileSave(str))
    {
      // Save the file name.
      m_pathName = str;
      AddMRUEntry(m_pathName);
    }
  }
}

This next code segment demonstrates how to write the contents of CDoc to an archive. Note that a failure to write to the archive throws a FileException. Here we use a catch to display the relevant information if writing to the archive fails.

BOOL CDoc::FileSave(LPCTSTR szFilename)
{
  BOOL isFileSaved = FALSE;

  try
  {
    CArchive ar(szFilename, CArchive::store);
    ar << *this;
    isFileSaved = TRUE;
  }
  catch (const CFileException &e)
  {
    // An exception occurred. Display the relevant information.
    ::MessageBox(NULL, e.GetText(), _T("Failed to Save File"), MB_ICONWARNING);
  }

  return isFileSaved;
}

CDoc inherits from CObject. This provides CDoc with a virtual Serialize function we can override to define how the CDoc class stores itself in the archive. Within the Serialize function we define, CArchive's IsStoring function is used to test if the archive is loading or storing so we can take the appropriate actions.

This is the Serialize function for CDoc. When storing, we first store the number of PlotPoints, and then we store an ArchiveObject that contains each PlotPoint. When loading, we read the number of PlotPoints, and then read an ArchiveObject containing each PlotPoint. The PlotPoints are then pushed into the our vector.  

void CDoc::Serialize(CArchive& ar)
// Uses CArchive to stream data to or from a file
{

  if (ar.IsStoring())
  {
    // Store the number of points.
    UINT points = GetPoints().size();
    ar << points;
		
    // Store the PlotPoint data.
    std::vector::iterator iter;
    for (iter = GetPoints().begin(); iter < GetPoints().end(); ++iter)
    {
      ArchiveObject ao( &(*iter), sizeof(PlotPoint) );
      ar << ao;
    }
  }
  else
  {
    UINT points;
    PlotPoint pp = {0};
    GetPoints().clear();

    // Load the number of points.
    ar >> points;

    // Load the PlotPoint data.
    for (UINT u = 0; u < points; ++u)
    {
      ArchiveObject ao( &pp, sizeof(pp) );
      ar >> ao;
      GetPoints().push_back(pp);
    }
  }

}

Loading Data

Reading data back from a file follows a rather similar process. The steps involved are as follows:

  • Use CFile::OpenFileDialog to open a dialog and choose the file to load.
  • Use CArchive to retrieve the data from the file.

The following code segment demonstrates how to use OpenFileDialog to choose the file to load.

void CMainFrame::OnFileOpen()
{
  CFileDialog fileDlg(TRUE, _T("dat"), 0, OFN_FILEMUSTEXIST, _T("Scribble Files (*.dat)\0*.dat\0\0"));
  fileDlg.SetTitle(_T("Open File"));

  // Bring up the file open dialog retrieve the selected filename.
  if (fileDlg.DoModal(*this) == IDOK)
  {
    // Load the file.
    LoadFile(fileDlg.GetPathName());
  }
}

This is the code that loads our PlotPoint vector from the contents of the chosen file. Once again we catch the exception and display the result if the archive fails to write to the file. 

BOOL CDoc::FileOpen(LPCTSTR fileName)
{
  GetPoints().clear();
  BOOL isFileOpened = FALSE;

  try
  {
    CArchive ar(fileName, CArchive::load);
    ar >> *this;
    isFileOpened = TRUE;
  }

  catch (const CWinException& e)
  {
    // An exception occurred. Display the relevant information.
    ::MessageBox(NULL, e.GetText(), _T("Failed to Load File"), MB_ICONWARNING);
		
    GetPoints().clear();
  }

  return isFileOpened;
}
void CMainFrame::LoadFile(LPCTSTR fileName)
{
  // Retrieve the PlotPoint data.
  if (GetDoc().FileOpen(fileName))
  {
    // Save the filename.
    m_fileName = fileName;
  }
  else
    m_filehName=_T("");

  GetView().Invalidate();
}

Supporting Drag and Drop

An alternative way to load a file is drop it on the view window. This is known as drag and drop. The steps required to support drag and drop are as follows:

  • Add DragAcceptFiles to CView::OnCreate
  • Add a handler for the WM_DROPFILES message to CView::WndProc
  • Add a CView::OnDropFiles member function.
int CView::OnCreate(CREATESTRUCT&)
{
  // Support Drag and Drop on this window.
  DragAcceptFiles(TRUE);
  return 0;
}
LRESULT CView::WndProc(UINT msg, WPARAM wparam, LPARAM lparam)
{
  switch (uMsg)
  {
    case WM_DROPFILES:	return OnDropFiles(msg, wparam, lparam);
    case WM_LBUTTONDOWN:	return OnLButtonDown(msg, wparam, lparam);
    case WM_MOUSEMOVE:	return OnMouseMove(msg, wparam, lparam);
    case WM_LBUTTONUP:	return OnLButtonUp(msg, wparam, lparam);
  }

  // Use the default message handling for remaining messages.
  return WndProcDefault(msg, wparam, lparam);
}
LRESULT CView::OnDropFiles(UINT msg, WPARAM wparam, LPARAM lparam)
{
  UNREFERENCED_PARAMETER(msg);
  UNREFERENCED_PARAMETER(lparam);

  HDROP drop = (HDROP)wparam;
  UINT length = DragQueryFile(drop, 0, 0, 0);

  if (length > 0)
  {
    CString fileName;
    DragQueryFile(drop, 0, fileName.GetBuffer(length), length+1);
    fileName.ReleaseBuffer();

    // Send a user defined message to the frame window.
    GetParent().SendMessage(UWM_DROPFILE, (WPARAM)fileName.c_str(), 0);

    DragFinish(drop);
  }

  return 0;
}

Also refer to the "Command Line Arguments" section in tutorial 11. Using command line arguments provides support for running the application by dropping a data file on the program's icon or .exe file.

The source code for this tutorial is located within the Tutorial folder of the software available from SourceForge at http://sourceforge.net/projects/win32-framework.