HomeC&C++WainTutorialsSamplesTip&TrickTools


Now you can draw nice images, would it not be nice to be able to save them in a file and read them back in? First we must add functions to our object classes which can write and read themself to and from a file, or more general a stream.
The footprint for the functions can be seen here:
class ObjectBaseClass
{
public:
   virtual void SetEnd(HDC &aDc, POINT aEnd) = 0;
   virtual void Draw(HDC &dc, bool aMoving = false)  = 0;
   virtual void Write(std::ostream &os) = 0;
   virtual bool Read(std::istream &is) = 0;
};
As both the line and the rectangle class needs to read and write a POINT to/from the file, common operators for these operations is handy:
std::ostream &operator << (std::ostream &os, const POINT aP)
{
   os << aP.x << " " << aP.y;
   return os;
}

std::istream &operator >> (std::istream &is, POINT &aP)
{
   is >> aP.x;
   is >> aP.y;
   return is;
}
With these we can make the LineClass::Read:
bool LineClass::Read(std::istream &is)
{
   is >> Start;
   is >> End;
   is >> Color;
   return is;
}
And LineClass::Write:
void LineClass::Write(std::ostream &os)
{
   os << LineObj << std::endl;
   os << Start << std::endl;
   os << End << std::endl;
   os << Color << std::endl;
}
The same functions for the rectangle class are very similar, so I don't show them here.
Now we will add a proper handler for the Open and Save messages in the window proc:
         case FileOpenCmd:
            OnOpen();
            break;
         case FileSaveCmd:
            OnSave();
            break;
The OnSave function should ask the user for a filename, and save the file. We use GetSaveFileName to ask for a filename when saving:
void OnSave()
{
   static char Filter[] = "dat (*.dat)\0*.dat\0All (*.*)\0*.*\0";
   OPENFILENAME OpenFilename;
   memset(&OpenFilename, 0, sizeof(OpenFilename));
   OpenFilename.lStructSize = sizeof(OpenFilename);
   OpenFilename.hwndOwner = MainWindow;
   OpenFilename.hInstance = InstanceHandle;
   OpenFilename.lpstrFilter = Filter;
   OpenFilename.lpstrFile = CurrentFileName;
   OpenFilename.nMaxFile = sizeof(CurrentFileName);
   OpenFilename.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
   OpenFilename.lpstrDefExt = "dat";

   if(GetSaveFileName(&OpenFilename))
   {
GetSaveFileName displays a normal file save dialogbox.
If the if(...) is true the user has entered a filename, and pressed the OK button, so we can save the file:
      std::ofstream File(CurrentFileName);
      if(File)
      {
         size_t i;
         for(i = 0; i < Objects.size(); i++)
         {
            Objects[i]->Write(File);
         }
         SetStatusBarText(CurrentFileName, 1);
      }
      else
      {
         std::string Msg = "Failed to open:\r\n";
         Msg += CurrentFileName;
         MessageBox(MainWindow,
                    Msg.c_str(),
                    "Whatever",
                    MB_OK | MB_ICONWARNING);
      }
   }
}
The line: SetStatusBarText(...); displays the name of the file in the middle part of the statusbar.
To read the file is almost as simple, first we forget all objects we have in the current image:
void OnOpen()
{
   size_t i;
   for(i = 0; i < Objects.size(); i++)
      delete Objects[i];
   Objects.clear();
Then we ask the user to enter the filename:
   static char Filter[] = "dat (*.dat)\0*.dat\0All (*.*)\0*.*\0";
   OPENFILENAME OpenFilename;
   memset(&OpenFilename, 0, sizeof(OpenFilename));
   OpenFilename.lStructSize = sizeof(OpenFilename);
   OpenFilename.hwndOwner = MainWindow;
   OpenFilename.hInstance = InstanceHandle;
   OpenFilename.lpstrFilter = Filter;
   OpenFilename.lpstrFile = CurrentFileName;
   OpenFilename.nMaxFile = sizeof(CurrentFileName);
   OpenFilename.Flags = OFN_FILEMUSTEXIST;
   OpenFilename.lpstrDefExt = "dat";
   if(GetOpenFileName(&OpenFilename))
GetOpenFileName does almost the same as GetSaveFileName, expect that it alows you to enter a file to open
Now we can read the objects from the file, and put them into our vector:
      std::ifstream File(CurrentFileName);
      if(File)
      {
         int ObjType;
         while(File >> ObjType)
         {
            ObjectBaseClass *Obj;
            if(ObjType == LineObj)
               Obj = new LineClass();
            else if(ObjType == RectangleObj)
               Obj = new RectangleClass;
            else
               MessageBox(MainWindow,
                          "Unknown Object Type",
                          "Whatever",
                          MB_OK);
            Obj->Read(File);
            Objects.push_back(Obj);
         }
         SetStatusBarText(CurrentFileName, 1);
      }
      else
      {
         std::string Msg = "Failed to open:\r\n";
         Msg += CurrentFileName;
         MessageBox(MainWindow,
                    Msg.c_str(),
                    "Whatever",
                    MB_OK|MB_ICONWARNING);
      }
   }
   InvalidateRect(MainWindow, 0, TRUE);
}
The call to InvalidateRect will erase anything on the screen, which will cause WM_PAINT to be received, and we will Draw the new image.
The complete code for the application can be found here