2012-03-10 48 views
6

Uso FastReport y necesito obtener una vista previa/imprimir Grillas con más de 1000 filas y tengo algunos problemas de rendimiento. Normalmente utilizo TfrxCrossObject para preparar mi cuadrícula porque el usuario final puede cambiar la presentación de la grilla (columnas usadas, nombre de la columna, tamaño), así que necesito una impresión dinámica. He probado una cuadrícula simple (16 columnas x2000 filas) y necesita más de 10 segundos para presentar la primera página de vista previa. ¿Alguna idea para mejorar el rendimiento?Prestaciones con FastReport TFrxCrossObject y grillas grandes (> 1000 filas)

EDIT: Como se ha dicho en algunas respuestas, el problema es: cómo crear 'dynamicaly' una rejilla (con nombres mismas columnas y tamaños que tengo en la pantalla) en FastReport sin utilizar TFrxCrossObject que parece ser no muy eficiente. Puedo admitir todas las soluciones como usar DataSet o mejorar TfrxCrossObject.

El código de prueba:

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. 
+1

Recomiendo publicar esta pregunta en los foros de FastReports. Si está utilizando AQ Time, puede usarlo para perfilar su aplicación y, al menos, ver DÓNDE pasa la mayor parte de ese tiempo. AQtime está incluido en algunas ediciones de Delphi XE y XE2, por lo que es posible que ya lo tenga. –

+3

intente utilizar ClientDataSet en lugar de 'crossview'. – teran

+1

La manera más simple de probar el rendimiento del código es usar 'GetTickCount' antes y después de partes del código y comparar valores. Pero primero, intente utilizar 'BeginUpdate' /' EndUpdate' cuando complete el StringGrid. – LightBulb

Respuesta

4

la tabla cruzada tiene muchos gastos generales. He aquí una versión UserDataSet:

  1. tan sólo reduce StringGrid 1, 1 botón, 1 frxReport, 1 frxUserDataSet en forma.

  2. Establezca los eventos frxUserDataSet, Form OnCreate y Buttom OnClick como el código siguiente.

  3. No hay necesidad de diseñar Informar o establecer ninguna propiedad, todas se establecerán o generarán en tiempo de ejecución.

Parece que es más rápido que la versión de tabulación cruzada, pero necesita más codificación y funcionalidad perdida de CrossObject.

Editar: Añada algunos comentarios y solucione el error de cálculo de PaperWidth.

Editar2: Agregue una versión para imprimir que divide los datos en páginas.

versión

Ver amigable en 1 sola página como el 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. 

-Imprimir Versión para lo cual es un poco más de datos por separado en diferentes páginas de impresión complejo y:

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. 
+0

En mi computadora portátil (i5 2.4GHz, 4GRam), la versión original muestra la primera página en 2.5 segundos y lista (toda la página cargada) en 9 segundos. Mi versión de pantalla (1 página grande) lista a los 2.5 segundos. Mi versión de impresión muestra la primera página de forma instantánea y lista (112 páginas) a los 1.5 segundos. 2000 Col X 200 Row muestra la primera página al instante y lista (448 páginas) a los 10.5 segundos. 2000 Col X 2000 La fila muestra la primera página al instante y lista (4144 páginas) a 104 segundos. – Justmade

+0

Parece ser una forma correcta. Lo probaré hoy. – philnext

+0

@philnext Si está utilizando DBGrid/DataSet, Y puede cambiar al índice de campo y el Inc (X) cambia a DataSet.Next y es posible que necesite un marcador para el salto de columna. Por lo tanto, en el evento getvalue puede proporcionar DataSet.Fields [Y] .Value. – Justmade

2

Su una parte del código corresponde a la demostración PrintStringGrid de FastReport ligeramente modificada (RowCount = 2000 en lugar de 16).

El uso de TStringGrid como contenedor no es una buena idea si tiene que tratar con datos de gran tamaño: solo debe utilizarse para problemas de presencias.

Use un conjunto de datos en memoria (por ejemplo, ClientDataset como se sugiere en el hilo de comentario de la pregunta), aún puede presentar sus datos grandes en un TStringGrid si es obligatorio pero un TDBGrid es más apropiado.

Iterar un TDataset grande es más rápido que hacer lo mismo con un simple TStringGrid.

La demo PrintTable de FastReport podría servir como punto de partida, la adaptación se deja a usted como un ejercicio sabiendo que utiliza los mismos componentes que PrintStringGrid demo:

  • Un TfrxReport y
  • Un TfrxCrossObject donde tiene lugar la iteración.
+1

¿Puedo preguntar si el Clientdataset (grilla) tiene actualmente 100 fila * 100 col con datos y el usuario desea agregar 1 columna más, cómo agregar la columna al Clientdataset? Además, ¿cómo agregar esa columna al informe? Si utilizo DBCrossTab, estoy seguro de que no habrá una mejora en el rendimiento debido al cálculo y la sobrecarga de clasificación de CrossTab. – Justmade

+0

Sí IRL, utilizo TDBGrid pero TStringGrid fue más fácil aquí para explicar mi problema y para que los contestadores lo prueben. Tengo que mismo perf. problemas con TDBGrid. – philnext

Cuestiones relacionadas