1

How to copy all nodes from one VirtualTreeView to another? I tried to use CopyTo-function, but the Data was empty, why? Data contains reference to record (as usual).

I've also tried to use the OnNodeCopying event as @Cosmin Prund suggested in his now deleted answer this way, but it crashes:

procedure TDataBaseForm.SourceTreeViewNodeCopying(Sender: TBaseVirtualTree; 
  Node, Target: PVirtualNode; var Allowed: Boolean); 
var 
  SourceNodeData, DestNodeData: PNodeDataForCompare; 
begin 
  SourceNodeData := Sender.GetNodeData(Node); 
  DestNodeData := VirtualStringTree1.GetNodeData(Target); 
  if Assigned(SourceNodeData) then 
  begin 
    DestNodeData^ := SourceNodeData^; 
  end; 
  Allowed := true; 
end;
TLama
  • 75,147
  • 17
  • 214
  • 392
user2078264
  • 35
  • 1
  • 4
  • How can I allow you to include code in my question? I'm new in stackoverflow. – user2078264 Feb 16 '13 at 13:05
  • It was just a rhetorical note :-) I've included the code from your comment by the @Cosmin's (now deleted answer), but please [edit your question](http://stackoverflow.com/posts/14910000/edit) and include the exact error message you're getting, when you use that code. And, welcome to StackOverflow! – TLama Feb 16 '13 at 13:18
  • 1
    If you say "it crashes", then all I can say is: "well, fix it!". If you tell us what exception occurs, what the error message is, and on which line it occurs, we might be able to help you fix it. – Martijn Feb 16 '13 at 13:30
  • The way `CopyTo` works is a bit troublesome: all the nodes to be copied are first streamed to an in-memory stream, and then re-created. While the nodes are re-created the `OnNodeCopying` is called, but with useless parameters: The `Node` is the newly created node, and the `Target` is the `Target` node that was passed to `CopyTo()`. That's why I deleted my answer, it's a bad "lead" unfortunately. You'd need the original node + the new node to be able to copy data, but you get the new node and the node that was used as the Target. – Cosmin Prund Feb 16 '13 at 13:51
  • An other idea would be to implement the `OnSaveNode` and `OnLoadNode` to save/read the nodes to the stream used by VirtualTreeView. I think those two would make VT's copying to work, but I don't have time to test and I don't want to provide an other partial answer that might not work. – Cosmin Prund Feb 16 '13 at 13:54

2 Answers2

5

I've dug a bit deeper into how the CopyTo() works. All the nodes to be copied are first saved to a stream, next the nodes from the stream are re-created on the target VirtualTree. The user node data needs to be copied into that stream, or else it will not be available when re-creating the nodes in the target VirtualTree.

The events that handle streaming user data are:

  • OnSaveNode
  • OnLoadNode

It makes sense for the VirtualTree to make use of those methods, and as a consequence makes sense to use the stream as an intermediary object. The VirtualTree also allows saving the tree to a file and copy-pasting nodes to and from clipboard. All those methods need to somehow copy the user's "payload"! After implementing those events the CopyTo method worked fine, so did saving the the content of the first tree to disk and re-loading it into the second tree. I also tested copy-pasting and it worked fine.

Here's my implementation of the OnSaveNode and OnLoadNode. Copy-pasting my methods will most likely not work for you, because it depends on the kind of data you put in the node payload. In my example I placed records (not pointers-to-records) made up of just one Integer: those records can safely be streamed to disk and reloaded, they're not managed types, they're simple value types. If you're placing pointers into the records (reference-to-records as you called them) you can still use this and it will work, but you'd be copying POINTERS: You don't get new records, they're the same records; you obviously can't save the Tree to a file, restart the program and reload from disk.

procedure TForm6.VT1SaveNode(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Stream: TStream);
begin
  Stream.Write(Sender.GetNodeData(Node)^, (Sender as TVirtualStringTree).NodeDataSize);
end;

procedure TForm6.VT2GetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
begin
  CellText := IntToStr(PNodeDataForCompare(VT1.GetNodeData(Node)).Payload);
end;

One final note: For your scenario you'll need the OnSaveNode implemented on the Source tree and the OnLoadNode implemented on the Target tree; I implemented them on both using the same code.

Cosmin Prund
  • 25,498
  • 2
  • 60
  • 104
  • I've tried to use your code, but it is not work for me (node data is empty). I also use simple value types (strings and integer values). Can you attach your project-files? I will try to compare it with my code. – user2078264 Feb 16 '13 at 15:05
  • Give me a minute, I'll paste-bin the whole test project. – Cosmin Prund Feb 16 '13 at 15:10
  • Paste bin for [Unit6.pas](http://pastebin.com/DxpGPyBJ) and [Unit6.dfm](http://pastebin.com/Q8h3sRir); You'll see every test I made, including CopyTo, Copy-Paste, Save to file, Reload from file. – Cosmin Prund Feb 16 '13 at 15:14
0
unit VirtualTrees;

...

function TBaseVirtualTree.CopyTo(Source, Target: PVirtualNode; Mode: TVTNodeAttachMode; ChildrenOnly: Boolean): PVirtualNode;

...

        Result := TargetTree.MakeNewNode;
        InternalConnectNode(Result, Target, TargetTree, Mode);
        TargetTree.InternalAddFromStream(Stream, VTTreeStreamVersion, Result);
        if not DoNodeCopying(Result, Source {Target}) then //        <=======
        begin
          TargetTree.DeleteNode(Result);
          Result := nil;
        end

...

Marco Forberg
  • 2,634
  • 5
  • 22
  • 33