为什么TStringList有BeginUpdate和EndUpdate方法?

15

我知道在VCL控件(例如TListBox)中使用BeginUpdate和EndUpdate可以加快将Items添加到控件的过程,因为它会阻止控件被重绘,直到调用EndUpdate。

示例:

procedure TForm1.AddItems;
var
  i: Integer;
begin
  Screen.Cursor := crHourGlass;
  try
    for i := 0 to 5000 do
    begin
      ListBox1.Items.Add('Item' + IntToStr(i));
    end;
  finally
    Screen.Cursor := crDefault;
  end;
end;

由于Listbox可以重新绘制,因此以上操作将存在延迟,但是可以通过禁止重新绘制来缩短延迟,方法如下:

procedure TForm1.AddItems;
var
  i: Integer;
begin
  Screen.Cursor := crHourGlass;
  try
    ListBox1.Items.BeginUpdate;
    try
      for i := 0 to 5000 do
      begin
        ListBox1.Items.Add('Item' + IntToStr(i));
      end;
    finally
      ListBox1.Items.EndUpdate;
    end;
  finally
    Screen.Cursor := crDefault;
  end;
end;

现在我使用 TStringList 进行了测试:

procedure TForm1.AddItems;
var
  SL: TStringList;
  i: Integer;
begin
  SL := TStringList.Create;
  try
    Screen.Cursor := crHourGlass;
    try
      SL.BeginUpdate;
      try
        for i := 0 to 5000 do
        begin
          SL.Add('Item' + IntToStr(i));
        end;
      finally
        SL.EndUpdate;
      end;

      ListBox1.Items.Assign(SL);
    finally
      Screen.Cursor := crDefault;
    end;
  finally
    SL.Free;
  end;
end;

似乎无论TStringList是否使用BeginUpdate和EndUpdate,在大约相同的速度下都会填充列表。

但是,既然TStringList是在内存中执行而不是可视化操作,那么它们真的需要吗?我应该在TStringList上使用BeginUpdate和EndUpdate吗?这样做是好的实践吗?

我觉得自己很傻,为什么TStringList有BeginUpdate和EndUpdate过程?

我想我可能已经回答了自己的问题,但无论如何,我想听听你的看法。

谢谢 :)

3个回答

24

BeginUpdate 方法可以阻止 StringList 对象触发 OnChangingOnChange 事件。这对于提升程序的运行速度有很大帮助,具体效果取决于连接的内容。

在你的例子中,使用 BeginUpdate/EndUpdate 方法并不会有太大的区别。直接将一个 TStringlist 实例分配给 ListView 是非常合理的做法。


@RRUZ,确实!感谢您的提示。 - Uwe Raabe

10

BeginUpdateEndUpdate是在抽象基类TStrings中引入的。因此,TStringList继承了这种能力,即使它不是特别有用。但是,当然对于许多其他TStrings的派生类来说是有用的。

请记住,许多其他TStrings的派生类具有私有实现。例如,与TListBox关联的TStrings对象是StdCtrls单元的实现部分的私有内容。TListBox控件将项目列表公开为TStrings,因此要使BeginUpdateEndUpdate可用,它们需要在抽象基类中声明。

在我看来,在使用您知道是TStringList的对象时,可以放心忽略这些方法。

现在,关于填充列表视图的代码,我认为完全没有必要使用中间TStringList。我会直接填充列表视图并在列表视图Items上使用BeginUpdate/EndUpdate。如果您仍然在列表视图方面遇到性能问题,则解决方案是虚拟列表视图。


1
+1,直接使用TListBox.Items.Add可能确实会稍微快一些,因为TListBox.Items.Assign对于每个条目都调用TStrings.AddObject,它在内部调用Add和PutObject。 - Uwe Raabe
@Uwe 相比将数据发送到公共控件,这将是完全微不足道的。这将是瓶颈所在。一旦您对列表视图的项目执行了BeginUpdate操作,那么所有变体将花费相同的时间。 - David Heffernan
1
但是TListBoxStrings.PutObject调用了ListBox.SetItemData,而这也是通过SendMessage实现的。因此,与每个项目的两个SendMessages相比,它是每个项目的一个SendMessage。 - Uwe Raabe
我明白了,所以BeginUpdate和EndUpdate之所以存在是因为它继承自父类TStrings - 这让我很想笑,因为我刚刚通过在SL.BeginUpdate上按Ctrl +单击找到了这一点。Classes.pas显示了TStrings.BeginUpdate过程,其中TStringList继承自此过程。我想知道为什么StringList有这个属性,尽管它是一个非可视控件。 - user1175743
正如Uwe所说,它在TStringList中用于修改更改通知事件的行为,但我认为它存在的主要原因是为了赋予抽象基类TStrings更强大的功能。 - David Heffernan

2

这只是一个实现锁定模式的例子,如此处所述。

它允许您暂时锁定类的某个方面,避免不必要的通知。

DB.TDataSet.DisableControlsDB.TDataSet.EnableControls中发现的相同。


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