如何将保存的树(在文件中)作为子树添加到另一棵树?

How to add a saved tree (in a file) to another tree as a subtree?

有什么方法可以将本地存储的(在文件中)TVirtualStringTree添加到另一个作为特定节点下的子树?

在例子中,为了速度,我使用了TTreeView,但我需要使用TVirtualStringTree。我试图找到有关此方法的信息。

{ TBaseVirtualTree.AddFromStream Method
  Adds the content from the given stream to the given node. }
procedure AddFromStream(Stream: TStream; TargetNode: PVirtualNode);
{ Description
  AddFromStream restores the subtree stored in Stream and adds it to 
  TargetNode. The content of the stream must have been saved previously with 
  SaveToStream. }

这非常适合这种情况,因为我需要将之前保存的树添加到特定节点(示例中选定的节点)。但是我在网上找不到任何关于它的信息,也找不到它的实际例子。那么如何将已保存的树(其所有内容)恢复为所选节点下的子树?

为了将虚拟树的特定节点及其子节点存储到文件中,您必须使用方法 SaveToStream,与 SaveToFile 不同,它接受源节点的可选参数:

procedure SaveNodeToFile(ATree: TBaseVirtualTree; ANode: PVirtualNode; 
  AFileName: String);
var
  stream: TFileStream;
begin
  stream := TFileStream.Create(AFileName, fmCreate);
  try
    ATree.SaveToStream(stream, ANode);
  finally
    stream.Free;
  end;
end;

由于虚拟树对数据一无所知,因此您必须提供一种方法来写入分配给每个节点的数据。树为此提供了事件 OnSaveNode。这里我假设节点有一个包含字符串 "Caption" 的记录中的数据(或者当然,你必须根据你的需要进行调整):

type
  PTreeData = ^TTreeData;
  TTreeData = record
    Caption: String;
  end;

procedure TForm1.VirtualStringTree1SaveNode(Sender: TBaseVirtualTree; 
  ANode: PVirtualNode; Stream: TStream);
var
  data: PTreeData;
  n: Integer;
begin
  // Get pointer to the node's data
  data := Sender.GetNodeData(ANode);
  // Write the length of the string (in characters)
  n := Length(data^.Caption);
  Stream.WriteDWord(n);
  if n > 0 then
    // write the string characters
    Stream.Write(data^.Caption[1], Length(data^.Caption) * SizeOf(char));
end;

为了将文件读回另一棵树的节点,您已经提到的方法AddFromStream是完美的:

procedure LoadNodeFromFile(ATree: TBaseVirtualTree; ANode: PVirtualNode; AFileName: String);
var
  stream: TFileStream;
begin
  stream := TFileStream.Create(AFileName, fmOpenRead);
  try
    ATree.AddFromStream(stream, ANode);
  finally
    stream.Free;
  end;
end;

同样,您需要告诉树如何获取分配给每个节点的数据 - 事件 OnLoadNode 的处理程序必须与 OnSaveNode 事件完全相反:

procedure TForm1.VirtualStringTree2LoadNode(Sender: TBaseVirtualTree; Node: PVirtualNode; 
  Stream: TStream);
var
  data: PTreeData;
  n: DWord;
begin
  // Get pointer to the node's data
  data := Sender.GetNodeData(Node);
  // Read the string length (in characters)
  n := Stream.ReadDWord;
  if n > 0 then begin
    // Set the length of the string
    SetLength(data^.Caption, n);
    if n > 0 then
      // Read the string's characters
      Stream.Read(data^.Caption[1], n * SizeOf(char));
  end;
end;    

编辑:已解决,需要添加getnodedatasize事件:

procedure TfrmBuilder.VST1GetNodeDataSize(Sender: TBaseVirtualTree;
  var NodeDataSize: Integer);
begin
   NodeDataSize := SizeOf(TTreeData);
end;

没有它,树就不会加载。

谢谢,


我将重新打开这个话题,我遇到的另一个问题是:

我可以使用包含字符串的两列完美地保存和加载:

type
  PTreeData = ^TTreeData;
  TTreeData = record
  Fname: String;
  quant: String;
End;

procedure TfrmBuilder.VST1LoadNode(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Stream: TStream);
var
  data: PTreeData;
  n: DWord;
begin
  // Get pointer to the node's data
  data := Sender.GetNodeData(Node);

  // Read the string length (in characters)
  n := Stream.ReadDWord;
  if n > 0 then begin
    // Set the length of the string
    SetLength(data^.Fname, n);
    if n > 0 then
      // Read the string's characters
      Stream.Read(data^.Fname[1], n * SizeOf(char));
  end;

  n := Stream.ReadDWord;
  if n > 0 then begin
    // Set the length of the second string
    SetLength(data^.quant, n);
    if n > 0 then
      // Read the second string's characters
      Stream.Read(data^.quant[1], n * SizeOf(char));
  end;

end;

procedure TfrmBuilder.VST1SaveNode(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Stream: TStream);
var
  data: PTreeData;
  n: Integer;
begin
  // Get pointer to the node's data
  data := Sender.GetNodeData(Node);

  // Write the length of the string (in characters)
  n := Length(data^.Fname);
  Stream.WriteDWord(n);
  if n > 0 then
    begin
    // write the string characters
    Stream.Write(data^.Fname[1], Length(data^.Fname) * SizeOf(char));
    end;

  // Write the length of the second string (in characters)
  n := Length(data^.quant);
  Stream.WriteDWord(n);

  if n > 0 then
    begin
    // write the second string chars
    Stream.Write(data^.quant[1], Length(data^.quant) * SizeOf(char));
    end;

end;

但是,当我尝试保存三列时,仅使用相同的方法(在流中写下字符串),它会加载到可执行文件的第一个 运行,但是当我关闭exe,重新打开并尝试重新加载它会出现错误,sisegv,换句话说,内存访问冲突

type
  PTreeData = ^TTreeData;
  TTreeData = record
  Fname: String;
  quant: String;
  OBS: string;

End;

procedure TfrmBuilder.VST1LoadNode(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Stream: TStream);
var
  data: PTreeData;
  n: DWord;
begin
  // Get pointer to the node's data
  data := Sender.GetNodeData(Node);

  // Read the string length (in characters)
  n := Stream.ReadDWord;
  if n > 0 then begin
    // Set the length of the string
    SetLength(data^.Fname, n);
    if n > 0 then
      // Read the string's characters
      Stream.Read(data^.Fname[1], n * SizeOf(char));
  end;

  n := Stream.ReadDWord;
  if n > 0 then begin
    // Set the length of the second string
    SetLength(data^.quant, n);
    if n > 0 then
      // Read the second string's characters
      Stream.Read(data^.quant[1], n * SizeOf(char));
  end;

  { THE CODE FOR THE THIRD STRING: 
  n := Stream.ReadDWord;
  if n > 0 then begin
    // Set the length of the THIRD string
    SetLength(data^.OBS, n);
    if n > 0 then
      // Read the THIRD string's characters
      Stream.Read(data^.OBS[1], n * SizeOf(char));
  end;
  }
end;

procedure TfrmBuilder.VST1SaveNode(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Stream: TStream);
var
  data: PTreeData;
  n: Integer;
begin
  // Get pointer to the node's data
  data := Sender.GetNodeData(Node);

  // Write the length of the string (in characters)
  n := Length(data^.Fname);
  Stream.WriteDWord(n);
  if n > 0 then
    begin
    // write the string characters
    Stream.Write(data^.Fname[1], Length(data^.Fname) * SizeOf(char));
    end;

  // Write the length of the second string (in characters)
  n := Length(data^.quant);
  Stream.WriteDWord(n);

  if n > 0 then
    begin
    // write the second string chars
    Stream.Write(data^.quant[1], Length(data^.quant) * SizeOf(char));
    end;

  { 
  // Write the length of the THIRD string (in characters)
  n := Length(data^.OBS);
  Stream.WriteDWord(n);

  if n > 0 then
    begin
    // write the THIRD string chars
    Stream.Write(data^.OBS[1], Length(data^.OBS) * SizeOf(char));
    end;
   }
end;

会是什么?对于 2 个字符串(2 列),它就像一个魅力,但对于三个,我遇到了访问冲突?

提前致谢, 莱昂纳多

PS:我正在尝试保存/加载的来自网格的图片

grid

正如所见,它有两列,第一列来自 data^.fname,第二列来自 data^.quant,第三列应该来自 data^.OBS,但它只是没有加载 (sigsegv),所以我拿出来了