使用FastReport TFrxCrossObject和大型网格(> 1000行)的性能

时间:2012-03-10 20:24:05

标签: delphi grid fastreport

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

编辑: 正如在一些答案中所说,问题是:如何在FastReport中创建'dynamicaly'网格(在屏幕上具有相同的列名称和大小),而不使用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.

2 个答案:

答案 0 :(得分:4)

CrossTab有很多开销。这是UserDataSet版本:

  1. 只需在表单中删除1个stringgrid,1个按钮,1个frxReport,1个frxUserDataSet。

  2. 设置frxUserDataSet事件,Form OnCreate和Buttom OnClick如下代码。

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

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

    编辑:添加一些注释并修复PaperWidth错误计算。

    Edit2:添加一个可打印的版本,将数据拆分成页面。

    查看友好版本在1个单页中显示为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.
    

答案 1 :(得分:2)

您的代码对应于稍微修改过的FastReport的PrintStringGrid演示(RowCount = 2000而不是16)。

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

使用内存数据集(例如ClientDataset作为问题评论线程中建议的teran),如果使用它是强制性的,但仍然可以将大数据呈现给TStringGrid,但TDBGrid更合适。

迭代一个大的TDataset比仅仅用TStringGrid做同样快。

FastReport的PrintTable演示可以作为一个起点,适应它只是一个练习,知道它使用与PrintStringGrid演示相同的组件:

  • TfrxReport和
  • 进行迭代的TfrxCrossObject。