TAdvTreeView - Virtual Mode - HowTo Add/Remove

Hi,

I am using TAdvTreeView in Virtual Mode. Things went very well with displaying simple trees but some deeper understanding about the inner workings of TAdvTreeView in Virtual Mode seems to be necessary in order to achieve more complex tree operations.

Asumption: Virtual Mode is usefull every time you already have some kind of tree data structures in your own classes and want those to be displayed in the TAdvTreeView.

The simple aproach is to implement the events for OnGetNumberOfNodes and OnGetNodeText as a minimum requirement. To do this you need some kind of mapping between the virtual tree nodes and your real data nodes. Depending on your own data structures you could use the Anode.Row as a key to identify your real data object.

But the Row number is subject to be changed when nodes are inserted for example. To map the virtual nodes of the TAdvTreeView I implemented a mapping function that takes a virtual node and returns the real node of my underlying data objects. This function uses an array of the index values oft he path between the virtual root node tot he actual virtual node that I want to map. With this path information, the mapping function walks down the real data tree and returns the found node.

This worked very well for displaying my existing tree structures in a TAdvTreeView Component.

Now I started to add and delete nodes to my real data tree and called TAdvTreeView1.Invalidate. I expected the tree would then call the OnGetNumberOfNodes and OnGetNodeText events to update the affected nodes which are actually visible. But that didn?t work.

Then I figured I may have to use the methods AddVirtualNode(parent) and RemoveVirtualNode. This seemed to work initially in simple trees but there are side effects.

The questions that arise are as follows: What hast to be done first? Adding a new virtual node or adding a node to my real data tree? If I add a new virtual node first, then that triggers OnGetNumberOfNodes/OnGetNodeText events which report wrong values because the node is not yet in the real data tree. If I add the real node to my real data tree first, then the OnGetNumberOfNodes/OnGetNodeText events already report the new values befort the call to AddVirtualNode. In both cases I see weired behaviour in the displayed tree with this symptom: An already expanded node has a + sign infront of it. And if I click that the node, it expands and looks like it has multiple children lists.

Anyway. This leads to general question of how to use a TAdvTreeView in virtual mode in the correct way?

- when to use AddVirtualNode

- how to map between virtual and real nodes

- do you have sample code for virtual mode where adding and removing nodes is involved

- what?s the expected way of using virtual mode when adding and removing nodes is involved

 

Thank you

There are 2 approaches when adding nodes in virtual mode. You can either add them to your own data structure and rebuild the node list (triggering the OnGetNumberOfNodes event), which is the easiest approach, or add them directly to the virtual data structure. 


The first approach might take performance hits as the node list is rebuilt on each data manipulation, especially with larger data structures. The second approach only builds the node list once, loading all data initially. Nodes that are added afterwards doesn't require a complete rebuild en thus have a benefit in performance. The downside using this approach is that you'll have to manage the node list manually (AddVirtualNode). 

Which approach suits your application depends on the usage. In some scenarios, which have smaller data structures, rebuilding the complete node list and not caring for manipulating the virtual node list manually is the best option, while in some cases there is a need to manipulate the virtual node list manually when having a large data structure and there are issues with performance when rebuilding the node list each time a node is added, removed, inserted or updated. The last method also takes more code to implement. 

Both methods of adding new nodes is demonstrated in the following complete sample, based on a default TAdvTreeView:



unit Unit1;


interface


uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, AdvTreeViewBase, AdvTreeViewData,
  AdvCustomTreeView, AdvTreeView, Generics.Collections, Vcl.StdCtrls;


type
  TNodeItem = class
  private
    FText: string;
  public
    property Text: string read FText write FText;
    constructor Create(const AText: string);
  end;


  TForm1 = class(TForm)
    AdvTreeView1: TAdvTreeView;
    Button1: TButton;
    Button2: TButton;
    procedure AdvTreeView1GetNumberOfNodes(Sender: TObject;
      ANode: TAdvTreeViewVirtualNode; var ANumberOfNodes: Integer);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure AdvTreeView1GetNodeText(Sender: TObject;
      ANode: TAdvTreeViewVirtualNode; AColumn: Integer;
      AMode: TAdvTreeViewNodeTextMode; var AText: string);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
    FNodes: TObjectList<TNodeItem>;
  public
    { Public declarations }
  end;


var
  Form1: TForm1;


implementation


{$R *.dfm}


procedure TForm1.AdvTreeView1GetNodeText(Sender: TObject;
  ANode: TAdvTreeViewVirtualNode; AColumn: Integer;
  AMode: TAdvTreeViewNodeTextMode; var AText: string);
begin
  AText := FNodes[ANode.Row].Text;
end;


procedure TForm1.AdvTreeView1GetNumberOfNodes(Sender: TObject;
  ANode: TAdvTreeViewVirtualNode; var ANumberOfNodes: Integer);
begin
  if not Assigned(FNodes) then
  begin
    ANumberOfNodes := 0;
    Exit;
  end;


  if ANode.Level = -1 then
    ANumberOfNodes := FNodes.Count;
end;


{ TNodeItem }


constructor TNodeItem.Create(const AText: string);
begin
  FText := AText;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
  AdvTreeView1.BeginUpdate;
  AdvTreeView1.ClearNodeList;
  FNodes.Add(TNodeItem.Create('New Item'));
  AdvTreeView1.EndUpdate;
end;


procedure TForm1.Button2Click(Sender: TObject);
begin
  AdvTreeView1.BeginUpdate;
  FNodes.Add(TNodeItem.Create('New Item'));
  AdvTreeView1.AddVirtualNode;
  AdvTreeView1.EndUpdate;
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
  FNodes := TObjectList<TNodeItem>.Create;
  FNodes.Add(TNodeItem.Create('Item 1'));
  FNodes.Add(TNodeItem.Create('Item 2'));
  FNodes.Add(TNodeItem.Create('Item 3'));
  AdvTreeView1.Columns.Clear;
  AdvTreeView1.Columns.Add.Text := 'Test';
end;


procedure TForm1.FormDestroy(Sender: TObject);
begin
  FNodes.Free;
end;


end.

Pieter Scheldeman2016-10-20 22:21:55

Thanks, that helped a lot.


"rebuilding the nodelist" is done by ClearNodeList or is there another way to do that? I am asking because ClearNodeList leads to loosing the actual selection.

This is the only way, you can persist the selection before the clear and restore it afterwards. We will investigate here if we can improve the losing selection behavior

If I change the children (add or remove) in one of my data node, wouldn't it theoretically be enough if I could tell the corresponding virtual tree node that sth. in the children list of it has changed, so that the tree could rebuild its internal list from that point on?

The AddVirtualNode method does that, only manipulating the node structure from the point of insertion. The sample demonstrates how the AddVirtualNode method works. It also calls the necessary events to retrieve the data, so first add the data in your data structure and then call AddVirtualNode, which takes care of everything. If you think this is to complex, or are experiencing difficulties with this approach then please use the simple method, which is rebuilding all nodes. The only you need to add data to your data structure and the changes will be reflected in the treeview after calling BeginUpdate, ClearNodeList and EndUpdate in respective order.Pieter Scheldeman2016-10-21 17:32:30