Skip to main content
Table of Contents

Nodes

Node configuration

The NodesAppearance property on TTMSFNCTreeView controls the visual style of all nodes. Individual columns can override these defaults by setting UseDefaultAppearance = False on a TTMSFNCTreeViewColumn. Set the shared appearance once, inside a BeginUpdate / EndUpdate pair:

Configure the node appearancePascalC++
uses
  FMX.TMSFNCGraphicsTypes, FMX.TMSFNCCustomTreeView, FMX.TMSFNCTreeView;

procedure TForm1.ConfigureNodes;
begin
  TreeView1.BeginUpdate;

  // Defaults shared by every node
  TreeView1.NodesAppearance.HeightMode := tnhmFixed;
  TreeView1.NodesAppearance.FixedHeight := 28;
  TreeView1.NodesAppearance.LevelIndent := 20;
  TreeView1.NodesAppearance.ShowLines := True;
  TreeView1.NodesAppearance.SelectionArea := tsaFull;
  TreeView1.NodesAppearance.SelectedFill.Kind := gfkSolid;
  TreeView1.NodesAppearance.SelectedFill.Color := gcSteelblue;
  TreeView1.NodesAppearance.SelectedFontColor := gcWhite;

  // A column can opt out of the defaults and use its own font
  TreeView1.Columns[0].UseDefaultAppearance := False;
  TreeView1.Columns[0].Font.Color := gcPurple;

  TreeView1.EndUpdate;
end;
void __fastcall TForm1::ConfigureNodes()
{
  TreeView1->BeginUpdate();

  // Defaults shared by every node
  TreeView1->NodesAppearance->HeightMode = tnhmFixed;
  TreeView1->NodesAppearance->FixedHeight = 28;
  TreeView1->NodesAppearance->LevelIndent = 20;
  TreeView1->NodesAppearance->ShowLines = true;
  TreeView1->NodesAppearance->SelectionArea = tsaFull;
  TreeView1->NodesAppearance->SelectedFill->Kind = gfkSolid;
  TreeView1->NodesAppearance->SelectedFill->Color = gcSteelblue;
  TreeView1->NodesAppearance->SelectedFontColor = gcWhite;

  // A column can opt out of the defaults and use its own font
  TreeView1->Columns->Items[0]->UseDefaultAppearance = false;
  TreeView1->Columns->Items[0]->Font->Color = gcPurple;

  TreeView1->EndUpdate();
}

Adding, inserting, and removing nodes

Both collection-based and virtual modes are covered in Modes. The snippets here use collection-based mode.

Reset the tree to a clean statePascalC++
uses
  FMX.TMSFNCTreeView;

procedure TForm1.ResetTree;
begin
  TreeView1.BeginUpdate;
  TreeView1.ClearNodeList;
  TreeView1.ClearNodes;
  TreeView1.ClearColumns;
  TreeView1.Columns.Add.Text := 'Column 1';
  TreeView1.EndUpdate;
end;
void __fastcall TForm1::ResetTree()
{
  TreeView1->BeginUpdate();
  TreeView1->ClearNodeList();
  TreeView1->ClearNodes();
  TreeView1->ClearColumns();
  TreeView1->Columns->Add()->Text = "Column 1";
  TreeView1->EndUpdate();
}

Add a root node and a child under it:

Add root and child nodesPascalC++
uses
  FMX.TMSFNCTreeView;

procedure TForm1.AddNodes;
var
  root, child: TTMSFNCTreeViewNode;
begin
  // Root node
  root := TreeView1.AddNode;
  root.Text[0] := 'Root Node';

  // Child node — pass the parent to AddNode
  child := TreeView1.AddNode(root);
  child.Text[0] := 'Child Node';

  TreeView1.ExpandNode(root);
end;
void __fastcall TForm1::AddNodes()
{
  // Root node
  TTMSFNCTreeViewNode *root = TreeView1->AddNode();
  root->Text[0] = "Root Node";

  // Child node - pass the parent to AddNode
  TTMSFNCTreeViewNode *child = TreeView1->AddNode(root);
  child->Text[0] = "Child Node";

  TreeView1->ExpandNode(root);
}
Node with child nodes expanded

Insert a node at a specific position, or remove nodes in several ways:

Insert and remove nodesPascalC++
uses
  FMX.TMSFNCTreeView;

procedure TForm1.InsertNodeExample;
begin
  TreeView1.InsertNode(1, nil); // insert at index 1 at root level
end;

procedure TForm1.RemoveNodeExamples(AParent: TTMSFNCTreeViewNode);
begin
  // By method
  TreeView1.RemoveNode(TreeView1.FocusedNode);

  // By freeing the collection item — same result
  TreeView1.FocusedNode.Free;

  // Remove all children of a node
  TreeView1.RemoveNodeChildren(AParent);

  // Remove all selected nodes
  TreeView1.RemoveSelectedNodes;
end;
void __fastcall TForm1::InsertNodeExample()
{
  TreeView1->InsertNode(1, nullptr); // insert at index 1 at root level
}

void __fastcall TForm1::RemoveNodeExamples(TTMSFNCTreeViewNode *AParent)
{
  // By method
  TreeView1->RemoveNode(TreeView1->FocusedNode);

  // By freeing the collection item - same result
  delete TreeView1->FocusedNode;

  // Remove all children of a node
  TreeView1->RemoveNodeChildren(AParent);

  // Remove all selected nodes
  TreeView1->RemoveSelectedNodes();
}

Fixed vs variable node height

Fixed height (default)

All nodes share the same height, set by NodesAppearance.FixedHeight. Icons and text that exceed this height are clipped.

Fixed node heightPascalC++
uses
  FMX.TMSFNCTreeView;

procedure TForm1.SetFixedNodeHeight;
begin
  TreeView1.NodesAppearance.FixedHeight := 40;
end;
void __fastcall TForm1::SetFixedNodeHeight()
{
  TreeView1->NodesAppearance->FixedHeight = 40;
}
Fixed height nodes with oversized icons

Variable height

Set NodesAppearance.HeightMode to tnhmVariable to let each node height expand to fit its content (icon size or wrapped text). The NodesAppearance.VariableMinimumHeight sets the minimum height for scroll calculations.

Variable heightPascalC++
// Variable node height — nodes adapt to icon/text content
procedure TForm1.SetupVariableHeight;
begin
  TreeView1.BeginUpdate;
  TreeView1.ClearNodeList;
  TreeView1.ClearNodes;
  TreeView1.ClearColumns;
  TreeView1.Columns.Add.Text := 'Variable TreeView';
  TreeView1.Columns[0].WordWrapping := True;
  TreeView1.NodesAppearance.HeightMode := tnhmVariable;
  TreeView1.EndUpdate;
end;
// Variable node height - nodes adapt to icon/text content
void __fastcall TForm1::SetupVariableHeight()
{
  TreeView1->BeginUpdate();
  TreeView1->ClearNodeList();
  TreeView1->ClearNodes();
  TreeView1->ClearColumns();
  TreeView1->Columns->Add()->Text = "Variable TreeView";
  TreeView1->Columns->Items[0]->WordWrapping = true;
  TreeView1->NodesAppearance->HeightMode = tnhmVariable;
  TreeView1->EndUpdate();
}
Variable height nodes with oversized icons

With word wrapping enabled, long text also drives the node height:

Word-wrapped variable-height nodesPascalC++
uses
  FMX.TMSFNCTreeView;

procedure TForm1.EnableWordWrap;
begin
  TreeView1.Columns[0].WordWrapping := True;
  TreeView1.NodesAppearance.HeightMode := tnhmVariable;
end;
void __fastcall TForm1::EnableWordWrap()
{
  TreeView1->Columns->Items[0]->WordWrapping = true;
  TreeView1->NodesAppearance->HeightMode = tnhmVariable;
}
Variable height nodes with wrapped text

Checkbox and radiobutton support

Each node column can display a checkbox or radiobutton independently via the CheckTypes property:

Checkbox and radiobutton supportPascalC++
// Checkbox and radiobutton nodes
procedure TForm1.SetupCheckNodes;
var
  n: TTMSFNCTreeViewNode;
  I: Integer;
begin
  TreeView1.BeginUpdate;
  TreeView1.ClearNodeList;
  TreeView1.ClearNodes;
  TreeView1.ClearColumns;
  TreeView1.Columns.Add.Text := 'Checkbox / radiobutton';

  for I := 0 to 3 do
  begin
    n := TreeView1.Nodes.Add;
    n.Text[0] := 'CheckBox ' + IntToStr(I);
    n.CheckTypes[0] := tvntCheckBox;
    if Odd(I) then
      n.Checked[0] := True;
  end;

  for I := 0 to 3 do
  begin
    n := TreeView1.Nodes.Add;
    n.Text[0] := 'Radiobutton ' + IntToStr(I);
    n.CheckTypes[0] := tvntRadioButton;
    if I = 2 then
      n.Checked[0] := True;
  end;

  TreeView1.EndUpdate;
end;
// Checkbox and radiobutton nodes
void __fastcall TForm1::SetupCheckNodes()
{
  TreeView1->BeginUpdate();
  TreeView1->ClearNodeList();
  TreeView1->ClearNodes();
  TreeView1->ClearColumns();
  TreeView1->Columns->Add()->Text = "Checkbox / radiobutton";

  for (int I = 0; I < 4; I++)
  {
    TTMSFNCTreeViewNode *n = TreeView1->Nodes->Add();
    n->Text[0] = "CheckBox " + IntToStr(I);
    n->CheckTypes[0] = tvntCheckBox;
    if (I % 2 == 1)
      n->Checked[0] = true;
  }

  for (int I = 0; I < 4; I++)
  {
    TTMSFNCTreeViewNode *n = TreeView1->Nodes->Add();
    n->Text[0] = "Radiobutton " + IntToStr(I);
    n->CheckTypes[0] = tvntRadioButton;
    if (I == 2)
      n->Checked[0] = true;
  }

  TreeView1->EndUpdate();
}
Nodes with checkboxes and radiobuttons

To use virtual mode for checkboxes, implement OnGetNodeCheckType and OnIsNodeChecked.

Toggle check state programmatically:

Toggle a node's check statePascalC++
uses
  FMX.TMSFNCTreeView;

procedure TForm1.ToggleChecks(ANode: TTMSFNCTreeViewNode;
  AVirtualNode: TTMSFNCTreeViewVirtualNode);
begin
  TreeView1.ToggleCheckNode(ANode, 0);         // collection-based
  TreeView1.CheckVirtualNode(AVirtualNode, 0); // virtual
end;
void __fastcall TForm1::ToggleChecks(TTMSFNCTreeViewNode *ANode,
  TTMSFNCTreeViewVirtualNode *AVirtualNode)
{
  TreeView1->ToggleCheckNode(ANode, 0);         // collection-based
  TreeView1->CheckVirtualNode(AVirtualNode, 0); // virtual
}

Extended nodes

An extended node spans all columns and displays text from column 0 only. Extended nodes are not selectable or editable by default; this can be changed via Interaction.ExtendedSelectable and Interaction.ExtendedEditable.

Use extended nodes as section headers within a flat list:

Extended nodesPascalC++
// Extended nodes that span all columns
procedure TForm1.SetupExtendedNodes;
var
  n, pn: TTMSFNCTreeViewNode;
  I: Integer;
begin
  TreeView1.BeginUpdate;
  TreeView1.ClearNodeList;
  TreeView1.ClearNodes;
  TreeView1.ClearColumns;
  TreeView1.Columns.Add.Text := 'Column 1';
  TreeView1.Columns.Add.Text := 'Column 2';

  pn := TreeView1.Nodes.Add;
  pn.Text[0] := 'Normal Node';

  pn := TreeView1.Nodes.Add;
  pn.Text[0] := 'Extended Group Header';
  pn.Extended := True;

  for I := 0 to 3 do
  begin
    n := TreeView1.AddNode(pn);
    if I = 1 then
    begin
      n.Text[0] := 'Sub-header ' + IntToStr(I);
      n.Extended := True;
    end
    else
    begin
      n.Text[0] := 'Normal Node Column 1 ' + IntToStr(I);
      n.Text[1] := 'Normal Node Column 2 ' + IntToStr(I);
    end;
  end;

  TreeView1.EndUpdate;
end;
// Extended nodes that span all columns
void __fastcall TForm1::SetupExtendedNodes()
{
  TreeView1->BeginUpdate();
  TreeView1->ClearNodeList();
  TreeView1->ClearNodes();
  TreeView1->ClearColumns();
  TreeView1->Columns->Add()->Text = "Column 1";
  TreeView1->Columns->Add()->Text = "Column 2";

  TTMSFNCTreeViewNode *pn = TreeView1->Nodes->Add();
  pn->Text[0] = "Normal Node";

  pn = TreeView1->Nodes->Add();
  pn->Text[0] = "Extended Group Header";
  pn->Extended = true;

  for (int I = 0; I < 4; I++)
  {
    TTMSFNCTreeViewNode *n = TreeView1->AddNode(pn);
    if (I == 1)
    {
      n->Text[0] = "Sub-header " + IntToStr(I);
      n->Extended = true;
    }
    else
    {
      n->Text[0] = "Normal Node Column 1 " + IntToStr(I);
      n->Text[1] = "Normal Node Column 2 " + IntToStr(I);
    }
  }

  TreeView1->EndUpdate();
}
Mix of normal and extended nodes

In virtual mode, return True from OnIsNodeExtended to mark a node as extended.


NodesAppearance reference

Property Description
FixedHeight Node height when HeightMode = tnhmFixed
HeightMode tnhmFixed or tnhmVariable
VariableMinimumHeight Minimum height for variable-height nodes (default 25)
Font.Color Default node text color
Font Default node font
Fill, Stroke Default node background and border
SelectedFill, SelectedFontColor Selected state style
DisabledFill, DisabledFontColor Disabled state style
LevelIndent Pixel indent per nesting level
ShowLines Draw tree connector lines
ShowFocus Draw focus indicator
ExpandWidth, ExpandHeight Size of the expand/collapse icon area
SelectionArea tsaFull — full row, or tsaText — text only
Tree view with connector lines, a wider level indent and a full-row selected node Tree view with connector lines, a wider level indent and a full-row selected node

Combining height modes, extended nodes and checkboxes

The node features in this guide are independent, so they combine freely. Variable height lets wrapped text grow a row, extended nodes provide full-width section headers, and per-node check types turn the children into a checklist. Use the combination for task lists, settings panels or any grouped list where item text has an unpredictable length. The sample builds a selectable "Open tasks" header with four word-wrapped checkbox children and expands it.

Combine variable height, extended nodes and checkboxesPascalC++
uses
  System.SysUtils, FMX.TMSFNCCustomTreeView, FMX.TMSFNCTreeView;

procedure TForm1.SetupCombinedNodes;
var
  hdr, n: TTMSFNCTreeViewNode;
  I: Integer;
begin
  TreeView1.BeginUpdate;
  TreeView1.ClearNodeList;
  TreeView1.ClearNodes;
  TreeView1.ClearColumns;
  TreeView1.Columns.Add.Text := 'Task';
  TreeView1.Columns[0].WordWrapping := True;

  // Variable height lets wrapped text grow the node
  TreeView1.NodesAppearance.HeightMode := tnhmVariable;
  // Allow the extended header to be selected
  TreeView1.Interaction.ExtendedSelectable := True;

  hdr := TreeView1.AddNode;
  hdr.Text[0] := 'Open tasks';
  hdr.Extended := True;

  for I := 0 to 3 do
  begin
    n := TreeView1.AddNode(hdr);
    n.Text[0] := 'Task ' + IntToStr(I) +
      ': a longer description that wraps onto several lines';
    n.CheckTypes[0] := tvntCheckBox;
    n.Checked[0] := Odd(I);
  end;

  TreeView1.ExpandNode(hdr);
  TreeView1.EndUpdate;
end;
void __fastcall TForm1::SetupCombinedNodes()
{
  TreeView1->BeginUpdate();
  TreeView1->ClearNodeList();
  TreeView1->ClearNodes();
  TreeView1->ClearColumns();
  TreeView1->Columns->Add()->Text = "Task";
  TreeView1->Columns->Items[0]->WordWrapping = true;

  // Variable height lets wrapped text grow the node
  TreeView1->NodesAppearance->HeightMode = tnhmVariable;
  // Allow the extended header to be selected
  TreeView1->Interaction->ExtendedSelectable = true;

  TTMSFNCTreeViewNode *hdr = TreeView1->AddNode();
  hdr->Text[0] = "Open tasks";
  hdr->Extended = true;

  for (int I = 0; I < 4; I++)
  {
    TTMSFNCTreeViewNode *n = TreeView1->AddNode(hdr);
    n->Text[0] = "Task " + IntToStr(I) +
      ": a longer description that wraps onto several lines";
    n->CheckTypes[0] = tvntCheckBox;
    n->Checked[0] = (I % 2 == 1);
  }

  TreeView1->ExpandNode(hdr);
  TreeView1->EndUpdate();
}

See also

  • Modes — virtual vs collection-based approach
  • Interaction — editing nodes, filtering, sorting