Mirror

Save and load a TTreeView to / from a stream (Views: 708)


Problem/Question/Abstract:

I'd like to use the TreeView component for hierarchizing some data. For this, I need to add to every node an integer property which will refer to the primary key field of an associated database. Alas, I fear that the TreeView "SaveToFile" and "LoadFromFile" methods save/ load only the Text property! Then, how can I save and load this integer property (and by the way, the imageindex) for every node?

Answer:

By writing your own save and load routines. Untested! Don't forget to rewind the stream before loading when you test this code.

procedure SaveTreeviewToStream(tv: TTreeview; S: TStream);
var
  writer: TWriter;
  node: TTreeNode;
begin
  Assert(Assigned(tv));
  Assert(Assigned(S));
  writer := TWriter.Create(S, 4096);
  try
    node := tv.Items[0];
    writer.WriteListBegin;
    while node <> nil do
    begin
      writer.WriteInteger(node.level);
      writer.WriteString(node.Text);
      writer.WriteInteger(node.Imageindex);
      writer.WriteInteger(Integer(node.data));
      node := node.GetNext;
    end;
    writer.WriteListEnd;
    writer.FlushBuffer;
  finally
    writer.Free;
  end;
end;

procedure LoadTreeviewFromStream(tv: TTreeview; S: TStream);
var
  reader: TReader;
  node: TTreeNode;
  level: Integer;
begin
  Assert(Assigned(tv));
  Assert(Assigned(S));
  tv.Items.BeginUpdate;
  try
    tv.Items.Clear;
    reader := TReader.Create(S, 4096);
    try
      node := nil;
      reader.ReadListBegin;
      while not Reader.EndOfList do
      begin
        level := reader.ReadInteger;
        if node = nil then
          {create root node, ignore its level}
          node := tv.Items.Add(nil, '')
        else
        begin
          if level = node.level then
            node := tv.Items.Add(node, '')
          else if level > node.level then
            node := tv.Items.AddChild(node, '')
          else
          begin
            while Assigned(node) and (level < node.level) do
              node := node.Parent;
            node := tv.Items.Add(node, '');
          end;
        end;
        node.Text := Reader.ReadString;
        node.ImageIndex := Reader.ReadInteger;
        node.Data := Pointer(Reader.ReadInteger);
      end;
      reader.ReadListEnd;
    finally
      reader.Free;
    end;
  finally
    tv.items.Endupdate;
  end;
end;

I'd rather suggest to use the data pointer as a pointer to a real object, not as integer (in the SaveTreeviewToStream procedure). You could add more complex info inside this object, like type information, or even the data objects itself. Type information is essential if your treeview browses through different tables of your database.

<< Back to main page