2

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?

maf-soft
  • 2,335
  • 3
  • 26
  • 49

1 Answers1

3

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]));
maf-soft
  • 2,335
  • 3
  • 26
  • 49
  • 2
    I'd call GetFirstNode and loop with GetNextSibling. – Sertac Akyuz Nov 14 '19 at 19:26
  • @SertacAkyuz, correct, I was accidentally looking at Vcl.ComCtrls, where TTreeNode.GetItem is done using GetFirstChild and GetNextChild... Edited. – maf-soft Nov 14 '19 at 19:29
  • I tried to ask a good question, and while writing it, @MartynA, I continued investigating and then didn't want to throw it away... So I posted it, already assuming that I would write the answer myself :) I've done this before and it seems some people don't like it. I really wonder why... – maf-soft Nov 14 '19 at 19:45
  • 1
    I don't have a problem at all with people answering their own questions, in fact I wish more people would do it when no-one else has posted an answer. In this case, I was just curious the way you phrased you answer, referring to yourself as "you" rather than just saying words to the effect of "On furtther consideration, I believe the following answers my q ...". That was all ... – MartynA Nov 14 '19 at 20:00