Table of Contents

Checked Nodes

TTMSFNCCheckedTreeView extends TTMSFNCTreeView with check boxes on nodes. Use it for hierarchical choices where a flat checked list would lose parent-child context.

Populate Nodes

Create nodes with the normal tree API, then set Checked[Node] for the initial state. A node added at run time does not show a check box until you set its check type - Node.CheckTypes[0] := tvntCheckBox - because the control assigns that automatically only for nodes created at design time.

procedure TForm1.ConfigureCheckedTreeView;
var
  RootNode: TTMSFNCTreeViewNode;
  ChildNode: TTMSFNCTreeViewNode;
begin
  TMSFNCCheckedTreeView1.Clear;

  RootNode := TMSFNCCheckedTreeView1.AddNode;
  RootNode.CheckTypes[0] := tvntCheckBox;
  RootNode.Text[0] := 'Export';
  TMSFNCCheckedTreeView1.Checked[RootNode] := True;

  ChildNode := TMSFNCCheckedTreeView1.AddNode(RootNode);
  ChildNode.CheckTypes[0] := tvntCheckBox;
  ChildNode.Text[0] := 'Include images';
  TMSFNCCheckedTreeView1.Checked[ChildNode] := True;

  ChildNode := TMSFNCCheckedTreeView1.AddNode(RootNode);
  ChildNode.CheckTypes[0] := tvntCheckBox;
  ChildNode.Text[0] := 'Include audit trail';
end;
A checked tree view with three branches: one fully checked, one checked parent with unchecked children, and an unchecked parent containing a checked child The same mixed checked state in the dark theme

Read Checked Nodes

Use CheckedNodes to collect checked nodes. The optional ARecurse argument controls whether child nodes are included recursively.

procedure TForm1.ShowCheckedNodes;
var
  Nodes: TTMSFNCTreeViewCheckedNodes;
  Node: TTMSFNCTreeViewNode;
  Summary: string;
begin
  Nodes := TMSFNCCheckedTreeView1.CheckedNodes(0, True);
  Summary := '';

  for Node in Nodes do
  begin
    if Summary <> '' then
      Summary := Summary + ', ';

    Summary := Summary + Node.Text[0];
  end;

  ShowMessage(Summary);
end;

Combining hierarchy, checked state, and recursive collection

Keep tree structure and checked state together in one setup routine when the checked hierarchy mirrors an application feature set. This makes later collection predictable because each checked node already carries the display text that users saw:

procedure TForm1.FormCreate(Sender: TObject);
var
  grpNode, childNode: TTMSFNCTreeViewNode;
begin
  TMSFNCCheckedTreeView1.BeginUpdate;
  TMSFNCCheckedTreeView1.Options.Interaction.RecursiveChecked := True;

  grpNode := TMSFNCCheckedTreeView1.AddNode(nil);
  grpNode.CheckTypes[0] := tvntCheckBox;
  grpNode.Text[0] := 'Reporting';
  grpNode.Checked := True; // checks all children automatically

  childNode := TMSFNCCheckedTreeView1.AddNode(grpNode);
  childNode.CheckTypes[0] := tvntCheckBox;
  childNode.Text[0] := 'Daily summary';
  childNode := TMSFNCCheckedTreeView1.AddNode(grpNode);
  childNode.CheckTypes[0] := tvntCheckBox;
  childNode.Text[0] := 'Weekly digest';
  childNode.Checked := False; // override: this one stays unchecked

  TMSFNCCheckedTreeView1.EndUpdate;
end;

procedure TForm1.btnCollectClick(Sender: TObject);
var
  nodes: TTMSFNCTreeViewCheckedNodes;
begin
  nodes := TMSFNCCheckedTreeView1.CheckedNodes(0, True); // column 0, recurse
  for var n in nodes do
    Memo1.Lines.Add(n.Text[0]);
end;

Reading the checked set

CheckedNodes returns a TTMSFNCTreeViewCheckedNodes — a plain array of TTMSFNCTreeViewNode. Both of its parameters have defaults, and the first one is the column, not the recursion flag:

function CheckedNodes(AColumn: Integer = 0; ARecurse: Boolean = True): TTMSFNCTreeViewCheckedNodes;

So CheckedNodes(0, True) walks the whole tree for column 0. Passing a single argument sets the column, which is the easiest call to get wrong.

Parents, children, and partial state

A checked tree carries a question a flat list never has: what does a checked parent mean when only some of its children are checked? The control stores one state per node and does not cascade for you, so decide the policy in your own code — either check the children when a parent is checked, or treat a parent as a label whose own state is ignored and read only the leaves. Doing neither is what produces a "selection" the user did not intend.

Common mistakes

  • No check boxes on run-time nodes. CheckTypes[0] is set to tvntCheckBox automatically only at design time. Nodes you add in code render without a check box until you set it yourself.
  • Passing the recursion flag as the first argument. CheckedNodes(True) sets the column, not the recursion. Use CheckedNodes(0, True).
  • Expecting parent checks to cascade. Each node's state is independent; cascade it yourself if that is the behaviour you want.
  • Collecting with ARecurse = False and wondering where the children went. That call returns only the top level.

See also