delphitreeviewfiremonkey

How to delete all children from a TTreeViewItem?


Is there a way to delete all children from a TTreeViewItem? I tried DeleteChildren but that causes crashes.

What I thought what was a simple question turns out to generate many more questions. That's why I explain of what I am trying to do.

My application tries to generate a directory tree in Delphi XE5 FMX. I use TTreeView for that. It starts by generating a list of drives, all of them TTreeViewItem's owned by TTreeView. When the user clicks on an item the directories below are added to the directory and the TTreeViewItem clicked upon expands. When the user clicks again the TTreeViewItem callapses. This has one caveat: the next time the user clicks on the same TTreeViewItem, the list of directories are added to the existing ones, see image below. In order to prevent that I would like to first clear the current list.

A list of duplicated directories

When I tried to delete the children using TreeViewItem.DeleteChildren from a TTreeViewItem I get an exception at another spot, see the picture below.

Error message

As to some questions: yes, I am sure I only add TTreeViewItems and this is the only Control I assign the OnClick event (import_directory_click). I have added the complete code and commented out the non-essentials to be sure.

I hope somebody tells me this functionality already exists (couldn't find it) but even then I would still like to know how to manage a TTreeView.

  procedure TMain.import_initialize;
  var
     Item: TTreeViewItem;
     drive: Char;
     start: string;
  begin
     Directory_Tree.Clear;

  {$IFDEF MSWINDOWS}
  // When Windows, just present a list of all existing drives
     for drive := 'C' to 'Z' do
     begin

  // A drive exists when its root directory exists
        start := drive + ':\';
        if TDirectory.Exists (start) then import_add (start, Directory_Tree);
     end; // for
  {$ELSE}
  // All other systems are unix systems, start with root.

     drive := '/';
     start:= drive;
     Item := import_add (TPath.GetPathRoot (start), DirectoryTree);
     import_get_dirs (Item, start);
  {$ENDIF}
     start := TPath.GetSharedPicturesPath;
     import_add (start, Directory_Tree);
     if start <> TPath.GetPicturesPath
        then import_add (TPath.GetPicturesPath, Directory_Tree);
  //   import_test_selection ('');
  end; // import_initialize //

  procedure TMain.import_directory_click (Sender: TObject);
  var
     TreeItem: TTreeViewItem;
     obj: TFMXObject;
     first_file: string;
  begin
     GridPanelLayout.Enabled := False;
     if Sender <> nil then
     begin
        TreeItem := Sender as TTreeViewItem;
        if TreeItem.IsExpanded then
        begin
           TreeItem.CollapseAll;
        end else
        begin
           TreeItem.DeleteChildren; // <== this statement
           import_get_dirs (TreeItem, TreeItem.Text);
  {
           first_file := find_first (TreeItem.Text, Selected_Images);
           if first_file <> '' then
           begin
              Image.Bitmap.LoadFromFile (first_file);
              GridPanelLayout.Enabled := True;
           end; // if
  }
           TreeItem.Expand; // <== causes an exception over here
        end; // if
     end; // if
  end; // import_directory_click //

  procedure TMain.import_get_dirs (Start_Item: TTreeViewItem; start: string);
  var
     DirArray: TStringDynArray;
     DirArraySize: Int32;
     i: Int32;
  begin
     DirArray := TDirectory.GetDirectories (start);
     DirArraySize := Length (DirArray);
     for i := 0 to DirArraySize - 1
        do import_add (DirArray [i], Start_Item);
  end; // get_dirs //

  function TMain.import_add (dir: string; owner: TControl): TTreeViewItem;
  var
     TreeItem: TTreeViewItem;
  begin
     TreeItem := TTreeViewItem.Create (owner);
     TreeItem.text := dir;
     TreeItem.OnClick := import_directory_click;
  //   TreeItem.Parent := owner;
     owner.AddObject (TreeItem);
     Result := TreeItem;
  end; // import_add //

Solution

  • It seems that TreeItem.DeleteChildren deletes the item content site instead of the subitems. I suggest to use this:

    for i := TreeItem.Count - 1 downto 0 do
        TreeItem.RemoveObject(TreeItem.Items[i]);