Delphi TStringGrid的性能较差

Delphi TStringGrid的性能较差,delphi,Delphi,我有一个10列的TStringGrid。向其中添加500行大约需要2秒钟。这是正常的表演吗 我觉得有点慢 我从数据库查询中获取数据。如果我循环查询,但不将结果写入StringGrid,这个过程大约需要100毫秒,因此不是数据库让事情变慢 添加行后,StringGrid性能良好 这是我正在使用的代码 Grid.RowCount := Query.RecordCount; J := 0; while not Query.EOF do begin Grid.Cells[0,J]:=Query

我有一个10列的TStringGrid。向其中添加500行大约需要2秒钟。这是正常的表演吗

我觉得有点慢

我从数据库查询中获取数据。如果我循环查询,但不将结果写入StringGrid,这个过程大约需要100毫秒,因此不是数据库让事情变慢

添加行后,StringGrid性能良好

这是我正在使用的代码

Grid.RowCount := Query.RecordCount;
J := 0;

while not Query.EOF do
begin
    Grid.Cells[0,J]:=Query.FieldByName('Value1').AsString;
    Grid.Cells[1,J]:=Query.FieldByName('Value2').AsString;
    Grid.Cells[2,J]:=Query.FieldByName('Value3').AsString;
    // etc for other columns.
    Inc(J);
    Query.Next();
end;

真正的代码实际上有点复杂(表列与查询列不完全对应),但这是基本思想

我认为它很慢,因为每次添加行时它都必须重新绘制自己。由于您是从查询中获取值,因此我认为最好使用TDBGrid


致以最诚挚的问候。

如果您知道要添加多少行,请将当前行数存储在一个临时变量中,设置网格的
rowcount
以适应当前行数加上要添加的行数,然后将新值分配给行(使用以前存储的行数),而不是添加它们。这将减少大量后台处理。

解决方案是使用“Rows”属性一次添加一行中的所有值

我的代码现在如下所示:

Grid.RowCount := Query.RecordCount;
rowValues:=TStringList.Create;
J := 0;

while not Query.EOF do
begin
    rowValues[0]:=Query.FieldByName('Value1').AsString;
    rowValues[1]:=Query.FieldByName('Value2').AsString;
    rowValues[2]:=Query.FieldByName('Value3').AsString;
    // etc for other columns.
    Grid.Rows[J]:=rowValues;
    Inc(J);
    Query.Next();
end;

rowValues.Free; // for the OCD among us

这将时间从2秒减少到了大约50毫秒。

我发现在查看大量记录时,另一件非常重要的事情是为每个字段使用适当的TField变量。FieldByName每次都会遍历Fields集合,因此不是性能最好的选项。 在循环之前,将每个字段定义为:

var
  f1, f2: TStringField;
  f3: TIntegerField;

begin
  // MyStringGrid.BeginUpdate; // Can't do this
  // Could try something like this instead:
  // MyStringGrid.Perform(WM_SETREDRAW, 0, 0);
  try
    while ... do
    begin
      rowvalues[0] := f1.AsString;
      rowvalues[1] := f2.AsString;
      rowvalues[2] := Format('%4.2d', f3.AsInteger);
      // etc 
    end;
  finally
    // MyStringGrid.EndUpdate; // Can't - see above
    // MyStringGrid.Perform(WM_SETREDRAW, 1, 0);
    // MyStringGrid.Invalidate;
  end;
end;

与BeginUpdate/Endupdate一起调用Query.DisableControls(如果合适)。

循环中使用的FieldByName非常慢,因为每次都会计算它。您应该在循环外进行测试,然后只在循环内使用结果。

尝试使用AQTime或类似工具(探查器)进行测试。
没有任何代码是困难的,但我认为性能差是由于FieldByName,而不是StringGrid

FieldByName进行liear搜索:

  for I := 0 to FList.Count - 1 do
  begin
    Result := FList.Items[I];
  ...
如果您的数据集有许多列(字段),那么性能仍然会较低


注意。

第一个优化是用本地TQuery替换非常慢的Query.FieldByName('Value1')调用

var
  F1, F2, F3: TField;

Grid.RowCount := Query.RecordCount;
J := 0;
F1 := Query.FieldByName('Value1');
F2 := Query.FieldByName('Value2');
F3 := Query.FieldByName('Value3');
while not Query.EOF do
begin
    Grid.Cells[0,J]:=F1.AsString;
    Grid.Cells[1,J]:=F2.AsString;
    Grid.Cells[2,J]:=F3.AsString;
    // etc for other columns.
    Inc(J);
    Query.Next();
end;
如果这还不够,请在虚拟模式下使用网格,即检索TStringList或任何内存结构中的所有内容,然后使用OnGetText或OnDrawCell方法。

我本来想说“为什么不使用beginupdate/endupdate?”但现在我发现常规字符串网格不支持它。
通过谷歌搜索,我找到了一种模拟beginupdate/endupdate的方法:


查看ZhaawZ的答案,他使用一对WM_SETREDRAW消息来禁用/启用重新绘制。如果这有效,请与“消除FieldbyName的使用”技巧结合使用,它应该不会花费时间绘制。

TStringGrid对少量记录有效,但不要尝试超过10000条记录

在加载/排序/分组大型网格集时,以及在网格顶部插入一行(扩展网格组节点)时,TMS的TAdvStringGrid(基于Delphi TStringGrid)存在严重的性能问题。内存使用率也很高。 是的,我已经使用了beginupdate/endupdate。还有其他技巧。但在深入研究了TStringGrid的结构后,我得出结论,对于许多记录来说,它永远不会是快的


一般提示(对于大网格):使用OnGetText(和OnSetText)事件。此事件用于按需填充网格(仅显示单元格)。将数据存储在您自己的数据对象中。这使得我们的网格非常快(1.000.000记录不再是问题,在几秒钟内加载!)

在循环之前设置
grid.RowCount=2
,然后在循环完成时将
RowCount
设置为正确的值


这避免了对OnPaint事件的大量调用。

在我的例子中,调试版本很慢,发布版本很快,这是一个Heisenbug


更具体地说,FullDebugMode触发了慢度。

如果没有代码,这是一个不可能回答的问题。“不,这不正常”是可以接受的,就像“是的,这是”和“可能-这取决于”一样。您是自定义绘制网格还是默认图形?发布您用于填充网格的代码,也许有人可以提供帮助。TListView可能是更好的控件选择。我添加了一些示例代码。网格只是一个标准的TStringGrid;没有自定义绘图。FieldByName调用repeatedlyAny很慢而且毫无意义,因为您没有使用DB感知的网格控件?我认为代码已经做到了这一点(请参阅上面添加的代码示例),如果您在原始问题中提供了代码,我会知道这一点。感谢您的澄清。在此操作期间,您还应锁定stringgrid。Grid.Cols[0]。开始更新,最后尝试(设置行数和填充行)Grid.Cols[0]。结束更新。这有点奇怪,但这个把戏锁定了整个网格,不仅仅是柱。老实说,我不理解速度的差异。这意味着
Grid.Cells
访问速度非常慢,我怀疑…有人能解释这一点吗?@Smasher据我所知,这只是更新的数量。使用单元格需要对网格进行10倍的“更新”;每个单元一个。而使用Grid.Rows每行只需要一次“更新”。因此,如果网格中有10列,则更新的次数是原来的10倍。@LU RD我尝试使用原始代码调用BeginUpdate和EndUpdate。这并没有造成明显的区别。不确定?最好是发布运行的代码,或者放入。。。(省略号)表示省略。虽然使用
TDBGrid
肯定是一个选项:应该可以在没有性能问题的情况下向字符串网格中添加500行,所以我认为这是不可能的