Как добавить сохраненное дерево (в файле) в другое дерево в качестве поддерева? - PullRequest
0 голосов
/ 02 октября 2018

Есть ли способ добавить локально сохраненный (в файле) TVirtualStringTree к другому как поддерево под конкретным узлом?

EXAMPLE

В этом примере я использовал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. }

, который идеально подходит для ситуации, потому что мне нужно добавить ранее сохраненное дерево в определенный узел (выбранный в примере).Но я не могу найти в Интернете ничего об этом или пример этого в действии.Так как же восстановить сохраненное дерево (все его содержимое) как поддерево под выбранным узлом?

Ответы [ 2 ]

0 голосов
/ 25 октября 2018

РЕДАКТИРОВАТЬ : решено, необходимо добавить событие 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, снова открываю и пытаюсьдля перезагрузки выдает ошибку, сисегв, другими словами нарушение доступа к памяти

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: картинка из сетки, которую я пытаюсьsave / load

grid

Как видно, он имеет два столбца, первый из данных ^ .fname, второй из данных ^ .quant, а третий должен был быть изdata ^ .OBS, но он просто не загружается (sigsegv), поэтому я вынул его.

0 голосов
/ 04 октября 2018

Для хранения конкретного узла и его дочерних элементов виртуального дерева в файле необходимо использовать метод 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 для этой цели.Здесь я предполагаю, что узел содержит данные в записи, содержащей строку «Заголовок» (или, конечно, вы должны адаптировать это к вашим потребностям):

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;    
...