1

I have a heirarchy of data that is displayed in a VirtualStringTree. I use this heirarchy multiple times in my application with slight modifications to the way the tree is drawn/displayed. My method currently utilizes the AddChild() procedure for adding nodes an d as such i have multiple copies of the data when the application is run.

I would now like to consolidate these trees and have a 'master' tree which points to the actual data, but then have the 'slave' trees point to the SAME data.

I am a little unsure of if/how this can be acheived. I would think i could simply load the master tree and populate its NodeData with pointers to where the data i being held, and then for all the slave trees, simply store the same pointer in their nodedata.

However im not having much luck.

My current code looks like:

//Initialize the NodeDataSize
procedure TForm1.FormCreate(Sender: TObject);
begin
     TreeMasterComponents.NodeDataSize := SizeOf(rMasterComponent);
     VST.NodeDataSize := SizeOf(Pointer);
end;

Procedure to copy the master tree to slave trees

procedure TForm1.LoadSlaveTree(ATree: TVirtualStringTree);
var Node : PVirtualNode;

procedure RecursiveCopy(SrcPNode,SrcTNode : PVirtualNode; ATree : TVirtualStringTree);
var SrcNode, TargetNode : PVirtualNode;
SrcData : PMasterComponent;
begin
     SrcNode := TreeMasterComponents.GetFirstChild(SrcPNode);
     while Assigned(SrcNode) do
     begin
          SrcData := TreeMasterComponents.GetNodeData(SrcNode);
          TargetNode := ATree.AddChild(SrcTNode,SrcData);
          RecursiveCopy(SrcNode,TargetNode,ATree);
          SrcNode := SrcNode.NextSibling;
     end;
end;

begin
     ATree.BeginUpdate;
     ATree.Clear;
     Node := TreeMasterComponents.GetFirst(true);
     while Assigned(Node) do
     begin
         RecursiveCopy(Node,nil,ATree);
         Node := Node.NextSibling;
     end;
     ATree.EndUpdate;
end;

Procedure for slave tree to getCellText

procedure TForm1.SlaveGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
var Data : PMasterComponent;
begin
     Data := Sender.GetNodeData(Node);
     Case column of
     0:CellText := Data^.ComponentCode;
     1:CellText := Data^.FullLocation;
     end;
end;

At the moment, the nodes are added in the correct heirarchy, however there is no text appearing for the slave trees. Any help would be appreciated.

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
Simon
  • 9,197
  • 13
  • 72
  • 115

2 Answers2

2

I don't know why no text appears in your slave trees but I'd like to advise the following.

Probably easier would be to create a frame with the tree and code, and reuse the frame on your forms. Each instance of the tree would simply load the same data (no copying of nodes necessary).

The slight modifications can be achieved by visual form inheritance by writing new event handlers for the specific instance of the frame/tree. You can also inherit from the frame, creating a new class, if needed.

Ondrej Kelle
  • 36,941
  • 2
  • 65
  • 128
  • This is (kind of) how i am currently doing it, but e.g. if i change the data in one tree, it is NOT reflected in other trees. I have actually found the solution so i will post here soon – Simon Oct 25 '11 at 06:52
  • If the tree is using the same data all you need to do is to invalidate the tree (or relevant nodes) when the data changes to make the tree repaint itself. If the hierarchy of the data changes you need to reload the tree (or relevant nodes). – Ondrej Kelle Oct 25 '11 at 07:02
2

Ok so i beleive i have found a solution:

The trick was to create a new record to store the Pointer to the original data:

type PRefMasterComponent = ^RefMasterComponent;
  RefMasterComponent = packed record
     PMCData : PMasterComponent;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
     TreeMasterComponents.NodeDataSize := SizeOf(rMasterComponent);
     VST.NodeDataSize := SizeOf(RefMasterComponent);
end;

Now the Copy code looks like:

procedure TForm1.LoadTree(ATree: TVirtualStringTree);
var Node,TargetNode : PVirtualNode;
SrcData : PMasterComponent;
Data : PRefMasterComponent;

procedure RecursiveCopy(SrcPNode, TargetPNode : PVirtualNode; ATree : TVirtualStringTree);
var Node, TargetNode : PVirtualNode;
SrcData : PMasterComponent;
Data : PRefMasterComponent;
begin
     Node := TreeMasterComponents.GetFirstChild(SrcPNode);
     while Assigned(Node) do
     begin
          SrcData := TreeMasterComponents.GetNodeData(Node);
          TargetNode := ATree.AddChild(TargetPNode);
          Data := ATree.GetNodeData(TargetNode);
          Data.PMCData := SrcData;
          RecursiveCopy(Node,TargetNode,ATree);
          Node := Node.NextSibling;
     end;
end;

begin
     ATree.BeginUpdate;
     ATree.Clear;
     Node := TreeMasterComponents.GetFirst(true);
     while Assigned(Node) do
     begin
          SrcData := TreeMasterComponents.GetNodeData(Node);
          TargetNode := ATree.AddChild(nil);
          Data := ATree.GetNodeData(TargetNode);
          Data.PMCData := SrcData;
          RecursiveCopy(Node,TargetNode,ATree);
          Node := Node.NextSibling;
     end;
     ATree.EndUpdate;
end;

and the OnGetText:

procedure TForm1.vstGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
var Data : PRefMasterComponent;
RefData : PMasterComponent;
begin
     Data := Sender.GetNodeData(Node);
     RefData := Data.PMCData;
     Case column of
     0:CellText := RefData.ComponentCode;
     1:CellText := RefData.FullLocation;
     end;
end;

Now if i change data in one tree all i have to do is call VST.Invalidate and the changes are reflected in the other tree.

Simon
  • 9,197
  • 13
  • 72
  • 115