Table of Contents

Modes

TTMSFNCTreeView supports two independent data modes that determine how node content is supplied. Choose the mode that fits your data size and update frequency; both expose the same rendering and interaction features.

Mode When to use
Collection-based Default. Nodes are stored in the Nodes collection. Best for moderate data sets where node objects need to persist between interactions.
Virtual Node data is returned on demand via OnGetNumberOfNodes and OnGetNodeText events. No collection is maintained. Best for large or dynamically computed data sets (millions of nodes).

Virtual mode

The tree retrieves values by firing events. The minimum implementation is two events.

A minimum virtual-mode tree wires OnGetNumberOfNodes and OnGetNodeText against the two columns declared in FormCreate:

procedure TForm1.FormCreate(Sender: TObject);
begin
  TreeView1.BeginUpdate;
  TreeView1.ClearNodeList;
  TreeView1.ClearNodes;
  TreeView1.ClearColumns;
  TreeView1.Columns.Add.Text := 'Column 1';
  TreeView1.Columns.Add.Text := 'Column 2';
  TreeView1.EndUpdate;
end;

procedure TForm1.TreeView1GetNumberOfNodes(Sender: TObject;
  ANode: TTMSFNCTreeViewVirtualNode; var ANumberOfNodes: Integer);
begin
  if ANode.Level = -1 then
    ANumberOfNodes := 3
  else if (ANode.Level = 0) and (ANode.Index = 0) then
    ANumberOfNodes := 5;
end;

procedure TForm1.TreeView1GetNodeText(Sender: TObject;
  ANode: TTMSFNCTreeViewVirtualNode; AColumn: Integer;
  AMode: TTMSFNCTreeViewNodeTextMode; var AText: string);
begin
  if ANode.Level = 0 then
    AText := 'Node ' + IntToStr(ANode.Index) + ' for ' + TreeView1.Columns[AColumn].Text
  else
    AText := 'Child Node ' + IntToStr(ANode.Index);
end;

OnGetNumberOfNodes

This event fires once per parent level during tree construction. Use ANode.Level to identify the level:

  • Level -1 — root level (first display).
  • Level 0 and above — child level for the node at that depth.
procedure TForm1.TreeView1GetNumberOfNodes(Sender: TObject;
  ANode: TTMSFNCTreeViewVirtualNode; var ANumberOfNodes: Integer);
begin
  if ANode.Level = -1 then
    ANumberOfNodes := 3
  else if (ANode.Level = 0) and (ANode.Index = 0) then
    ANumberOfNodes := 5;
end;

OnGetNodeText

Returns the text for each node column:

procedure TForm1.TreeView1GetNodeText(Sender: TObject;
  ANode: TTMSFNCTreeViewVirtualNode; AColumn: Integer;
  AMode: TTMSFNCTreeViewNodeTextMode; var AText: string);
begin
  if ANode.Level = 0 then
    AText := 'Node ' + IntToStr(ANode.Index) + ' for ' + TreeView1.Columns[AColumn].Text
  else
    AText := 'Child Node ' + IntToStr(ANode.Index);
end;
Virtual TreeView with root and child nodes

Virtual node identity

Each virtual node carries:

Property Type Description
Index Integer Position relative to its parent
Row Integer Global row index in the tree
Level Integer Depth; -1 = root level, 0+ = child depths
Children Integer Direct child count
TotalChildren Integer Recursive child count

Virtual tree methods

All virtual-mode operations use the Virtual suffix:

uses
  FMX.TMSFNCTreeView;

procedure TForm1.VirtualMethodExamples;
var
  pn, n: TTMSFNCTreeViewVirtualNode;
begin
  // Add and expand
  pn := TreeView1.AddVirtualNode;
  n := TreeView1.AddVirtualNode(pn); // add as child of pn
  TreeView1.ExpandVirtualNode(pn);

  // Remove
  TreeView1.RemoveVirtualNode(TreeView1.FocusedVirtualNode);

  // Expand / collapse all
  TreeView1.ExpandAllVirtual;
  TreeView1.CollapseAllVirtual;
end;

Collection-based mode

Nodes are stored in the Nodes collection. Each TTMSFNCTreeViewNode holds text, icons, check states, and an Extended flag per column.

procedure TForm1.PopulateTree;
var
  pn, childn: TTMSFNCTreeViewNode;
  I, C: Integer;
begin
  TreeView1.BeginUpdate;
  TreeView1.ClearNodeList;
  TreeView1.ClearNodes;
  TreeView1.ClearColumns;

  TreeView1.Columns.Add.Text := 'Column 1';
  TreeView1.Columns.Add.Text := 'Column 2';

  for I := 0 to 2 do
  begin
    pn := TreeView1.AddNode;
    for C := 0 to TreeView1.Columns.Count - 1 do
      pn.Text[C] := 'Node ' + IntToStr(I) + ' for ' + TreeView1.Columns[C].Text;

    if I = 0 then
    begin
      for C := 0 to 4 do
      begin
        childn := TreeView1.AddNode(pn);
        childn.Text[0] := 'Child Node ' + IntToStr(C);
      end;
    end;
  end;

  TreeView1.EndUpdate;
end;
Collection-based TreeView

Collection node properties

Property Description
Text[AColumn] Text value for the given column
CheckTypes[AColumn] tvntCheckBox or tvntRadioButton
Checked[AColumn] Check state
Extended When True, node spans all columns
VirtualNode Reference to the underlying TTMSFNCTreeViewVirtualNode

Collection tree methods

uses
  FMX.TMSFNCTreeView;

procedure TForm1.CollectionMethodExamples;
var
  pn, n: TTMSFNCTreeViewNode;
begin
  // Add root and child nodes
  pn := TreeView1.AddNode;
  pn.Text[0] := 'Parent';
  n := TreeView1.AddNode(pn); // child of pn
  n.Text[0] := 'Child';

  // Expand and collapse
  TreeView1.ExpandNode(pn);
  TreeView1.ExpandAll;
  TreeView1.CollapseAll;

  // Remove (either form)
  TreeView1.RemoveNode(TreeView1.FocusedNode);
  TreeView1.FocusedNode.Free;
end;

Switching between modes

You cannot mix virtual and collection-based operations in the same tree. If you use AddVirtualNode, call RemoveVirtualNode and the other *Virtual methods throughout. The same applies for the collection API. When using collection-based nodes and responding to events, the ANode.Node property on the virtual node gives back the collection item; the reverse direction is TTMSFNCTreeViewNode.VirtualNode.

Virtual mode events for customisation beyond text:

Event When to implement
OnGetNumberOfNodes Always — defines tree structure
OnGetNodeText Always — provides display text
OnGetNodeIcon Node icons per column
OnGetNodeCheckType Per-node checkbox/radiobutton type
OnIsNodeExpanded Initial expand/collapse state
OnIsNodeEnabled Disabled nodes
OnGetNodeHeight Variable height (HeightMode = tnhmVariable)

OnGetNumberOfNodes and OnGetNodeText are the two mandatory events; the rest are optional refinements that the tree consults while it builds each visible row. Because they are called during layout, keep the handlers cheap and free of side effects — they run again on every repaint, expand, and scroll.

uses
  FMX.TMSFNCTypes, FMX.TMSFNCBitmapContainer, FMX.TMSFNCTreeViewData,
  FMX.TMSFNCCustomTreeView, FMX.TMSFNCTreeView;

procedure TForm1.ConfigureVirtualNodeAppearance;
begin
  // Icons are resolved from a bitmap container, and variable height must be
  // switched on before OnGetNodeHeight is consulted.
  TreeView1.BitmapContainer := TMSFNCBitmapContainer1;
  TreeView1.NodesAppearance.HeightMode := tnhmVariable;

  TreeView1.OnGetNodeIcon := TreeView1GetNodeIcon;
  TreeView1.OnGetNodeCheckType := TreeView1GetNodeCheckType;
  TreeView1.OnIsNodeExpanded := TreeView1IsNodeExpanded;
  TreeView1.OnIsNodeEnabled := TreeView1IsNodeEnabled;
  TreeView1.OnGetNodeHeight := TreeView1GetNodeHeight;
end;

// Group rows carry a folder icon in the first column only.
procedure TForm1.TreeView1GetNodeIcon(Sender: TObject;
  ANode: TTMSFNCTreeViewVirtualNode; AColumn: Integer; ALarge: Boolean;
  var AIcon: TTMSFNCBitmap);
begin
  if (AColumn = 0) and (ANode.Level = 0) then
    AIcon := TMSFNCBitmapContainer1.FindBitmap('folder');
end;

// Only leaf rows can be ticked; groups show no check control.
procedure TForm1.TreeView1GetNodeCheckType(Sender: TObject;
  ANode: TTMSFNCTreeViewVirtualNode; AColumn: Integer;
  var ACheckType: TTMSFNCTreeViewNodeCheckType);
begin
  if (AColumn = 0) and (ANode.Level > 0) then
    ACheckType := tvntCheckBox
  else
    ACheckType := tvntNone;
end;

// The first group opens automatically the first time the tree is shown.
procedure TForm1.TreeView1IsNodeExpanded(Sender: TObject;
  ANode: TTMSFNCTreeViewVirtualNode; var AExpanded: Boolean);
begin
  AExpanded := (ANode.Level = 0) and (ANode.Index = 0);
end;

// Detail rows below the second level are shown but not interactive.
procedure TForm1.TreeView1IsNodeEnabled(Sender: TObject;
  ANode: TTMSFNCTreeViewVirtualNode; var AEnabled: Boolean);
begin
  AEnabled := ANode.Level < 2;
end;

// Group rows are taller than their children.
procedure TForm1.TreeView1GetNodeHeight(Sender: TObject;
  ANode: TTMSFNCTreeViewVirtualNode; AColumn: Integer; var AHeight: Double);
begin
  if ANode.Level = 0 then
    AHeight := 36
  else
    AHeight := 24;
end;

Two ordering constraints are worth remembering: NodesAppearance.HeightMode must be tnhmVariable before OnGetNodeHeight has any effect, and BitmapContainer must be assigned before OnGetNodeIcon can resolve a bitmap by name. OnIsNodeExpanded supplies the initial state only — once the user expands or collapses a node, the tree keeps its own state and no longer asks.

See also

  • Nodes — fixed vs variable height, checkboxes, extended nodes
  • Interaction — editing, filtering, sorting