Delphi FastReport TFrxCrossObject和大网格(>;1000行)的性能

Delphi FastReport TFrxCrossObject和大网格(>;1000行)的性能,delphi,grid,fastreport,Delphi,Grid,Fastreport,我使用FastReport,我需要预览/打印超过1000行的网格,我有一些性能问题。 通常我使用TfrxCrossObject来准备网格,因为最终用户可能会更改网格表示(使用的列、列的名称、大小),所以我需要动态打印。 我测试了一个简单的网格(16 cols x2000行),显示第一个预览页面需要10秒钟以上。 有没有改进性能的想法 编辑: 正如一些回答中所说,问题在于:如何在FastReport中创建“动态”网格(与我在屏幕上看到的列名称和大小相同),而不使用TFrxCrossObject,这

我使用FastReport,我需要预览/打印超过1000行的网格,我有一些性能问题。 通常我使用TfrxCrossObject来准备网格,因为最终用户可能会更改网格表示(使用的列、列的名称、大小),所以我需要动态打印。 我测试了一个简单的网格(16 cols x2000行),显示第一个预览页面需要10秒钟以上。 有没有改进性能的想法

编辑: 正如一些回答中所说,问题在于:如何在FastReport中创建“动态”网格(与我在屏幕上看到的列名称和大小相同),而不使用TFrxCrossObject,这似乎不是很有效。我可以承认所有的解决方案,比如使用DataSet或增强TfrxCrossObject

测试代码:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  frxClass, StdCtrls, Grids, frxCross;

type
  TForm1 = class(TForm)
    Button1: TButton;
    StringGrid1: TStringGrid;
    frxCrossObject1: TfrxCrossObject;
    frxReport1: TfrxReport;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure frxReport1BeforePrint(c: TfrxReportComponent);
  end;

var
  Form1: TForm1;

implementation
{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
  i, j: Integer;
begin
  for i := 1 to 16 do
    for j := 1 to 2000 do
      StringGrid1.Cells[i - 1, j - 1] := IntToStr(i * j);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  frxReport1.ShowReport;
end;

procedure TForm1.frxReport1BeforePrint(c: TfrxReportComponent);
var
  Cross: TfrxCrossView;
  i, j: Integer;
begin
  if c is TfrxCrossView then
  begin
    Cross := TfrxCrossView(c);
    for i := 1 to 16 do
      for j := 1 to 2000 do
        Cross.AddValue([i], [j], [StringGrid1.Cells[i - 1, j - 1]]);
  end;
end;
end.

交叉表有很多开销。以下是UserDataSet版本:

  • 只需在表单中删除1个stringgrid、1个按钮、1个frxReport和1个frxUserDataSet

  • 设置frxUserDataSet事件、表单OnCreate和按钮OnClick,如下代码所示

  • 无需设计报告或设置任何属性,所有属性都将在运行时设置或生成

  • 它似乎比交叉表版本快,但您需要更多的编码和CrossObject的功能丢失

    编辑:添加一些注释并修复纸张宽度计算错误

    Edit2:添加一个打印友好版本,可将数据拆分为页面

    以stringgrid格式在一个页面中显示友好版本:

    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, frxClass, Grids, StdCtrls;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        StringGrid1: TStringGrid;
        frxReport1: TfrxReport;
        frxUserDataSet1: TfrxUserDataSet;
        procedure Button1Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure frxUserDataSet1Next(Sender: TObject);
        procedure frxUserDataSet1GetValue(const VarName: string; var Value: Variant);
        procedure frxUserDataSet1CheckEOF(Sender: TObject; var Eof: Boolean);
        procedure frxUserDataSet1First(Sender: TObject);
      private
        X, Y, TCol, TRow : Integer;
        IsEof : Boolean;
        CW, CH, PF : Double;
        Page : TfrxReportPage;
        MDB : TfrxMasterData;
        Memo : TfrxMemoView;
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      BW : Double;
    begin
      IsEof := False;
      Page.PaperWidth :=  CW * TCol + 20; // EndlessWidth seems not work with band column
      MDB.SetBounds(0,0, CW * PF * TCol, CH * PF);
      MDB.Columns := TCol;
      MDB.ColumnWidth := CW * PF;
      frxReport1.ShowReport;
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    var
      i, j : Integer;
    begin
      CW := 12; // Cell Width in mm
      CH := 5;  // Cell Height in mm
      PF := 3.7794; // Pixie Factor i.e. the conversion of mm to FR component measurement
      TCol := 2000; // Total Column
      TRow := 16; // Total Row
    
      for i := 1 to TRow do
        for j := 1 to TCol do
          StringGrid1.Cells[i - 1, j - 1] := IntToStr(i * j);
    
      frxUserDataSet1.Fields.Text := 'Data';
      frxReport1.Clear;
      frxReport1.DataSets.Add(frxUserDataSet1);
      Page := TfrxReportPage.Create(frxReport1);
      Page.CreateUniqueName;
      Page.TopMargin := 10;
      Page.BottomMargin := 10;
      Page.LeftMargin := 10;
      Page.RightMargin := 10;
      Page.EndlessHeight := True;
      Page.EndlessWidth := True;
      MDB := TfrxMasterData.Create(Page);
      MDB.DataSet := frxUserDataSet1;
      Memo := TfrxMemoView.Create(MDB);
      Memo.SetBounds(0,0,CW * PF,CH * PF);
      Memo.Memo.Text := '[frxUserDataSet1."Data"]';
      Memo.Frame.Typ := [ftLeft, ftRight, ftTop, ftBottom];
    end;
    
    procedure TForm1.frxUserDataSet1CheckEOF(Sender: TObject; var Eof: Boolean);
    begin
      Eof := IsEof;
    end;
    
    procedure TForm1.frxUserDataSet1First(Sender: TObject);
    begin
      X := 0;
      Y := 0;
    end;
    
    procedure TForm1.frxUserDataSet1GetValue(const VarName: string; var Value: Variant);
    begin
      Value := StringGrid1.Cells[X,Y];
    end;
    
    procedure TForm1.frxUserDataSet1Next(Sender: TObject);
    begin
      If Y = TCol - 1 then
      begin
        if X = TRow - 1 then
          IsEof := True;
        Inc(X);
        Y := 0;
      end
      else
        Inc(Y);
    end;
    
    end.
    
    打印友好型版本,更为复杂,可在不同页面中单独打印数据:

    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, frxClass, Grids, StdCtrls;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        StringGrid1: TStringGrid;
        frxReport1: TfrxReport;
        frxUserDataSet1: TfrxUserDataSet;
        procedure Button1Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure frxUserDataSet1Next(Sender: TObject);
        procedure frxUserDataSet1GetValue(const VarName: string; var Value: Variant);
        procedure frxUserDataSet1CheckEOF(Sender: TObject; var Eof: Boolean);
        procedure frxUserDataSet1First(Sender: TObject);
      private
        X, Y, TCol, TRow, RPP, ColBreak : Integer;
        IsEof : Boolean;
        CW, CH, PF : Double;
        Page : TfrxReportPage;
        MDB : TfrxMasterData;
        Memo : TfrxMemoView;
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    uses Math;
    
    {$R *.dfm}
    
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      BW : Double;
    begin
      IsEof := False;
      RPP := Ceil((Page.PaperHeight - Page.TopMargin - Page.BottomMargin) / CH) - 1; // Row per page
      ColBreak := RPP; // break to next column
    
      frxReport1.ShowReport;
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    var
      i, j : Integer;
    begin
      CW := 12; // Cell Width in mm
      CH := 5;  // Cell Height in mm
      PF := 3.7794; // Pixil Factor i.e. the conversion of mm to FR component measurement
      TCol := 2000; // Total Column
      TRow := 16; // Total Row
    
      for i := 1 to TRow do
        for j := 1 to TCol do
          StringGrid1.Cells[i - 1, j - 1] := IntToStr(i * j);
    
      frxUserDataSet1.Fields.Text := 'Data';
      frxReport1.Clear;
      frxReport1.DataSets.Add(frxUserDataSet1);
      Page := TfrxReportPage.Create(frxReport1);
      Page.CreateUniqueName;
      Page.TopMargin := 10;
      Page.BottomMargin := 10;
      Page.LeftMargin := 10;
      Page.RightMargin := 10;
      Page.Columns := Ceil(Page.PaperWidth / CW);
      MDB := TfrxMasterData.Create(Page);
      MDB.DataSet := frxUserDataSet1;
      MDB.SetBounds(0,0, CW * PF, CH * PF);
      Memo := TfrxMemoView.Create(MDB);
      Memo.Align := baClient;
      Memo.Memo.Text := '[frxUserDataSet1."Data"]';
      Memo.Frame.Typ := [ftLeft, ftRight, ftTop, ftBottom];
    end;
    
    procedure TForm1.frxUserDataSet1CheckEOF(Sender: TObject; var Eof: Boolean);
    begin
      Eof := IsEof;
    end;
    
    procedure TForm1.frxUserDataSet1First(Sender: TObject);
    begin
      X := 0;
      Y := 0;
    end;
    
    procedure TForm1.frxUserDataSet1GetValue(const VarName: string; var Value: Variant);
    begin
      Value := StringGrid1.Cells[X,Y];
    end;
    
    procedure TForm1.frxUserDataSet1Next(Sender: TObject);
    begin
      If X = TRow - 1 then
      begin
        if Y = TCol - 1 then
          IsEof := True
        else
        begin
          frxReport1.Engine.NewColumn;
          Inc(Y);
          X := ColBreak - RPP;
        end;
      end
      else if (X = ColBreak - 1) then
      begin
        if Y = TCol - 1 then
        begin
          frxReport1.Engine.NewPage;
          ColBreak := ColBreak + RPP;
          Y := 0;
        end
        else
          Inc(Y);
        frxReport1.Engine.NewColumn;
        X := ColBreak - RPP;
      end
      else
        Inc(X);
    end;
    
    end.
    

    您的一段代码对应于稍加修改的FastReport的
    PrintStringGrid
    demo(RowCount=2000而不是16)

    如果您必须处理大数据,那么使用TStringGrid作为容器不是一个好主意:它应该只用于表示问题

    使用内存数据集(如问题注释线程中teran建议的ClientDataset),如果必须使用TStringGrid,您仍然可以将您的大数据呈现给TStringGrid,但TDBGrid更合适

    迭代一个大的TDataset要比只使用TStringGrid进行同样的操作快

    FastReport的
    PrintTable
    演示可以作为一个起点,您可以将其作为一个练习进行调整,因为您知道它使用与
    PrintStringGrid
    演示相同的组件:

    • TFRX报告和
    • 发生迭代的TFRxCross对象

    我建议将此问题发布在FastReports论坛上。如果您正在使用AQTime,您可以使用它来评测您的应用程序,至少可以看到大部分时间都花在了哪里。AQtime包含在某些版本的Delphi XE和XE2中,因此您可能已经拥有了它。请尝试使用ClientDataSet而不是
    crossview
    。测试代码性能的最简单方法是在部分代码之前和之后使用
    GetTickCount
    ,并比较值。但首先,在填充StringGrid时尝试使用
    BeginUpdate
    /
    EndUpdate
    。我刚刚测试了您的示例,有3-4秒的时间获得第一页预览,有9-10秒的时间生成整个报告。我认为112页的报告还不错。(Q8200@2.3GHz,4GB内存)如果我在
    AddValue
    中交换
    I
    j
    (生成16列和2000行),然后将
    I
    减少到15(适合页面),总时间将变为大约5秒(45页)@WarrenP我以前在FastReport论坛上发布过,但最近,它的效率和垃圾邮件更少。和性能。问题不在我的代码中,而是在FR one中,所以性能。工具不是很有用。请问Clientdataset(网格)当前是否有100行*100列的数据,并且用户希望再添加一列,如何将该列添加到Clientdataset?另外,如何将该列添加到报告中?如果使用DBCrossTab,我确信由于CrossTab的计算和排序开销,性能不会有任何提高。是的,IRL,我使用TDBGrid,但是TStringGrid在这里更容易解释我的问题,也更容易让回答者测试它。我必须做同样的表演。TDBGrid存在问题。在我的笔记本电脑(i5 2.4GHz,4GRam)中,原始版本在2.5秒时显示第一页,在9秒时显示就绪(所有页面已加载)。我的显示版本(1个大页面)在2.5秒时准备就绪。我的打印版本会立即显示第一页,并在1.5秒内准备就绪(112页)。2000 Col X 200 Row立即显示第一页,并以10.5秒的速度准备就绪(448页)。2000 Col X 2000 Row立即显示第一页,并在104秒时准备就绪(4144页)。@philnext如果使用DBGrid/DataSet,Y可以更改为字段索引,Inc(X)可以更改为DataSet。接下来,您可能需要一个书签用于分栏。因此,在getvalue事件中,您可以提供DataSet.Fields[Y].Value。@JustMake,如果可能,请查看我在遇到的FastReport问题