从循环中的TreeView中删除用户选择的项时,如何避免无效索引错误?

时间:2016-04-19 17:46:11

标签: delphi listview treeview vcl

我有一个树视图和一个列表视图,以及一个应该将所选项目从树视图移动到列表视图的按钮。选择和传输完全正常,但删除树视图中的项目不是。它仅在我在树视图下选择单个项目时才有效。当我有TreeView1.MultiSelect := True时,就会出现问题。

以下是我正在使用的代码:

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

以上代码在某些选择后给出无效索引。不完美有时只添加两个中的一个。

尝试:

  1. For i := TreeView1.Items.Count to 1 do Begin
  2. 首先填充listview,然后尝试从treeview中删除,而不是同时执行。同样的错误。
  3. 尝试将Parent和Child存储在数组中,然后使用名称删除它们。问题是未在树视图中选择的特定项目。
  4. 当我选择父节点中的所有元素被复制的最后一个元素时,这是无效的代码

    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;
    

2 个答案:

答案 0 :(得分:5)

将for循环更改为:

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

在新代码出现后编辑

你没有显示实际的错误信息,也没有显示它发生在哪一行,但我认为它在这一行

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

表示根级别的TreeView项目。那些没有父母的人(IOW Item[itr].Parentnil),结果是Access violation错误。在尝试访问父项之前,您需要检查项目是否具有父项。

如果您的错误是其他原因,请澄清。

修改,添加了解决方法

正如@MBo报道的那样,原因是删除期间选择会发生变化。为防止这种情况,您可以使用以下解决方法。

在表单中声明一个布尔字段,例如TreeView1_Deleting,并为树视图声明OnChanging事件。

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

以及OnChanging事件的实施:

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

最后,在删除所选节点的过程中

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;

记住我之前说过的关于访问根级节点的Parent属性的内容。

答案 1 :(得分:2)

我可以确认这段代码确实清除了整个分支或所有树(如果选择了最后一个元素)(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;

它将选定的节点标签和父标签输出到根级别 - 因此选择在删除期间跳跃升级(我不会在树视图源中看到原因)

解决方法:标记所选节点,删除选择,删除标记为:

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;