Question

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;
Was it helpful?

Solution

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.

OTHER TIPS

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

...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top