Question

Surely I am overlooking something so obvious and simple here, but I just cannot see it.

I have a treeview, nodes are added to it at runtime and can also be deleted. When I delete a node, there is no longer a selected node in the tree, HideSelection is also set to False but that would make no difference anyway as the selected node is deleted, standard behavior I guess.

Anyway, to reduce the number of times and input required focusing back onto the treeview, I want to automatically reselect a node in the tree after one has been deleted (especially useful if deleting lots of nodes in quick succession as it removes the need to click back onto the treeview).

So lets say I have a tree like this:

enter image description here

This is the behavior pattern I am looking for, using the following scenarios as an example:

  • Item1 could be selected, when deleted Root becomes selected.
  • Item2 could be selected, when deleted Item6 becomes selected.
  • Item3 could be selected, when deleted Item4 becomes selected.
  • Item4 could be selected, when deleted Item5 becomes selected.
  • Item5 could be selected, when deleted Item4 becomes selected.
  • Item5 could be selected, when deleted if Item4 is not present, then Item3 should be selected.
  • Item5 could be selected, when deleted if Item3 or Item4 are not present, select Item2.
  • Item6 could be selected, when deleted Item2 becomes selected.
  • Item7 could be selected, when deleted Item6 becomes selected.

I keep getting Index out of bounds and other AV errors (in Lazarus) and that is without even getting to the point of checking where in the tree the current selected node is.

As it stands right now, in my Delete event I have this:

procedure TMainForm.actDeleteExecute(Sender: TObject);
var  
  SelNode: TTreeNode;
begin
  if TreeView1.Selected <> nil then
  begin
    SelNode := TreeView1.Selected;

    TreeView1.Selected.Delete;
    TreeView1.SetFocus;
    //ShowMessage(SelNode.GetPrev.Text);  
    TreeView1.Selected.Index := SelNode.Index; 
  end;
end;

Is this a case of me completely misunderstanding the situation again and making the task needlessly difficult, or is there a lot of work involved to implement such behavior?

Many thanks in advance.

Was it helpful?

Solution

Your mistake is to access the node, to get its index, that you've deleted: SelNode in your example. It's causing an AV.

Assuming your question is about the Lazarus's TreeView control (as mentioned in the question), you can follow Kobik's suggestion in the comments and find the node to be selected before you delete the item. Or, which I find simpler, you can implement your logic in the OnDeletion event of the TreeView. This event is fired just before the item is actually destroyed.

procedure TForm1.actDeleteExecute(Sender: TObject);
begin
  if Assigned(TreeView1.Selected) then begin
    TreeView1.Selected.Delete;
    TreeView1.SetFocus;
  end;
end;

procedure TForm1.TreeView1Deletion(Sender: TObject; Node: TTreeNode);
begin
  if Assigned(TreeView1.Selected.GetPrevSibling) then
    TreeView1.Selected := TreeView1.Selected.GetPrevSibling
  else if Assigned(TreeView1.Selected.GetNextSibling) then
    TreeView1.Selected := TreeView1.Selected.GetNextSibling
  else if Assigned(TreeView1.Selected.GetPrev) then
    TreeView1.Selected := TreeView1.Selected.GetPrev
  else
    TreeView1.Selected := TreeView1.Selected.GetNext; // can be nil
end;

Note that I was unable to follow your logic regarding which item to select when one is deleted, primarily because when a node is deleted all of its children is deleted as well. You can adjust the above to suit your needs.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top