This site is moving to The Zen Source Library
Please Update your bookmarks


Saving a TreeView to a DocFile

Here is a semi-practical example demonstrating the usefulness of a DocFile. A very common requirement for an application that uses a TreeView, is to save the TreeView to disk.

Form many people the TreeView's SaveToFile method is just not good enough as it only saves the tree structure. All TTreeNode information like images, data etc is lost.

To keep the example short I've used my TDocFile classes to simplify things.


Saving to a DocFile


   procedure Save_CallBack(  Node : TTreeNode;  pData : pointer  );
   var
      stor : TDocFileStorage;
      subStor : TDocFileStorage;
      strmImages : TDocFileStream;
   begin
         {End of recursion (base case)}
      if(  (Node = nil) or (pData = nil)  ) then
         Exit;

      stor := TDocFileStorage(pData);

         {Create a storage for this node
           NB Remember the limits on a storage element's name,
           (31 chars max etc...)}
      subStor := stor.CreateStorage(  Node.Text,  MY_STGM_CREATE  );

         {Was storage created}
      if(  subStor = nil  ) then
      begin
         ShowMessage(  'Error creating sub-storage'  );
         Exit;
      end;

         {Open the stream for saving the image info to}
      strmImages := subStor.CreateStream(  'Images',  MY_STGM_CREATE  );

         {Images stream created?}
      if(  strmImages = nil  ) then
      begin
         ShowMessage(  'Error Creating stream'  )
      end
      else begin
            {Save image and selected index}
         strmImages.WriteString(   IntToStr(  Node.ImageIndex  ) + #13#10 +
                                   IntToStr(  Node.SelectedIndex  )
                                );

      end;

         {Save all children}
      if(  Node.GetFirstChild <> nil  ) then
         Save_CallBack(  Node.GetFirstChild,  subStor  );

         {Save all siblings}
      if(  Node.GetNextSibling <> nil  ) then
         Save_CallBack(  Node.GetNextSibling,  stor  );


      strmImages.Free;
      subStor.Free;
   end;



   procedure TForm1.but_SaveClick(Sender: TObject);
   var
      storFile : TDocFileStorage;
   begin
         {Create the file}
      storFile := CreateDocFile(   ExtractFilePath(  Application.ExeName  ) + '\z.ole',
                                   MY_STGM_CREATE
                                );

      if(  storFile = nil  ) then
      begin
         ShowMessage(  'Cant create file'  );
         Exit;
      end;

         {Start saving}
      Save_CallBack(  tv_eg5.Items[ 0 ],  storFile  );

         {Done}
      storFile.Free;
   end;


When the save button is clicked the following occurs

  1. A DocFile is created
  2. Saving of the TreeView is started by calling Save_CallBack passing it the node (root node) to start saving from and storage (root storage) to start saving to
  3. DocFile is closed

The Save_CallBack function works as follows

  1. Check that there is a valid node + DocFile to work with
  2. Create a storage for the current node
  3. Create a stream in the new storage
  4. Save the node's image info to the stream
  5. Save the nodes children (recursive)
  6. Save the node's siblings (recursive)



Load data from a DocFile

Loading the data back from the DocFile is slightly (only just) more complex than saving it.



   procedure Load_CallBack(  Node : TTreeNode;  pData : pointer  );
   var
      stor : TDocFileStorage;
      substor : TDocFileStorage;
      strmImages : TDocFileStream;
   begin
      stor := TDocFileStorage(pData);
         {Open the storage for this element}
      substor := stor.OpenStorage(  Node.Text,  MY_STGM_OPEN  );

         {Storage opened?}
      if(  substor = nil  ) then
      begin
         ShowMessage(  'Cant open storage ' + Node.Text  );
         Exit;
      end;

         {Open the images stream}
      strmImages := substor.OpenStream(  'Images',  MY_STGM_OPEN  );

         {Stream open?}
      if(  strmImages <> nil  ) then
      begin
         with TStringList.Create do
         begin
               {Load the stream's data into a TString List}
            LoadFromStream(  strmImages  );

            if(  Count > 0  ) then
            begin
                  {Get the saved image index}
               Node.ImageIndex := StrToInt(  Strings[ 0 ]  );
   
               if(  Count > 1  ) then
                     {Get the saved selected index}
                  Node.SelectedIndex := StrToInt(  Strings[ 1 ]  );
            end;

            Free;
         end;

            {Close the stream}
         strmImages.Free;
      end;

         {Enum all sub-elements}
      substor.EnumElements(  Form1.LoadName_callback,  substor  );

         {Done with storage}
      substor.Free;
   end;



   function TForm1.LoadName_callback(  sElementName : WideString;
                                       dwType : DWORD;
                                       pData : pointer
                                     ) : boolean;
   var
      Node : TTreeNode;
      OldNode : TTreeNode;
   begin
         {Continue enum}
      Result := true;

         {Only interested in storages}
      if(  dwType <> STGTY_STORAGE  ) then
         Exit;

         {Save the node that is currently selected}
      OldNode := tv_eg5.Selected;

         {Add a new node for this element}
      Node := tv_eg5.Items.AddChild(  tv_eg5.Selected,  sElementName  );
         {Select new node} 
      Node.Selected := true;

         {Load data and all sub-elements}
      Load_CallBack(  Node,  pData  );

         {Select old node again}
      if(  OldNode <> nil  ) then
         OldNode.Selected := true;
   end;



   procedure TForm1.but_LoadClick(Sender: TObject);
   var
      storFile : TDocFileStorage;
   begin
         {Open the file}
      storFile := OpenDocFile(   ExtractFilePath(  Application.ExeName  ) + '\z.ole',
                                 MY_STGM_OPEN
                              );

         {Was the DocFile opened}
      if(  storFile = nil  ) then
      begin
         ShowMessage(  'Cant open file'  );
         Exit;
      end;

        {Remove all tree nodes}
      tv_eg5.Items.Clear;

         {Enum all root level elements in the storage file}
      storFile.EnumElements(  LoadName_callback,  storFile  );

         {Done}
      storFile.Free;
   end;


As you can see there are three functions involved here, the loading (and recursion) takes place beteen LoadName_callback and Load_CallBack.


The following picture depicts the flow of execution


When the load button is clicked

  1. Open the DocFile
  2. Clear the Tree
  3. Start enumerating all items in the root storage. This passed control to LoadName_callback
  4. Close the DocFile


LoadName_callback

  1. Check if the element is a storage
  2. Get the selected node
  3. Add a new node to the TreeNode
  4. Select the new node
  5. Call Load_CallBack to load the storages info
  6. Select the original node again


Load_CallBack

  1. Open the sub-storage for this storage element
  2. Open the images stream
  3. Load the image info from the stream
  4. Close the stream
  5. Emum all elements in the sub-storage, call LoadName_callback
  6. Close the sub-storage


This example does not show saving of the TTreeNode's data to the DocFile. The reason for that is that saving the TTreeNode's data is very similar to saving the image info to the stream. Natrally the exact method will be differant depending on what type of data TTreeNode.Data represents.


The full source code for this example is available as a zip file on the source code page






All information on these www pages is copyright (©) 1997 Andre .v.d. Merwe And may not be copied or mirrored without my permission.