Delphi FastReport TFrxCrossObject和大网格(>;1000行)的性能
我使用FastReport,我需要预览/打印超过1000行的网格,我有一些性能问题。 通常我使用TfrxCrossObject来准备网格,因为最终用户可能会更改网格表示(使用的列、列的名称、大小),所以我需要动态打印。 我测试了一个简单的网格(16 cols x2000行),显示第一个预览页面需要10秒钟以上。 有没有改进性能的想法 编辑: 正如一些回答中所说,问题在于:如何在FastReport中创建“动态”网格(与我在屏幕上看到的列名称和大小相同),而不使用TFrxCrossObject,这似乎不是很有效。我可以承认所有的解决方案,比如使用DataSet或增强TfrxCrossObject 测试代码:Delphi FastReport TFrxCrossObject和大网格(>;1000行)的性能,delphi,grid,fastreport,Delphi,Grid,Fastreport,我使用FastReport,我需要预览/打印超过1000行的网格,我有一些性能问题。 通常我使用TfrxCrossObject来准备网格,因为最终用户可能会更改网格表示(使用的列、列的名称、大小),所以我需要动态打印。 我测试了一个简单的网格(16 cols x2000行),显示第一个预览页面需要10秒钟以上。 有没有改进性能的想法 编辑: 正如一些回答中所说,问题在于:如何在FastReport中创建“动态”网格(与我在屏幕上看到的列名称和大小相同),而不使用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版本:
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对象
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问题