TListBox中的错误数据移动到TStringGrid

3

我正在使用 Delphi 7 进行开发。我有一个 TListBox 和一个 TStringGrid,其中 TStringGrid 有两列(没有固定行或列)。我在 TListBox 中有以下数据:

可用元素 - a123(a123) 可用元素 - a1234(a1234) 可用元素 - a12345(a12345)

TStringGrid 中具有以下数据:

列1 列2

a1 可用元素 - a1 a2 可用元素 - a12

如果我选择 TListbox 中的第一项,即 a123,并执行以下按钮单击事件过程,则最后一项数据即 a12345 将移动到网格中。

请问下面的代码有什么问题。以下代码将所选 TListbox 中的项移动到 TStringgird 的两个列中:

procedure TForm1.btnMoveLeftClick(Sender: TObject);
var
  sString : String;
  i : Integer;
begin
  for i := 0 to ListBox1.Items.Count - 1 do
  begin
      {-- Is this status selected? --}
      if ListBox1.Selected[i] then
      begin
        sString := Trim(ListBox1.Items[i]);

        {-- Delete selected status. --}
          ListBox1.Items.Delete (i);

        if ((grdVFormDetails.RowCount >= 1) And (Trim(grdVFormDetails.Cells[0, 0]) <> EmptyStr)) then
          grdVFormDetails.RowCount := grdVFormDetails.RowCount+1;

        grdVFormDetails.Cols[1].Add(Copy(sString, 1, Pos('(', sString) - 1));

        sString := Copy(sString, Pos('(', sString) + 1, Length(sString));
        sString := Copy(sString, Pos('(', sString) + 1, Length(sString) - 1);

        grdVFormDetails.Cols[0].Add(sString);


        break;
      end;
  end;
end;

列表框被定义为多选吗? - No'am Newman
不,列表框没有定义为多选。 - Vishal Tiwari
2
如果你在删除时打破循环,为什么还要使用for循环呢?如果多选关闭,你可以简单地使用ListBox1.Items[ListBox1.ItemIndex]。你在循环中还做了什么没有展示出来? - Marjan Venema
现在选择列表框中的第一项,然后在“<”按钮的单击事件中设置断点,然后按“<”按钮。然后逐行执行代码,您会看到正确的数据被添加到网格中,现在按“F9”键。在这里,您会看到列表框中的最后一项移动到了网格中,并且列表框中也存在相同的内容。但是列表框中不会有第一项。但是,如果您查看网格下方的两个编辑框,这些编辑框具有正确的值。这意味着代码没有错误。我认为问题出在TStringGrid上。 - Vishal Tiwari
1
有几个地方需要改进。为什么不使用两个变量,比如ItemID,它将保存括号内的值,而ItemText将保存输入字符串的其余部分。然后,您可以简单地使用Cells[]来赋值。还有,你应该从后往前解析这个输入字符串,如果有一个字符串像 Text(comment)-(a12345)。如果按照您当前的方式进行解析,您将提取出comment而不是a12345 - TLama
显示剩余4条评论
4个回答

2

永远不要在FOR循环中删除TList的项。

这行代码存在问题:

  ListBox1.Items.Delete (i);

循环从 i:=0 到 2。选择项目-0并将其删除。下一次重复我们得到什么?i=1,但这里只剩下2个项目而不是3个(所有后续项目都被移动),i指向最后一个项目而不是第二个。在下一次重复中,当i=3时,我们将获得“索引超出范围”的错误。为避免此问题,您应该在FOR循环之后才删除Item。
procedure TForm1.btnMoveLeftClick(Sender: TObject);
var
  sString : String;
  i : Integer;
  k: integer;
begin
  k:=-1; 
  for i := 0 to ListBox1.Items.Count - 1 do
  begin
      {-- Is this status selected? --}
      if ListBox1.Selected[i] then
      begin
        sString := Trim(ListBox1.Items[i]);

        {-- Delete selected status. --}
          k:=i;         

        if ((grdVFormDetails.RowCount >= 1) And (Trim(grdVFormDetails.Cells[0, 0]) <> EmptyStr)) then
          grdVFormDetails.RowCount := grdVFormDetails.RowCount+1;

        grdVFormDetails.Cols[1].Add(Copy(sString, 1, Pos('(', sString) - 1));

        sString := Copy(sString, Pos('(', sString) + 1, Length(sString));
        sString := Copy(sString, Pos('(', sString) + 1, Length(sString) - 1);

        grdVFormDetails.Cols[0].Add(sString);


        break;
      end;
  end;
  if k>=0 then  ListBox1.Items.Delete (k);

end;

3
我同意你的观点,但我在删除 TListBox 中的项目时已经加入了 "break" 语句。 - Vishal Tiwari
我已经用你的处理程序做了一个测试D7项目,它运行良好。所以请检查所有TListBox事件处理程序。我觉得在你选择一个项目和点击按钮之间发生了一些事情。或者是之后? - valex
我的示例项目中,我的 TListBox 和 TStringGrid 组件都没有事件处理程序代码。 - Vishal Tiwari
3
@valex 在你的注释中提到,“永远不要在 FOR 循环中删除 TList 的项”,为什么?可以使用 downto 循环,从底部向顶部删除项目。 - TLama
@TLama,同意。但如果你想指向下一个项目(i+1),请小心。 - valex

1
假设您想要解析输入字符串,如下所示:

'Some text (comment) etc. (12345)'

将输入字符串从开头到第一个左括号(从末尾开始的第一个左括号)截取为一个部分,以获得类似这样的值:
'Some text (comment) etc.'

从输入字符串的最后一对括号内提取出字符串:

'12345'

如果是这样,您可以使用以下代码。请注意,预期将通过关闭括号终止列表框项目。如果需要,您可以查看此代码的{{link1:commented version}}或下载{{link2:示例项目}}。
以下是将焦点项从列表框移动到字符串网格的部分:
procedure TForm1.MoveLeftButtonClick(Sender: TObject);
var
  S: string;
  I: Integer;
  ItemID: string;
  ItemText: string;
begin
  if ListBox1.ItemIndex = -1 then
    Exit;

  S := ListBox1.Items[ListBox1.ItemIndex];
  for I := Length(S) - 1 downto 1 do
  begin
    if S[I] = '(' then
    begin
      ItemID := Trim(Copy(S, I + 1, Length(S) - I - 1));
      ItemText := Trim(Copy(S, 1, I - 1));
      with StringGrid1 do
      begin
        if (Cells[0, RowCount - 1] <> '') and
          (Cells[1, RowCount - 1] <> '')
        then
          RowCount := RowCount + 1;
        Cells[0, RowCount - 1] := ItemID;
        Cells[1, RowCount - 1] := ItemText;
      end;
      ListBox1.Items.Delete(ListBox1.ItemIndex);
      Break;
    end;
  end;
end;

这里是将选定行从字符串网格移动到列表框的部分:移动

procedure TForm1.MoveRightButtonClick(Sender: TObject);
var
  I: Integer;
  RowIndex: Integer;
begin
  RowIndex := StringGrid1.Selection.Top;
  if (StringGrid1.Cells[0, RowIndex] <> '') and
    (StringGrid1.Cells[1, RowIndex] <> '') then
  begin
    ListBox1.Items.Add(
      Trim(StringGrid1.Cells[1, RowIndex]) + ' (' +
      Trim(StringGrid1.Cells[0, RowIndex]) + ')'
    );
    for I := RowIndex to StringGrid1.RowCount - 2 do
      StringGrid1.Rows[I].Assign(StringGrid1.Rows[I + 1]);
    if StringGrid1.RowCount > 1 then
      StringGrid1.RowCount := StringGrid1.RowCount - 1
    else
    begin
      StringGrid1.Cells[0, 0] := '';
      StringGrid1.Cells[1, 0] := '';
    end;
  end;
end;

非常感谢TLama和大家的帮助。我非常感激你们的帮助,特别是TLama和Jachguate。因此我想以同样的方式感谢每一个人。Jachguate,很抱歉我很晚才加入了整个代码,我认为我的事件过程代码会有问题,所以我只想把重点放在那里,避免不必要的领域,但随着讨论的深入,我发现包含整个源代码的重要性。@TLama,我发现你的代码真的很棒:),但你能告诉我将项目从ListBox移动到Grid的代码中有什么问题吗? - Vishal Tiwari
不用客气!关于你在上一条评论中提到的代码问题,它现在还存在吗? - TLama

0

如果ListBox没有多选功能,最好使用ItemIndex属性,它可以给你选中项目的索引,这样你就不必循环检查列表中的所有项目。

像这样:

procedure TForm1.btnMoveLeftClick(Sender: TObject);
var
  sString : String;
  i : Integer;
begin
  if ListBox1.ItemIndex <> -1 then
  begin
    sString := Trim(ListBox1.Items[ListBox1.ItemIndex]);
    //do all your processing here
    //and at the end:
    ListBox1.Items.Delete(ListBox1.ItemIndex);
  end
end;

我按照您的代码尝试了一下,但问题仍然存在。 - Vishal Tiwari
就像我之前所说的,如果我在程序中设置断点并查看“sString”变量中收集到的值,那么这些值是正确的,并且正确的值将填充到网格中。问题在于当我退出这个过程时,网格会显示TListBox中最后一项相关数据。这很奇怪,但确实如此。 - Vishal Tiwari
控制流程进入"Forms.pas"文件中的"procedure TApplication.Run;",并且在repeat..until循环中一直执行,直到我按下"F9"键才会退出。在此之前,没有任何线索表明出了什么问题。 - Vishal Tiwari
@Vishal 如果我的示例出了问题,请归咎于你的 Delphi 编译器。(它是否已经应用了所有可用的更新?) - jachguate
这是pas文件的链接:http://pastebin.com/rgwrk2Rt,这是dfm文件的链接:http://pastebin.com/36YsuDq3。 - jachguate
显示剩余11条评论

0

我目前没有安装Delphi,但据我所记,如果使用ListBox1.Selected[i],将无法给出正确的结果。您需要首先获取该项,然后检查该项是否被选中。


如果我设置断点并查看收集在“sString”变量中的值,我发现它是正确的。此外,当这些值插入到网格中时,它们也是正确的,但是当我退出此过程时,我发现TListBox中的最后一项数据已添加到TStringGrid中,并且最后一项数据仍然存在于TListBox中。这是观察结果。 - Vishal Tiwari

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接