Delphi XE and VirtualStringTree 4.8.7 - Saving and loading data

I'm having a problem saving and loading the nodes of the VirtualStringTree.  

Please explain where the problem is and how to fix it.

type
  PNodeData = ^TNodeData;
  TNodeData = record
    Caption: String;
    Filename: String;
    Description: String;
    FileDateTime: TDateTime;
    ImageIndex: Byte;
    ID: Integer;
  end;

procedure MainForm.TreeSaveNode (Sender: TBaseVirtualTree;
  Node: PVirtualNode; Stream: TStream);
var
  Data: PNodeData;
  Len: integer;
begin
  Data := Tree.GetNodeData(Node);

  Len := Length(Trim(Data^.Caption));
  Stream.write(Len, SizeOf(Len));
  Stream.write(Data^.Caption, Len * SizeOf(Char));

  Len := Length(Trim(Data^.Filename));
  Stream.write(Len, SizeOf(Len));
  Stream.write(Data^.Filename, Len * SizeOf(Char));

  Len := Length(Trim(Data^.Description));
  Stream.write(Len, SizeOf(Len));
  Stream.write(Data^.Description, Len * SizeOf(Char));

  Stream.write(Data^.FileDateTime, SizeOf(Data^.FileDateTime));
  Stream.Write(Data^.ImageIndex, SizeOf(Data^.ImageIndex));
  Stream.Write(Data^.ID, SizeOf(Data^.ID));
end;


procedure MainForm.TreeLoadNode(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Stream: TStream);
var
  Data: PNodeData;
  Len: integer;
begin
  Data := Tree.GetNodeData(Node);

  Stream.Read(Len, SizeOf(Len));
  SetLength(Data^.Caption, Len);
  Stream.Read(Data^.Caption, Len * SizeOf(Char));

  Stream.Read(Len, SizeOf(Len));
  SetLength(Data^.Filename, Len);
  Stream.Read(Data^.Filename, Len * SizeOf(Char));

  Stream.Read(Len, SizeOf(Len));
  SetLength(Data^.Description, Len);
  Stream.Read(Data^.Description, Len * SizeOf(Char));

  Stream.Read(Data^.FileDateTime, SizeOf(Data^.FileDateTime));
  Stream.Read(Data^.ImageIndex, SizeOf(Data^.ImageIndex));
  Stream.Read(Data^.ID, SizeOf(Data^.ID));
end;
ScottCannonAsked:
Who is Participating?
 
Ephraim WangoyaCommented:

Another way you could do this is to save to a TClientDataset. For me it would be much easier to scale a database than a binary file (In case you want to add more fields to your record object)

However the SaveToStream and SaveToFile methods of the TVirtualStringTree are pretty fast

Thanks for your patience
0
 
Ephraim WangoyaCommented:

You are storing the wrong sizes, and also using TRIM when getting length but not when saving
Cast as  Untyped pointer as well
procedure MainForm.TreeSaveNode (Sender: TBaseVirtualTree;
  Node: PVirtualNode; Stream: TStream);
var
  Data: PNodeData;
  Len: integer;
begin
  Data := Tree.GetNodeData(Node);

  Len := Length(Data^.Caption);
  Stream.write(Len, SizeOf(Len));
  Stream.write(Pointer(Data^.Caption)^, Len);

  Len := Length(Data^.Filename);
  Stream.write(Len, SizeOf(Len));
  Stream.write(Pointer(Data^.Filename)^, Len);

  Len := Length(Data^.Description);
  Stream.write(Len, SizeOf(Len));
  Stream.write(Pointer(Data^.Description)^, Len);

  Stream.write(Data^.FileDateTime, SizeOf(Data^.FileDateTime));
  Stream.Write(Data^.ImageIndex, SizeOf(Data^.ImageIndex));
  Stream.Write(Data^.ID, SizeOf(Data^.ID));
end;

var
  Data: PNodeData;
  Len: integer;
begin
  Data := Tree.GetNodeData(Node);

  Stream.Read(Len, SizeOf(Len));
  SetLength(Data^.Caption, Len);
  Stream.Read(Pointer(Data^.Caption)^, Len);

  Stream.Read(Len, SizeOf(Len));
  SetLength(Data^.Filename, Len);
  Stream.Read(Pointer(Data^.Filename)^, Len);

  Stream.Read(Len, SizeOf(Len));
  SetLength(Data^.Description, Len);
  Stream.Read(Pointer(Data^.Description)^, Len);

  Stream.Read(Data^.FileDateTime, SizeOf(Data^.FileDateTime));
  Stream.Read(Data^.ImageIndex, SizeOf(Data^.ImageIndex));
  Stream.Read(Data^.ID, SizeOf(Data^.ID));

Open in new window

0
 
ScottCannonAuthor Commented:
I emplemented your changes and it still has the following issue.

I have this code assigned to the tree OnChange event.

  if not Assigned(Node) then
    Exit;
  Data := Tree.GetNodeData(Node);
  Filename.Text := Data^.Filename;
  Description.Text := Data^.Description;
  ID.Text := IntToStr(Data^.ID);

Moving between nodes, the data is displayed correctly.
If I save the tree and open it, I don't receive any errors but when I change nodes the first half of the data displayed is correct and the second half is wrong.  (i.e. GeneralPurpose.pdf in the Filename becomes GeneralPuDivision).
0
Cloud Class® Course: Amazon Web Services - Basic

Are you thinking about creating an Amazon Web Services account for your business? Not sure where to start? In this course you’ll get an overview of the history of AWS and take a tour of their user interface.

 
Ephraim WangoyaCommented:

Sorry for not replying sooner. I'll take a look at this again later today or tomorrow morning
0
 
ScottCannonAuthor Commented:
Any idea what might be causing the corruption of the stings?  I've inspected the data prior to saving it to the stream and it is valid.  Inspecting is as it is being read from the stream it is corrupted.

This still does not tell me if it is corrupted when it is written out or when it is read in.
0
 
Ephraim WangoyaCommented:

I'm currently debugging it, I created random 50 records and seems I always get the correct results.
I'm yet to try with different length strings, I'll definietly find the offending code, and I can also suggest a more robust way saving the data.

0
 
ScottCannonAuthor Commented:
Any suggestions on a better way of saving the data would be appreciated.
0
 
Ephraim WangoyaCommented:

Ok, I found the problem, I just could not recreate until I switched to Delphi 2010
You have to Typecast the strings as PChar. Also, use the Sender parameter from the method signature


procedure TMainForm.TreeLoadNode(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Stream: TStream);
var
  Data: PNodeData;
  Len: Integer;
begin
  Data := Sender.GetNodeData(Node);

  Stream.Read(Len, SizeOf(Len));
  SetLength(Data.Caption, Len);
  Stream.Read(PChar(Data.Caption)^, Len * SizeOf(Char) );

  Stream.Read(Len, SizeOf(Len));
  SetLength(Data.Filename, Len);
  Stream.Read(PChar(Data.Filename)^, Len * SizeOf(Char));

  Stream.Read(Len, SizeOf(Len));
  SetLength(Data.Description, Len);
  Stream.Read(PChar(Data.Description)^, Len * SizeOf(Char));

  Stream.Read(Data.FileDateTime, SizeOf(Data.FileDateTime));
  Stream.Read(Data.ImageIndex, SizeOf(Data.ImageIndex));
  Stream.Read(Data.ID, SizeOf(Data.ID));

end;

procedure TMainForm.TreeSaveNode(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Stream: TStream);
var
  Data: PNodeData;
  Len: integer;
begin
  Data := Sender.GetNodeData(Node);

  Len := Length(Data.Caption);
  Stream.write(Len, SizeOf(Len));
  Stream.write(PChar(Data.Caption)^, Len * SizeOf(Char));

  Len := Length(Data^.Filename);
  Stream.write(Len, SizeOf(Len));
  Stream.write(PChar(Data.Filename)^, Len * SizeOf(Char));

  Len := Length(Data^.Description);
  Stream.write(Len, SizeOf(Len));
  Stream.write(PChar(Data.Description)^, Len * SizeOf(Char));

  Stream.write(Data.FileDateTime, SizeOf(Data.FileDateTime));
  Stream.Write(Data.ImageIndex, SizeOf(Data.ImageIndex));
  Stream.Write(Data.ID, SizeOf(Data.ID));

end;

Open in new window

0
 
ScottCannonAuthor Commented:
This works perfectly.  Thanks for the great work!

You mentioned that you would suggest a more robust way of saving the data... any more thought on doing so and maybe an example?
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.