Skip to main content
Table of Contents

Customization

Custom drawing

TTMSFNCTreeView exposes paired OnBeforeDraw* / OnAfterDraw* events for every visual element: nodes, node text, node icons, checkboxes, expand/collapse icons, column headers, and groups. Use the After variant to draw on top of the default rendering.

Example: highlight nodes by value

The sample below draws a red indicator circle in the year column for rows built in 2012 or later:

Custom-draw node textPascalC++
uses
  System.SysUtils, System.Types, FMX.TMSFNCGraphics, FMX.TMSFNCGraphicsTypes,
  FMX.TMSFNCTreeView;

procedure TForm1.TreeView1AfterDrawNodeText(Sender: TObject;
  AGraphics: TTMSFNCGraphics; ARect: TRectF; AColumn: Integer;
  ANode: TTMSFNCTreeViewVirtualNode; AText: string);
var
  v: Integer;
begin
  if TryStrToInt(AText, v) and (AColumn = 1) and (v >= 2012) then
  begin
    AGraphics.Fill.Kind := gfkSolid;
    AGraphics.Fill.Color := gcRed;
    AGraphics.DrawEllipse(
      RectF(ARect.Right - 12, ARect.Top + 2, ARect.Right - 2, ARect.Top + 12), gcrmNone);
  end;
end;
void __fastcall TForm1::TreeView1AfterDrawNodeText(TObject *Sender,
  TTMSFNCGraphics *AGraphics, const TRectF &ARect, int AColumn,
  TTMSFNCTreeViewVirtualNode *ANode, UnicodeString AText)
{
  int v;
  if (TryStrToInt(AText, v) && (AColumn == 1) && (v >= 2012))
  {
    AGraphics->Fill->Kind = gfkSolid;
    AGraphics->Fill->Color = gcRed;
    AGraphics->DrawEllipse(
      RectF(ARect.Right - 12, ARect.Top + 2, ARect.Right - 2, ARect.Top + 12), gcrmNone);
  }
}
Custom red indicator drawn in year column

Available drawing events

Event group Description
OnBeforeDrawNode / OnAfterDrawNode Full node background
OnBeforeDrawNodeText / OnAfterDrawNodeText Node cell text
OnBeforeDrawNodeIcon / OnAfterDrawNodeIcon Node icon
OnBeforeDrawNodeCheck / OnAfterDrawNodeCheck Checkbox/radiobutton
OnBeforeDrawNodeExpand / OnAfterDrawNodeExpand Expand/collapse icon
OnBeforeDrawColumn / OnAfterDrawColumn Column header area
OnBeforeDrawColumnHeader / OnAfterDrawColumnHeader Column header text area
OnBeforeDrawGroup / OnAfterDrawGroup Group header

Assign the handlers you need once, typically when the form is created. After handlers paint on top of the default rendering; Before handlers can also suppress it. The sample wires three of the events:

Wire the drawing eventsPascalC++
uses
  System.SysUtils, System.Types, FMX.TMSFNCGraphics, FMX.TMSFNCGraphicsTypes,
  FMX.TMSFNCTreeView;

procedure TForm1.ConfigureDrawEvents;
begin
  // After* events draw on top of the default rendering. Before* events
  // additionally let the handler suppress the default drawing.
  TreeView1.OnAfterDrawNode := TreeView1AfterDrawNode;
  TreeView1.OnAfterDrawNodeText := TreeView1AfterDrawNodeText;
  TreeView1.OnAfterDrawColumnHeader := TreeView1AfterDrawColumnHeader;
end;

// Draw a thin separator under every node.
procedure TForm1.TreeView1AfterDrawNode(Sender: TObject;
  AGraphics: TTMSFNCGraphics; ARect: TRectF; ANode: TTMSFNCTreeViewVirtualNode);
begin
  AGraphics.Stroke.Color := gcSilver;
  AGraphics.DrawLine(PointF(ARect.Left, ARect.Bottom),
    PointF(ARect.Right, ARect.Bottom));
end;

// Mark the text of the first column with a small dot.
procedure TForm1.TreeView1AfterDrawNodeText(Sender: TObject;
  AGraphics: TTMSFNCGraphics; ARect: TRectF; AColumn: Integer;
  ANode: TTMSFNCTreeViewVirtualNode; AText: string);
begin
  if AColumn = 0 then
  begin
    AGraphics.Fill.Kind := gfkSolid;
    AGraphics.Fill.Color := gcSteelblue;
    AGraphics.DrawEllipse(
      RectF(ARect.Left + 2, ARect.Top + 2, ARect.Left + 8, ARect.Top + 8), gcrmNone);
  end;
end;

// Underline every column header.
procedure TForm1.TreeView1AfterDrawColumnHeader(Sender: TObject;
  AGraphics: TTMSFNCGraphics; ARect: TRectF; AColumn: Integer;
  AKind: TTMSFNCTreeViewCacheItemKind);
begin
  AGraphics.Stroke.Color := gcSteelblue;
  AGraphics.DrawLine(PointF(ARect.Left, ARect.Bottom - 1),
    PointF(ARect.Right, ARect.Bottom - 1));
end;
void __fastcall TForm1::ConfigureDrawEvents()
{
  // After* events draw on top of the default rendering. Before* events
  // additionally let the handler suppress the default drawing.
  TreeView1->OnAfterDrawNode = TreeView1AfterDrawNode;
  TreeView1->OnAfterDrawNodeText = TreeView1AfterDrawNodeText;
  TreeView1->OnAfterDrawColumnHeader = TreeView1AfterDrawColumnHeader;
}

// Draw a thin separator under every node.
void __fastcall TForm1::TreeView1AfterDrawNode(TObject *Sender,
  TTMSFNCGraphics *AGraphics, const TRectF &ARect,
  TTMSFNCTreeViewVirtualNode *ANode)
{
  AGraphics->Stroke->Color = gcSilver;
  AGraphics->DrawLine(PointF(ARect.Left, ARect.Bottom),
    PointF(ARect.Right, ARect.Bottom));
}

// Mark the text of the first column with a small dot.
void __fastcall TForm1::TreeView1AfterDrawNodeText(TObject *Sender,
  TTMSFNCGraphics *AGraphics, const TRectF &ARect, int AColumn,
  TTMSFNCTreeViewVirtualNode *ANode, UnicodeString AText)
{
  if (AColumn == 0)
  {
    AGraphics->Fill->Kind = gfkSolid;
    AGraphics->Fill->Color = gcSteelblue;
    AGraphics->DrawEllipse(
      RectF(ARect.Left + 2, ARect.Top + 2, ARect.Left + 8, ARect.Top + 8), gcrmNone);
  }
}

// Underline every column header.
void __fastcall TForm1::TreeView1AfterDrawColumnHeader(TObject *Sender,
  TTMSFNCGraphics *AGraphics, const TRectF &ARect, int AColumn,
  TTMSFNCTreeViewCacheItemKind AKind)
{
  AGraphics->Stroke->Color = gcSteelblue;
  AGraphics->DrawLine(PointF(ARect.Left, ARect.Bottom - 1),
    PointF(ARect.Right, ARect.Bottom - 1));
}

JSON viewer

Use ViewJSON to load and display any JSON document as a navigable tree. Key names appear in one column and values in another.

Display a JSON documentPascalC++
uses
  FMX.TMSFNCTreeView;

procedure TForm1.LoadJSON;
var
  o: TTMSFNCTreeViewViewJSONOptions;
begin
  o := TTMSFNCTreeViewViewJSONOptions.Create;
  try
    o.ArrayItemPrefix := 'Item ';
    o.NameHTMLTemplate := '<B><#NAME></B>';
    o.ValueHTMLTemplate := '<FONT color="gcRed"><#VALUE></FONT>';
    TreeView1.ViewJSON('mydata.json', o);
  finally
    o.Free;
  end;
end;
void __fastcall TForm1::LoadJSON()
{
  TTMSFNCTreeViewViewJSONOptions *o = new TTMSFNCTreeViewViewJSONOptions();
  try
  {
    o->ArrayItemPrefix = "Item ";
    o->NameHTMLTemplate = "<B><#NAME></B>";
    o->ValueHTMLTemplate = "<FONT color=\"gcRed\"><#VALUE></FONT>";
    TreeView1->ViewJSON("mydata.json", o);
  }
  __finally
  {
    delete o;
  }
}

#NAME and #VALUE are mandatory placeholders in the HTML templates. Omit TTMSFNCTreeViewViewJSONOptions to use defaults.


TMS Mini HTML rendering

The tree renders HTML-formatted text natively in any node column. No property needs to be enabled — the component switches to HTML mode automatically when node text starts with <.

Supported tags:

Tag Effect
<B>, </B> Bold
<I>, </I> Italic
<U>, </U> Underline
<S>, </S> Strikethrough
<FONT face="..." size="..." color="..."> Font face, size, color
<A href="url_or_value"> Hyperlink or anchor
<IMG src="name"> Image from BitmapContainer
<P align="..."> Paragraph with alignment
<BR> Line break
<UL> / <LI> Unordered list
<SUB>, <SUP> Subscript/superscript
<IND x="N"> Indent N pixels
<SHAD> Text shadow
<Z> Hidden text

Example:

HTML-formatted node textPascalC++
uses
  FMX.TMSFNCTreeView;

procedure TForm1.SetHTMLNodeText(ANode: TTMSFNCTreeViewVirtualNode);
begin
  // Virtual nodes expose Text read-only; set it on the backing collection node.
  if ANode.Node <> nil then
    ANode.Node.Text[0] := '<B>Embarcadero</B> <FONT color="gcRed">Delphi</FONT>';
end;
void __fastcall TForm1::SetHTMLNodeText(TTMSFNCTreeViewVirtualNode *ANode)
{
  // Virtual nodes expose Text read-only; set it on the backing collection node.
  if (ANode->Node != nullptr)
    ANode->Node->Text[0] = "<B>Embarcadero</B> <FONT color=\"gcRed\">Delphi</FONT>";
}

Anchor click handling

When a user clicks an <A href="value"> element where value is not a URL:

Handle anchor clicksPascalC++
uses
  FMX.Dialogs, FMX.TMSFNCTreeView;

procedure TForm1.TreeView1NodeAnchorClick(Sender: TObject;
  ANode: TTMSFNCTreeViewVirtualNode; AColumn: Integer; AAnchor: string);
begin
  ShowMessage('Anchor clicked: ' + AAnchor);
end;
void __fastcall TForm1::TreeView1NodeAnchorClick(TObject *Sender,
  TTMSFNCTreeViewVirtualNode *ANode, int AColumn, UnicodeString AAnchor)
{
  ShowMessage("Anchor clicked: " + AAnchor);
}

Set Interaction.AutoOpenURL := True to open http://, https://, and mailto: links automatically via the system browser.


TTMSFNCCheckedTreeView

TTMSFNCCheckedTreeView is a preconfigured subclass that adds a checkbox to every node automatically. Its API is identical to TTMSFNCTreeView; there is no need to set CheckTypes per node.


Combining custom drawing, HTML text and anchors

The custom drawing events, HTML rendering and anchor clicks are most useful together. HTML text gives you styled, clickable content without owner drawing, while an After draw event adds decoration that markup cannot express, such as a status badge, and the anchor event turns the rendered link into an action. Reach for the combination when a node must look rich and also respond to the user, for example a product list with a "details" link and a highlight for recent entries. The sample below fills two columns with HTML text, draws a badge in the year column and shows the clicked anchor value.

Combine HTML text, custom drawing and anchor clicksPascalC++
uses
  System.SysUtils, System.Types, FMX.Dialogs, FMX.TMSFNCGraphics,
  FMX.TMSFNCGraphicsTypes, FMX.TMSFNCTreeView;

procedure TForm1.SetupCombinedCustomization;
var
  n: TTMSFNCTreeViewNode;
begin
  TreeView1.BeginUpdate;
  TreeView1.ClearNodeList;
  TreeView1.ClearNodes;
  TreeView1.ClearColumns;
  TreeView1.Columns.Add.Text := 'Product';
  TreeView1.Columns.Add.Text := 'Year';

  // HTML text: the tree switches to HTML rendering when the text starts with '<'
  n := TreeView1.AddNode;
  n.Text[0] := '<B>Delphi</B> <A href="delphi">details</A>';
  n.Text[1] := '2024';

  n := TreeView1.AddNode;
  n.Text[0] := '<I>C++Builder</I> <A href="cppbuilder">details</A>';
  n.Text[1] := '2009';

  // Custom drawing and anchor handling layered on the HTML text
  TreeView1.OnAfterDrawNodeText := TreeView1AfterDrawNodeBadge;
  TreeView1.OnNodeAnchorClick := TreeView1NodeAnchorClickCombined;
  TreeView1.EndUpdate;
end;

procedure TForm1.TreeView1AfterDrawNodeBadge(Sender: TObject;
  AGraphics: TTMSFNCGraphics; ARect: TRectF; AColumn: Integer;
  ANode: TTMSFNCTreeViewVirtualNode; AText: string);
var
  v: Integer;
begin
  if (AColumn = 1) and TryStrToInt(AText, v) and (v >= 2020) then
  begin
    AGraphics.Fill.Kind := gfkSolid;
    AGraphics.Fill.Color := gcRed;
    AGraphics.DrawEllipse(
      RectF(ARect.Right - 12, ARect.Top + 2, ARect.Right - 2, ARect.Top + 12), gcrmNone);
  end;
end;

procedure TForm1.TreeView1NodeAnchorClickCombined(Sender: TObject;
  ANode: TTMSFNCTreeViewVirtualNode; AColumn: Integer; AAnchor: string);
begin
  ShowMessage('Anchor clicked: ' + AAnchor);
end;
void __fastcall TForm1::SetupCombinedCustomization()
{
  TreeView1->BeginUpdate();
  TreeView1->ClearNodeList();
  TreeView1->ClearNodes();
  TreeView1->ClearColumns();
  TreeView1->Columns->Add()->Text = "Product";
  TreeView1->Columns->Add()->Text = "Year";

  // HTML text: the tree switches to HTML rendering when the text starts with '<'
  TTMSFNCTreeViewNode *n = TreeView1->AddNode();
  n->Text[0] = "<B>Delphi</B> <A href=\"delphi\">details</A>";
  n->Text[1] = "2024";

  n = TreeView1->AddNode();
  n->Text[0] = "<I>C++Builder</I> <A href=\"cppbuilder\">details</A>";
  n->Text[1] = "2009";

  // Custom drawing and anchor handling layered on the HTML text
  TreeView1->OnAfterDrawNodeText = TreeView1AfterDrawNodeBadge;
  TreeView1->OnNodeAnchorClick = TreeView1NodeAnchorClickCombined;
  TreeView1->EndUpdate();
}

void __fastcall TForm1::TreeView1AfterDrawNodeBadge(TObject *Sender,
  TTMSFNCGraphics *AGraphics, const TRectF &ARect, int AColumn,
  TTMSFNCTreeViewVirtualNode *ANode, UnicodeString AText)
{
  int v;
  if ((AColumn == 1) && TryStrToInt(AText, v) && (v >= 2020))
  {
    AGraphics->Fill->Kind = gfkSolid;
    AGraphics->Fill->Color = gcRed;
    AGraphics->DrawEllipse(
      RectF(ARect.Right - 12, ARect.Top + 2, ARect.Right - 2, ARect.Top + 12), gcrmNone);
  }
}

void __fastcall TForm1::TreeView1NodeAnchorClickCombined(TObject *Sender,
  TTMSFNCTreeViewVirtualNode *ANode, int AColumn, UnicodeString AAnchor)
{
  ShowMessage("Anchor clicked: " + AAnchor);
}

See also

  • Nodes — checkbox and extended nodes
  • Interaction — anchor click and interaction properties