Search code examples
delphitreeviewdelphi-10-seattle

Delphi TTreeView: get root node count and root node by index?


Every TTreeNode can give me its number of direct children using Node.Count, and I can get any child by index using Node[Index].

I am searching and searching but it seems this not possible for root nodes?? Do I really have to count them myself? And if yes, what's the most elegant way to do this both?

I was expecting some hidden root-root-item which just has all the root nodes as children, wouldn't that be helpful to be able to treat all sorts of nodes the same, i.e. for a recursive function doing something for all nodes?


Solution

  • You are right, TTreeView should have this, but it doesn't, and I don't see a good reason. However, here are some thoughts to take into account:

    The data structure used in the TTreeView doesn't directly support counting direct children or accessing them by index, because it is a linked list where each node links to its parent, its next and previous sibling, and its first child.

    For your convenience, the TTreeNode object is able to give you what you want, but for that it has to loop through the chain and count.

    That also means, accessing all children in a for-loop is anyway not a good idea, like in the recursive function you mentioned - it would unnecessarily be a loop in a loop.

    Instead, directly go through the chain using TreeView.Items.GetFirstNode (or MyParentNode.getFirstChild) and then a while loop with Node:= Node.getNextSibling (that also works very well for a recursive function).

    Suggestion: look at the implementation in Vcl.ComCtrls. From there, you could also adapt the two most elegant functions you ask for, in case you still need it :)

    type
      TTreeViewClassHelper = class helper for TTreeView
        function GetRootCount: Integer;
        function GetRootItem(Index: Integer): TTreeNode;
      end;
    
    function TTreeViewClassHelper.GetRootCount: Integer;
    var
      Node: TTreeNode;
    begin
      Result:= 0;
      Node:= Items.GetFirstNode;
      while Assigned(Node) do begin
        Inc(Result);
        Node:= Node.getNextSibling;
      end;
    end;
    
    function TTreeViewClassHelper.GetRootItem(Index: Integer): TTreeNode;
    begin
      Result:= Items.GetFirstNode;
      while Assigned(Node) and (Index > 0) do begin
        Result:= Result.getNextSibling;
        Dec(Index);
      end;
    end;
    

    And just to demonstrate how you should not do it ;-)

    for I:= 0 to TreeView.GetRootCount - 1 do
      with TreeView.GetRootItem(I) do
        Memo.Lines.Add(string.Join(#9, [AbsoluteIndex, Index, Text]));