Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to delete a node and its object from a TTreeView

I am using Delphi 10 Starter edition. I am studying how to use Delphi's TTreeView component.

Everything works, no issues. The only question I have left is:

After adding an object to a node, what is the right way to delete the item and its object?

Here is my code (see Button6Click):

type
  TMyNode2 = class
  public
    nom: Integer;
    comm: String;
  end;

var
  Form1: TForm1;
  MyNode2: TMyNode2;

procedure TForm1.Button1Click(Sender: TObject); // add node
var
  x: TTreeNode;
begin
  x := tv1.Items.Add(tv1.Selected, Edit1.Text + ' ' + IntToStr(Round(Random(100))));
  x.ImageIndex := 1;
  x.SelectedIndex := x.ImageIndex;
  x.StateIndex := x.ImageIndex;
end;

procedure TForm1.Button4Click(Sender: TObject); // add child node
var
  x: TTreeNode;
begin
  x := tv1.Items.AddChild(tv1.Selected, 'Child ' + Edit1.Text + ' ' + IntToStr(Round(Random(100))));
  x.ImageIndex := 2;
  x.SelectedIndex := x.ImageIndex;
  x.StateIndex := x.ImageIndex;
  tv1.Selected.Expand(true);
  x.Selected := true;
  x.Expand(true);
end;

procedure TForm1.Button2Click(Sender: TObject); // del node (if not 1st)
var
  x, y: TTreeNode;
begin
  y := tv1.Items[0];
  x := tv1.Selected;
  if x <> y then
    x.Delete
  else
    ShowMessage('Do not del 1st node!');
end;

procedure TForm1.Button3Click(Sender: TObject); // add node (v.2 as object)
var
  x: TTreeNode;
begin
  MyNode2 := TMyNode2.Create;
  MyNode2.nom := Round(Random(1000));
  MyNode2.comm := IntToStr(MyNode2.nom) + ' comment ' + IntToStr(MyNode2.nom);
  x := tv1.Items.AddObject(tv1.Selected, Edit1.Text + ' ' + IntToStr(Round(Random(1000))), Pointer(MyNode2)); 
  x.ImageIndex := 3;
  x.SelectedIndex := x.ImageIndex;
  x.StateIndex := x.ImageIndex;
  tv1.Selected.Expand(true);
  x.Selected := true;
  x.Expand(true);
end;

procedure TForm1.Button5Click(Sender: TObject); // add child node (v.2 as object)
var
  x: TTreeNode;
begin
  MyNode2 := TMyNode2.Create;
  MyNode2.nom := Round(Random(1000));
  MyNode2.comm := IntToStr(MyNode2.nom) + ' child comment ' + IntToStr(MyNode2.nom);
  x := tv1.Items.AddChildObject(tv1.Selected, Edit1.Text + ' ' + IntToStr(Round(Random(1000))), Pointer(MyNode2));
  x.ImageIndex := 4;
  x.SelectedIndex := x.ImageIndex;
  x.StateIndex := x.ImageIndex;
  tv1.Selected.Expand(true);
  x.Selected := true;
  x.Expand(true);
end;

procedure TForm1.Button6Click(Sender: TObject); // del node (if not 1st) and "as" object
var
  x, y: TTreeNode;
begin
  y := tv1.Items[0];
  x := tv1.Selected;
  if x <> y then begin
    //class(x.tMyNode2(node.Data)).Free; // **** // !!!!!!!!!!!!!!!
    x.Delete;
  end else
    ShowMessage('Do not del 1st node!');
end;

procedure TForm1.FormCreate(Sender: TObject); // on create - add data 2 1st node
var
  x: TTreeNode;
begin
  MyNode2 := TMyNode2.Create;
  MyNode2.nom := 100001;
  MyNode2.comm := '1st node comment';
  x := tv1.Items[0];
  x.Data := Pointer(MyNode2);
end;

procedure TForm1.tv1Change(Sender: TObject; Node: TTreeNode); // look node prop.
begin
  if Node = nil then
    Caption:='Not select'
  else
  if Node.Data = nil then
    Caption := Format('Nom: %d, Im.Ind: %d', [Integer(Node.Index), Integer(Node.ImageIndex)])
  else
    Caption := Format('Nom: %d, Im.Ind: %d, MyNode.Nom: %d, Comm: %s', [Integer(Node.Index), Integer(Node.ImageIndex), Integer(TMyNode2(node.Data).nom), string(TMyNode2(node.Data).comm)]);
end;
like image 658
Gu. Avatar asked Feb 05 '23 14:02

Gu.


1 Answers

Use the TreeView's OnDeletion event, which is triggered when a TTreeNode is being removed from the tree:

procedure TForm1.tv1Deletion(Sender: TObject; Node: TTreeNode);
begin
  TMyNode2(Node.Data).Free;
  // or simply:
  // TObject(Node.Data).Free;
end;

Thus:

procedure TForm1.Button2Click(Sender: TObject);
// del node (if not 1st)
var
  x, y: TTreeNode;
begin
  x := tv1.Selected;
  if x <> nil then
  begin
    y := tv1.Items.GetFirstNode;
    if x <> y then
      x.Delete
    else
      ShowMessage('Do not del 1st node!');
  end;
end;

procedure TForm1.Button6Click(Sender: TObject);
// del node (if not 1st) and its object
var
  x, y: TTreeNode;
begin
  x := tv1.Selected;
  if x <> nil then
  begin
    y := tv1.Items.GetFirstNode;
    if x <> y then
      x.Delete
    else
      ShowMessage('Do not del 1st node!');
  end;
end;

As you can see, Button2Click() and Button6Click() are now identical, so you can remove one of them. Unless you really want Button2Click() to not free the node's object, in which case you can just set x.Data to nil before calling x.Delete() (you are still responsible for keeping track of the object somewhere so you can free it later, or else it will be leaked):

procedure TForm1.Button2Click(Sender: TObject);
// del node (if not 1st) but not its object
var
  x, y: TTreeNode;
begin
  x := tv1.Selected;
  if x <> nil then
  begin
    y := tv1.Items.GetFirstNode;
    if x <> y then
    begin
      x.Data := nil; // <-- !!!
      x.Delete;
    end else
      ShowMessage('Do not del 1st node!');
  end;
end;

procedure TForm1.Button6Click(Sender: TObject);
// del node (if not 1st) and its object
var
  x, y: TTreeNode;
begin
  x := tv1.Selected;
  if x <> nil then
  begin
    y := tv1.Items.GetFirstNode;
    if x <> y then
      x.Delete
    else
      ShowMessage('Do not del 1st node!');
  end;
end;
like image 78
Remy Lebeau Avatar answered Feb 16 '23 22:02

Remy Lebeau