Sign in Join
Wiki copytemporary

Revision 3 of 3, by WikiArchive on 11-10-2026. The page as it is now.

TTreeView

A TTreeView ttreeview.png is a graphical control element that presents a hierarchical view of information. Each item can have a number of subitems.

An item can be expanded to reveal subitems, if any exist, and collapsed to hide subitems.

A Short example of using TTreeview

Here is a simple example:

pascal
procedure TForm1.Button1Click(Sender: TObject);
    var 
      n: integer;
      s: string;
    begin
      // If there are no nodes, create a root node with a parent of nil
      if TreeView1.Items.Count = 0 then
      begin
        Treeview1.Items.Add(nil,'Root Node');
        exit;
      end;
     
      // Set up a simple text for each new node - "Node1", "Node2" etc
      n := Treeview1.Items.Count;
      s := 'Node ' + IntToStr(n);
    
      // Add a new node to the currently selected node
      if TreeView1.Selected <> nil then
        Treeview1.Items.AddChild(Treeview1.Selected, s);
    end;
     
    procedure TForm1.Button2Click(Sender: TObject);
    
      // A procedure to recursively delete nodes
      procedure DeleteNode(ANode: TTreeNode);
      begin
        while ANode.HasChildren do 
          DeleteNode(ANode.GetLastChild);
        TreeView1.Items.Delete(ANode);
      end;
     
    begin
      if TreeView1.Selected = nil then 
        exit;
      
      // If the selected node has child nodes, first ask for confirmation
      if Treeview1.Selected.HasChildren then
        if MessageDlg('Delete node and all children?', mtConfirmation, [mbYes,mbNo], 0 ) <> mrYes then 
          exit;
      DeleteNode(TreeView1.Selected);
    end;

When running, the treeview is empty. If you click "Add Child", a root node is created. After that a child will be added to any selected node by clicking "Add Child" again.

The "Delete" button will delete the currently selected node. If it doesn't have children, it will delete it immediately, but if it has children, it will first ask.

Adding a new item in code

To add a sibling node to pre-existing node, use TTreeView.ITems.Add.

pascal
TreeView1.Items.Add(ATreeNode, 'Added node');

To add the first node, it becomes:

pascal
TreeView1.Items.Add(nil, 'First node');

Use TTreeView.Items.AddChild or AddChildObject to add child node.

pascal
TreeView1.Items.AddChild(ATreeNode, 'Child node');

Creating a TreeView which loads items only when expanding

To add the expansion symbol to a node without subitems use:

pascal
MyNode.HasChildren := True;

And then set an event handler for the OnExpanding event. In this even you should return if the expansion can actually be made or not and when yes, you should add subitems to the node. If the expansion cannot be done, the expansion symbol will be automatically removed even if you have previously set HasChildren to true.

How to move a node in TTreeview

Assuming you have UP and Down buttons to move the selected node in the TTreeview, these procedures will achieve moving the selected node.

pascal
procedure TForm1.UpBtnClick(Sender: TObject);
    begin
      // Ensure a node is selected 
      if(Treeview1.Selected <> nil) then
        // Ensure there is a previous sibling node
        if Treeview1.Selected.GetPrevSibling <> nil then
          // If we have made it this far, move it UP
          Treeview1.Selected.MoveTo(Treeview1.Selected.GetPrevSibling, naInsert);
    end;
    
    procedure TForm1.DownBtnClick(Sender: TObject);
    begin
      // Ensure a node is selected 
      if(Treeview1.Selected <> nil) then
        // Ensure there is a next sibling node
        if Treeview1.Selected.GetNextSibling <> nil then
          // If we have made it this far, move it DOWN
          Treeview1.Selected.MoveTo(Treeview1.Selected.GetNextSibling, naInsertBehind);
    end;

Note: These procedures will only move nodes which are at the same level in the TTreeview.

Table of attachment mode of Nodes

Mode | Effect
naAdd | add as last sibling of target node
naAddFirst | add as first sibling of target node
naAddChild | add as last child of target node
naAddChildFirst | add as first child of target node
naInsert | insert in front of target node
naInsertBehind | insert behind target node

Using multiple node selection

To allow multiple selection, you need to set the tvoAllowMultiSelect flag of the Options property. This will allow you to select additional nodes by Ctrl+Mouse click.

To get a list of selected nodes from code, you can use the TreeView1.Selections property:

pascal
procedure TForm1.Button1Click(Sender: TObject);
    var
      i: integer;
    begin
      Memo1.Lines.Clear;
      for i := 0 to TreeView1.SelectionCount - 1 do
        Memo1.Lines.Add(TreeView1.Selections[i].GetTextPath);
    end;

Freeing TreeNode Data

Use the TreeView's OnDeletion event to free your object.

pascal
procedure TForm1.TreeView1Deletion(Sender: TObject; Node: TTreeNode);
    begin
      TMyObject(Node.Data).Free;
      Node.Data := nil;
    end;

Using Drag and Drop in a TreeView

If you want to allow a drag and drop function in your treeview, you need to :

  1. Set to DmAutomatic, the property "Drag Mode" of your treeview
  2. Create events for onDragDrop and OnDragOver:
pascal
...
    
    Var
      Node: TTreeNode;
    
    ...
    
    procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
    begin
      Treeview1 := TTreeView(Sender);    { Sender is TreeView where the data is being dropped  }
      Node := Treeview1.GetNodeAt(x,y);  { x,y are drop coordinates (relative to the Sender)   }
                                         {   since Sender is TreeView we can evaluate          }
                                         {   a tree at the X,Y coordinates                     }
      if Source = Sender then            { drop is happening within a TreeView   }
        begin
          if Assigned(Treeview1.Selected) and     {  check if any node has been selected  }
            (Node <> Treeview1.Selected) then     {   and we're dropping to another node  }
              begin
                if Node <> nil then
                  Treeview1.Selected.MoveTo(Node, naAddChild) { complete drop, by moving selected node }
                else
                  Treeview1.Selected.MoveTo(Node, naAdd);     { complete drop, by moving selected node in root }
              end;
        end;
    end;
    
    procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
    begin
      Accept := true;
    end;

Comments on Events

Event handlers are convenient places for developing LCL applications. There are some peculiar events for TTreeview.

When selected node is changed

pascal
procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);

The OnChange event is fired when another node is selected. It can be used in a program to update some data.

pascal
procedure TForm1.TreeView1Changing(Sender: TObject; Node: TTreeNode; var AllowChange: Boolean);

The OnChanging event is called before OnChange, which allows you to prevent node selection under certain conditions. To do this, just set AllowChange to false. It is not necessary to set to true as this is the default value.

Please note that in Delphi the Node variable points to the node that leaves the selection. In Lazarus, the Node variable points to the node receiving the selection, but you can use the Selected property to find out the "previous" node.

In both events, the Node and Selected can be equal to nil (if there was no selection or it is being removed).

When a node is inserted

When a new node is inserted, related events occur in the following order.

pascal
1) OnNodeChanged
          2) (OnChanging): This is called if there was a previously selected node.
          3) OnSelectionChanged
          4) OnChange

When deleting a node

When the selected node is deleted, the following events occur.

pascal
1) OnDelete
         2) OnChanging
         3) SelectionChanged
         4) OnChange :  This is called because when you delete a node, another node is selected.

If an unselected node is deleted, e.g. programmatically, then only the OnDelete event handler is called.

When Form is Closed

OnChanging event occurs, as the selected node is "de"selected. But OnChanging eventhander is executed AFTER the form is closed. This means that you cannot access the form's other data (other controls, other variables, etc.) within the OnChanging event handler procedure.

When Drag &Drop is done

When you drag a node to another node and drop, the following events occur.

pascal
1) OnSelectionChanged
         2) OnNodeChanged
         3) OnChange

Painting of the TreeView

The treeview can draw itself in three ways:

Basically the drawing process of the treeview consists of two phases:

The "advanced" events are fired several times during the painting process. There is a Stage parameter which signals what is happening at the moment. It is an enumeration of the following elements:

They still are not used by the OnAdvancedCustomDraw event.

In contrast to cdPrePaint, setting DefaultDraw to false here only inhibits painting of the background of the current node.

The OnCustomDraw* events are equivalent to the "advanced" events in the cdPrePaint stage.

Example: Custom-painting a treeview with a background gradient

The gradient must be painted at the very beginning of the drawing cyle, i.e. we must provide a handler for the OnAdvancedCustomDraw event at the cdPrePaint stage:

pascal
procedure TForm1.BackgroundGradient_AdvancedCustomDraw(Sender: TCustomTreeView;
      const ARect: TRect; Stage: TCustomDrawStage; var DefaultDraw: Boolean);
    begin
      if Stage = cdPrePaint then
      begin
        // Draw the gradient (sky-blue at the top, to white at the bottom)
        Sender.Canvas.GradientFill(ARect, clSkyBlue, clWhite, gdVertical);
        // Avoid painting the normal background below the last node
        Sender.Canvas.Brush.Color := clNone;
      end;
      // We must not set DefaultDraw to false here because the CustomDraw
      // PrePaint stage would force us to paint everything by ourselves.
    end;

However, that's not all because when the default painting routine of the node background is executed the gradient is overwritten by the node rectangle over the entire tree width. Therefore we must inhibit drawing of the normal background in OnAdvancedCustomDrawItem in the cdPreErase stage:

pascal
procedure TMainForm.BackgroundGradient_AdvancedCustomDrawItem(Sender: TCustomTreeView;
      Node: TTreeNode; State: TCustomDrawState; Stage: TCustomDrawStage;
      var PaintImages, DefaultDraw: Boolean);
    begin
      if Stage = cdPreErase then
        // Avoid overwriting the gradient with the node background color
        DefaultDraw := false;
    end;

When tvoThemedDraw is contained in the treeview's Options, this draws a themed treeview, but with gradient background. If you want to custom-draw also the selected and hot-tracked nodes you must remove tovThemedDraw from the Options and also set the corresponding canvas properties in the cdPostErase stage:

pascal
procedure TMainForm.BackgroundGradient_AdvancedCustomDrawItem(Sender: TCustomTreeView;
      Node: TTreeNode; State: TCustomDrawState; Stage: TCustomDrawStage;
      var PaintImages, DefaultDraw: Boolean);
    begin
      case Stage of
        cdPreErase:
          // Avoid overwriting the gradient with the node background color
          DefaultDraw := false;
        cdPostErase:
          // Set selected and hot-track properties as usual
          if ([cdsFocused, cdsSelected] * State <> []) then   // selected node
          begin
            Sender.Canvas.Brush.Color := clNavy;
            Sender.Canvas.Font.Color := clYellow;
            Sender.Canvas.Font.Style := [fsBold];
          end else
          if (cdsHot in State) then                           // hot-tracked node
          begin
            Sender.Canvas.Brush.Color := clGray;
            Sender.Canvas.Font.Color := clHighlightText;
            Sender.Canvas.Font.Style := [];
          end else                                            // normal node
            // Setting Brush.Color to clNone inhibits drawing the rectangle for the normal nodes.
            // Don't use Sender.Canvas.BrushStyle = bsClear because it will be reset by changing the brush color.
            Sender.Canvas.Brush.Color := clNone;
      end;
    end;

All this looks rather complicated... Yes, it is. Therefore, you can find more worked-out examples in the demo project in the examples/treeview/customdrawing folder of the Lazarus installation.

Saving and loading a TTreeView

Using TTreeView's method SaveTFile (or SaveToStream) you can save the Items of a TTreeView.
Notice that this will save only the Text of each Item in a plain text format.
A file (or stream) created as such, can be loaded using the LoadFromFile (or LoadFromStream) method.

As of Lazarus 4.99 you can use the procedures in the TreeStorage unit to save and restore the items of a TTreeView along with most of the item's properties.
By default it will save the properties Expanded, Selected, Focused, Visible, Enabled, ImageIndex, SelectedIndex, StateIndex and tsoOverlayIndex.
You can supply a callback event to these procedures to load (and save) arbitrary data.
The example below saves the contents of a TTreeView into a stream and shows the stream in a TSynEdit with additional information using a callback event.

pascal
uses
      ..., TreeStorage, Laz2_Dom, ...
    
    function DbgS(AGraphicsDrawEffect: TGraphicsDrawEffect): String; overload;
    begin
      WriteStr(Result, AGraphicsDrawEffect);
    end;
    
    procedure TForm1.WriteCallBack(ATreeNode: TTreeNode; ADomNode: TDomNode);
    begin
      TDomElement(ADomNode).Attribute['NodeEffect'] := Dbgs(ATreeNode.NodeEffect)
    end;
    
    procedure TForm1.Button2Click(Sender: TObject);
    var
      F: TextFile;
      S: String;
      Str: TMemoryStream;
    begin
      Str := TMemoryStream.Create;
      try
        TreeStorage.TreeSaveToXML(TreeView1, Str, TreeItemStorageDefaultOptions, @WriteCallBack);
        Str.Position := 0;
        SynEdit1.Lines.LoadFromStream(Str);
      finally
        Str.Free;
      end;
    end;

See also

LCL Components Component Tab Components
Standard TMainMenu • TPopupMenu • TButton • TLabel • TEdit • TMemo • TToggleBox • TCheckBox • TRadioButton • TListBox • TComboBox • TScrollBar • TGroupBox • TRadioGroup • TCheckGroup • TPanel • TFrame • TActionList
Additional TBitBtn • TSpeedButton • TStaticText • TImage • TShape • TBevel • TPaintBox • TNotebook • TLabeledEdit • TSplitter • TTrayIcon • TControlBar • TFlowPanel • TMaskEdit • TCheckListBox • TScrollBox • TApplicationProperties • TStringGrid • TDrawGrid • TPairSplitter • TColorBox • TColorListBox • TValueListEditor
Common Controls TTrackBar • TProgressBar • TTreeView • TListView • TStatusBar • TToolBar • TCoolBar • TUpDown • TPageControl • TTabControl • THeaderControl • TImageList • TPopupNotifier • TDateTimePicker
Dialogs TOpenDialog • TSaveDialog • TSelectDirectoryDialog • TColorDialog • TFontDialog • TFindDialog • TReplaceDialog • TTaskDialog • TOpenPictureDialog • TSavePictureDialog • TCalendarDialog • TCalculatorDialog • TPrinterSetupDialog • TPrintDialog • TPageSetupDialog
Data Controls TDBNavigator • TDBText • TDBEdit • TDBMemo • TDBImage • TDBListBox • TDBLookupListBox • TDBComboBox • TDBLookupComboBox • TDBCheckBox • TDBRadioGroup • TDBCalendar • TDBGroupBox • TDBGrid • TDBDateTimePicker
Data Access TDataSource • TCSVDataSet • TSdfDataSet • TBufDataset • TFixedFormatDataSet • TDbf • TMemDataset
System TTimer • TIdleTimer • TLazComponentQueue • THTMLHelpDatabase • THTMLBrowserHelpViewer • TAsyncProcess • TProcessUTF8 • TProcess • TSimpleIPCClient • TSimpleIPCServer • TXMLConfig • TEventLog • TServiceManager • TCHMHelpDatabase • TLHelpConnector
Misc TColorButton • TSpinEdit • TFloatSpinEdit • TArrow • TCalendar • TEditButton • TFileNameEdit • TDirectoryEdit • TDateEdit • TTimeEdit • TCalcEdit • TFileListBox • TFilterComboBox • TComboBoxEx • TCheckComboBox • TButtonPanel • TShellTreeView • TShellListView • TXMLPropStorage • TINIPropStorage • TJSONPropStorage • TIDEDialogLayoutStorage • TMRUManager • TStrHolder
LazControls TCheckBoxThemed • TDividerBevel • TExtendedNotebook • TListFilterEdit • TListViewFilterEdit • TLvlGraphControl • TShortPathEdit • TSpinEditEx • TFloatSpinEditEx • TTreeFilterEdit • TExtendedTabControl •
RTTI TTIEdit • TTIComboBox • TTIButton • TTICheckBox • TTILabel • TTIGroupBox • TTIRadioGroup • TTICheckGroup • TTICheckListBox • TTIListBox • TTIMemo • TTICalendar • TTIImage • TTIFloatSpinEdit • TTISpinEdit • TTITrackBar • TTIProgressBar • TTIMaskEdit • TTIColorButton • TMultiPropertyLink • TTIPropertyGrid • TTIGrid
SQLdb TSQLQuery • TSQLTransaction • TSQLScript • TSQLConnector • TMSSQLConnection • TSybaseConnection • TPQConnection • TPQTEventMonitor • TOracleConnection • TODBCConnection • TMySQL40Connection • TMySQL41Connection • TMySQL50Connection • TMySQL51Connection • TMySQL55Connection • TMySQL56Connection • TMySQL57Connection • TSQLite3Connection • TIBConnection • TFBAdmin • TFBEventMonitor • TSQLDBLibraryLoader
Pascal Script TPSScript • TPSScriptDebugger • TPSDllPlugin • TPSImport Classes • TPSImport DateUtils • TPSImport ComObj • TPSImport DB • TPSImport Forms • TPSImport Controls • TPSImport StdCtrls • TPSCustomPlugin
SynEdit TSynEdit • TSynCompletion • TSynAutoComplete • TSynMacroRecorder • TSynExporterHTML • TSynPluginSyncroEdit • TSynPasSyn • TSynFreePascalSyn • TSynCppSyn • TSynJavaSyn • TSynPerlSyn • TSynHTMLSyn • TSynXMLSyn • TSynLFMSyn • TSynDiffSyn • TSynUNIXShellScriptSyn • TSynCssSyn • TSynPHPSyn • TSynTeXSyn • TSynSQLSyn • TSynPythonSyn • TSynVBSyn • TSynAnySyn • TSynMultiSyn • TSynBatSyn • TSynIniSyn • TSynPoSyn
Chart TChart • TListChartSource • TRandomChartSource • TUserDefinedChartSource • TCalculatedChartSource • TDbChartSource • TChartToolset • TChartAxisTransformations • TChartStyles • TChartLegendPanel • TChartNavScrollBar • TChartNavPanel • TIntervalChartSource • TDateTimeIntervalChartSource • TChartListBox • TChartExtentLink • TChartImageList
IPro TIpFileDataProvider • TIpHtmlDataProvider • TIpHttpDataProvider • TIpHtmlPanel
Virtual Controls TVirtualDrawTree • TVirtualStringTree • TVTHeaderPopupMenu

From the Free Pascal wiki, page "TTreeView", as the Internet Archive kept it on 2025-06-16, last changed on 2025-05-17. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.