How do I avoid invalid-index errors when removing user-selected items from a TreeView in a loop?

廉价感情. 提交于 2019-12-12 06:56:25

问题


I have a treeview and a listview, and a button that should move selected items from the treeview to the listview. The selection and the transfer are happening perfectly fine, but deleting the items from the treeview is not. It only works if I select a single item under the treeview. When I have TreeView1.MultiSelect := True, then there is a problem.

Here is the code I am using:

For i := 0 to TreeView1.Items.Count-1 do Begin
 If TreeView1.Items[i].Selected then
 Begin
 Itm := ListView1.Items.add;
 .....
 TreeView1.Items[i].Delete

The above code gives an invalid index after certain selection. Not perfect sometimes only one out of two selected are added.

Tried:

  1. For i := TreeView1.Items.Count to 1 do Begin
  2. Populated listview first and then tried to delete from treeview, instead of simulatainously doing it. Same error.
  3. Tried to store Parent and Child in an array and then later delete them with the name. Problem is particular item not selected in treeview.

This is the code which is not working when I select the last element all of the elements in the parent node get copied

for Itr := TreeView1.Items.Count-1 downto 0 do Begin
  if TreeView1.Items[Itr].Selected then
  begin
    Str := TreeView1.Items[Itr].Parent.Text + ' ,' + TreeView1.Items[Itr].Text;
    TrimLeft(Str);
    for k := 0 to SaveList.Count -1 do Begin
      If ansipos(Str, SaveList[k]) > 0 Then Begin
        Value := StringReplace(SaveList[k], Str, '',[rfReplaceAll, rfIgnoreCase]);
      End;
    End;
    Itm := ListView1.Items.Add;
    Itm.Caption := TreeView1.Items[Itr].Parent.Text;
    Itm.SubItems.Add(TreeView1.Items[Itr].Text);
    Itm.SubItems.Add(Value);
    TreeView1.Items[Itr].Delete
  end;
End;

回答1:


Change your for loop as:

For i := TreeView1.Items.Count-1 downto 0 do 
....

Edit after new code presented

You did not show the actual error message, nor on which line it happens, but I assume it is on this line

Str := TreeView1.Items[Itr].Parent.Text + ' ,' + TreeView1.Items[Itr].Text;

for a TreeView item at the root level. Those don't have a parent (IOW Item[itr].Parent is nil) and the result is an Access violation error. You need to check that the item has a parent before attempting to access the parent.

If your error is something else, please clarify.


Edit, added workaround

As @MBo reported, the reason is that the selection changes during deletion. To prevent this, you can use the following workaround.

Declare a boolean field, say, TreeView1_Deleting in your form and an OnChanging event for the tree view.

type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    Button1: TButton;
    ...
    procedure TreeView1Changing(Sender: TObject; Node: TTreeNode;
      var AllowChange: Boolean);
  private
    TreeView1_Deleting: boolean;
  end;

and the implementation of the OnChangingevent:

procedure TForm1.TreeView1Changing(Sender: TObject; Node: TTreeNode;
  var AllowChange: Boolean);
begin
  if TreeView1_Deleting then
    AllowChange := False;
end;

Finally, in the procedure where you delete the selected nodes

begin
  TreeView1_Deleting := True; // Add this line

  for i := TreeView1.Items.Count-1 downto 0 do
  begin
    if TreeView1.Items[i].Selected then
    begin
      // copy values to listview
      // and finally delete the node
      TreeView1.Items[i].Delete;
    end;
  end

  TreeView1_Deleting := False; // Add this line
end;

Remember what I said earlier regarding accessing the Parent property of a root level node.




回答2:


I can confirm that this code really clears the whole branch or all the tree (if the last element is chosen) (XE3, Win7/32):

  for I := TreeView1.Items.Count - 1 downto 0 do begin 
    if TreeView1.Items[i].Selected then begin
      Memo1.Lines.Add(TreeView1.Items[i].Text);
      TreeView1.Items[i].Delete;
    end;
  end;

And it outputs both selected node label and parent labels up to root level - so selection jumps level up during deletion (I don't see a reason in treeview sources)

Workaround: mark selected nodes, remove selection, delete marked:

var
  i: Integer;
  Node: TTreeNode;
begin
  for I := TreeView1.SelectionCount - 1 downto 0 do begin
      Node := TreeView1.Selections[i];
      Memo1.Lines.Add(Node.Text);
      Node.Data := Pointer(-1);
  end;
  TreeView1.Selected := nil;
  for I := TreeView1.Items.Count - 1 downto 0 do
     if TreeView1.Items[i].Data = Pointer(-1) then
        TreeView1.Items[i].Delete;


来源:https://stackoverflow.com/questions/36725780/how-do-i-avoid-invalid-index-errors-when-removing-user-selected-items-from-a-tre

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!