【问题标题】:Why does TStringList have BeginUpdate and EndUpdate?为什么 TStringList 有 BeginUpdate 和 EndUpdate?
【发布时间】:2012-02-05 16:24:04
【问题描述】:

我了解在 TListBox 等 VCL 控件上使用 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 是否使用 BegindUpdate 和 EndUpdate,列表的填充速度大致相同。

虽然 TStringList 是在内存中执行而不是在视觉上执行,但它们是否真的需要。我是否应该在 TStringList 上使用 BeginUpdate 和 EndUpdate,这样做是个好习惯吗?

我问这个感觉很傻,但是为什么TStringList有BeginUpdate和EndUpdate过程?

我想我可能已经在这里回答了我自己的问题,无论哪种方式我都想听听您的意见。

谢谢:)

【问题讨论】:

    标签: delphi library-design


    【解决方案1】:

    BeginUpdate 禁止字符串列表的OnChangingOnChange 事件。根据连接的内容,它可以显着加快速度。

    在您的示例中,BeginUpdate/EndUpdate 没有太大区别。使用 TStringlist 实例并将其分配给列表视图是一种非常有效的方法。

    【讨论】:

    • @RRUZ,确实如此!感谢您的提示。
    【解决方案2】:

    BeginUpdateEndUpdate在抽象基类TStrings中引入。所以TStringList 继承了这个能力,即使它不是特别有用。但是,它当然对许多其他TStrings 后代有用。

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

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

    现在,关于填充列表视图的代码,我认为使用中间 TStringList 没有任何意义。我将直接填充列表视图并在列表视图Items 上使用BeginUpdate/EndUpdate。如果列表视图仍然存在性能问题,则解决方案是虚拟列表视图。

    【讨论】:

    • +1,直接使用 TListBox.Items.Add 可能确实会稍微快一些,因为 TListBox.Items.Assign 为每个条目调用 TStrings.AddObject,内部调用 Add 和 PutObject。
    • @Uwe 与将数据发送到公共控件相比,这将是完全无关紧要的。这将成为瓶颈。在列表视图的 Items 上完成 BeginUpdate 后,所有变体都将花费相同的时间。
    • 但是 TListBoxStrings.PutObject 调用 ListBox.SetItemData 这也是用 SendMessage 实现的。因此,与每个项目两个 SendMessage 相比,每个项目一个 SendMessage。
    • 我明白了,所以 BeginUpdate 和 EndUpdate 存在是因为它继承自父类 TStrings - 具有讽刺意味的是,我刚刚通过 Ctrl + 单击 SL.BeginUpdate 发现了这一点。 Classes.pas 显示了 TStringList 继承自的过程 TStrings.BeginUpdate。我想知道为什么 StringList 有它是一个非可视控件。
    • 正如 Uwe 所说,它在 TStringList 中用于修改更改通知事件的行为,但我认为它存在的主要原因是授权抽象基类 TStrings
    【解决方案3】:

    这只是 锁定模式 的实现,如 here 所述。

    它允许您向temporarily lock 类的一个方面,避免不必要的通知。

    与您在 DB.TDataSet.DisableControlsDB.TDataSet.EnableControls 中可以找到的完全相同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-27
      • 2015-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多