TStringGrid性能不佳

时间:2011-10-06 01:53:50

标签: 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.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;

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

11 个答案:

答案 0 :(得分:7)

我发现在执行大量记录时非常重要的另一件事是为每个字段使用适当的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(如果适用)。

答案 1 :(得分:6)

解决方案是使用“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秒减少到大约50ms。

答案 2 :(得分:3)

循环中使用的FieldByName非常慢,因为每次都会计算它。你应该在循环中完成它,然后只在循环中使用结果。

答案 3 :(得分:3)

第一个优化是用本地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方法。

答案 4 :(得分:3)

TStringGrid适用于少量记录,但不要尝试超过10.000条记录。

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

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

答案 5 :(得分:2)

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

最好的问候。

答案 6 :(得分:2)

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

答案 7 :(得分:2)

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

FieldByName进行谎言搜索:

  for I := 0 to FList.Count - 1 do
  begin
    Result := FList.Items[I];
  ...

如果您的数据集包含许多列(字段),则性能仍会较低。

问候。

答案 8 :(得分:2)

我打算说“为什么不使用beginupdate / endupdate?”但现在我看到常规字符串网格不支持它 在谷歌搜索时,我找到了一种模拟beginupdate / endupdate的方法:

http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_21832072.html

请参阅ZhaawZ的答案,他使用一对WM_SETREDRAW消息来禁用/启用重新绘制。如果这样做,与“消除使用FieldbyName”技巧一起使用,它应该没有时间绘制。

答案 9 :(得分:1)

在循环之前设置Grid.RowCount = 2,然后在循环结束时将rowcount设置为正确的值。

这可以避免大量调用OnPaint事件。

答案 10 :(得分:0)

在我的情况下,事实证明,调试版本的运行速度很慢,而发布版本的运行速度却很快-一个Heisenbug。

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