Search code examples
delphitreeviewdelphi-xe6

how to expand only to the second level of the treeview


Ex.:

    - link 1
    -- link 1.1
    --- link 1.1.1(only)
    ---- link 1.1.1.1 (not expand)
    -- link 1.2
    --- link 1.2.1 (only)
    ---- link 1.2.1.1 (not expand)

I can expand only link 1.1, link 1.2... how?


Solution

  • There is no build-in functionality for expanding multiple items or items on a specific level, so there is no other way then traversing through the items. Call the Expand method on all second level items. As well on all first level items, otherwise the second level items will not be shown. Its Recurse parameter should be False in order not to expand a possibly third or deeper level.

    There are two ways to traverse through a TreeView's items: by Item index and by Node. Normally, operations on a TreeView's items are preferably done by Node, because the Items property's getter traverses through all Items to find a single Item with a specific index. However, TTreeNodes caches the last retrieved item, thus by incrementing the loop Index with 1, harm will be minimized.

    The easy solution then becomes:

    procedure ExpandTreeNodes(Nodes: TTreeNodes; Level: Integer);
    var
      I: Integer;
    begin
      Nodes.BeginUpdate;
      try
        for I := 0 to Nodes.Count - 1 do
          if Nodes[I].Level < Level then
            Nodes[I].Expand(False);
      finally
        Nodes.EndUpdate;
      end;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      ExpandTreeNodes(TreeView1.Items, 2);
    end;
    

    Note that the Items property's getter is still called twice. Despite the caching mechanism, I think this still should be avoided:

    procedure ExpandTreeNodes(Nodes: TTreeNodes; Level: Integer);
    var
      I: Integer;
      Node: TTreeNode;
    begin
      Nodes.BeginUpdate;
      try
        for I := 0 to Nodes.Count - 1 do
        begin
          Node := Nodes[I];
          if Node.Level < Level then
            Node.Expand(False);
        end;
      finally
        Nodes.EndUpdate;
      end;
    end;
    

    But then you might as well use:

    procedure ExpandTreeNodes(Nodes: TTreeNodes; Level: Integer);
    var
      Node: TTreeNode;
    begin
      Nodes.BeginUpdate;
      try
        Node := Nodes.GetFirstNode;
        while Node <> nil do
        begin
          if Node.Level < Level then
            Node.Expand(False);
          Node := Node.GetNext;
        end;
      finally
        Nodes.EndUpdate;
      end;
    end;
    

    This still traverses áll Items. Ok, just once. But if you have a really big and/or deep tree, and you want maximum efficiency, and you do not care about readability, or you just want to experiment with a TreeView's Nodes for fun, then use:

    procedure ExpandTreeNodes(Nodes: TTreeNodes; Level: Integer);
    var
      Node: TTreeNode;
      Next: TTreeNode;
    begin
      if Level < 1 then
        Exit;
      Nodes.BeginUpdate;
      try
        Node := Nodes.GetFirstNode;
          while Node <> nil do
          begin
            Node.Expand(False);
            if (Node.Level < Level - 1) and Node.HasChildren then
              Node := Node.GetFirstChild
            else
            begin
              Next := Node.GetNextSibling;
              if Next <> nil then
                Node := Next
              else
                if Node.Level > 0 then
                  Node := Node.Parent.GetNextSibling
                else
                  Node := Node.GetNextSibling;
            end;
          end;
      finally
        Nodes.EndUpdate;
      end;
    end;