Performances with FastReport TFrxCrossObject and large grids (> 1000 lines)

I use FastReport, and I need to view / print grids with more than 1000 lines, and I have some performance issues. Usually I use TfrxCrossObject to prepare my grid, because the end user can change the grid view (columns used, column name, size), so I need to have dynamic printing. I checked a simple grid (16 columns x2000 rows) and it takes more than 10 seconds to present the first preview page. Any idea to improve performance?

EDIT: As some answers say, the problem is this: how to create a “dynamic” grid (with the same column names and sizes that I have on the screen) in FastReport without using TFrxCrossObject, which seems to be not very efficient. I can make all decisions, such as using a DataSet or the TfrxCrossObject extension.

Security Code:

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. 
+6
source share
2 answers

CrossTab has a lot of overhead. Here is the version of UserDataSet:

  • Just release 1 stringgrid, 1 button, 1 frxReport, 1 frxUserDataSet in the form.

  • Set the frxUserDataSet, Form OnCreate, and Buttom OnClick events, as shown below.

  • No need to create reports or set any properties, all will be set or generated at runtime.

This seems to be faster than the cross-tab version, but you need to code more and lose CrossObject functionality.

Edit: Add comments and correct the erroneous calculation of PaperWidth.

Edit2: Add a print version that divides data into pages.

Show friendly version show in 1 single page as 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 ie 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. 

The print version, which is a little more complicated and shares data on different pages for printing:

 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 ie 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. 
+4
source

Your part of the code corresponds to the demo version of PrintStringGrid FastReport, slightly modified (RowCount = 2000 instead of 16).

Using TStringGrid as a container is not a good idea if you have to deal with big data: it should be used only for concern.

Use a dataset in memory (for example, ClientDataset as a teran, suggested in the comment thread of the question), you can still present your big data in TStringGrid, if its use is required, but TDBGrid is more suitable.

Iterating a large TDataset is faster than using a simple TStringGrid.

The FastReport PrintTable can serve as a starting point, PrintTable it to you as an exercise, knowing that it uses the same components as the PrintStringGrid demo:

  • TfrxReport and
  • TfrxCrossObject where iteration is performed.
+2
source

Source: https://habr.com/ru/post/910421/


All Articles