2012-03-10 1 views
6

FastReport를 사용하며 1000 개가 넘는 행이있는 그리드를 미리보고 인쇄해야하며 일부 성능 문제가 있습니다. 일반적으로 TfrxCrossObject를 사용하여 최종 사용자가 그리드 프레젠테이션 (열, 열 이름, 크기 사용)을 변경할 수 있기 때문에 그리드를 준비합니다. 따라서 동적 인 인쇄물이 필요합니다. 간단한 그리드 (16 cols x2000 rows)를 테스트했으며 첫 번째 미리보기 페이지를 표시하는 데 10 초 이상이 걸렸습니다. 공연을 개선하기위한 아이디어가 있으십니까?FastReport TFrxCrossObject 및 큰 그리드 (> 1000 행)를 사용하는 실적

편집 : 으로 일부 답변에서 말했다, 문제는 'dynamicaly'없는 것 같다 TFrxCrossObject를 사용하지 않고 FastReport에서 (같은 열 이름과 내가 화면에있는 크기) 그리드를 만드는 방법 매우 효율적입니다. 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. 
+1

이 질문은 FastReports 포럼에 게시하는 것이 좋습니다. AQTime을 사용하는 경우 응용 프로그램을 프로파일 링하고 적어도 대부분의 시간을 볼 수 있습니다. AQtime은 Delphi XE 및 XE2의 일부 에디션에 포함되어 있으므로 이미 가지고있을 수 있습니다. –

+3

'crossview' 대신 ClientDataSet을 사용하십시오. – teran

+1

코드 성능을 테스트하는 가장 간단한 방법은 코드의 일부와 전후에'GetTickCount'를 사용하여 값을 비교하는 것입니다. 하지만 먼저 StringGrid를 채울 때'BeginUpdate' /'EndUpdate'를 사용하십시오. – LightBulb

답변

4

크로스 탭은 많은 오버 헤드를 가지고있다. 여기 UserDataSet 버전은 다음과 같습니다

  1. 단지 형태로 1 stringgrid, 1 개 버튼 1 frxReport, 1 frxUserDataSet 놓습니다.

  2. 아래 코드와 같이 frxUserDataSet 이벤트, Form OnCreate 및 Buttom OnClick을 설정하십시오.

  3. 보고서를 디자인하거나 속성을 설정할 필요가 없습니다. 모두 런타임에 설정되거나 생성됩니다.

크로스 탭 버전보다 빠르지 만 CrossObject의 코딩 및 손실 된 기능이 더 필요합니다.

편집 : 일부 의견을 추가하고 PaperWidth 잘못 계산을 수정하십시오.

편집 2 : 페이지로 데이터를 분할하는 인쇄용 버전을 추가하십시오.

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

내 노트북 ​​컴퓨터 (i5 2.4GHz, 4GRam)에서 원래 버전은 첫 번째 페이지를 2.5 초에 표시하고 준비 완료 (모든 페이지로드)를 9 초에 완료합니다. 내 디스플레이 버전 (1 큰 페이지) 2.5 초 준비. 내 인쇄 버전은 첫 페이지를 즉시 표시하고 1.5 초 (112 페이지)를 준비합니다. 2000 Col X 200 행은 10.5 초에 첫 페이지를 즉시 표시하고 준비 할 수 있습니다 (448 페이지). 2000 Col X 2000 Row는 첫 번째 페이지를 즉시 표시하고 104 초에 준비 (4144 페이지)합니다. – Justmade

+0

올바른 방법으로 보입니다. 나는 오늘 그것을 시험 할 것이다. – philnext

+0

@philnext DBGrid/DataSet을 사용하는 경우 Y는 필드 인덱스로 변경하고 Inc (X)는 DataSet.Next로 변경되며 열 나누기에 북마크가 필요할 수 있습니다. 따라서 getvalue 이벤트에서 DataSet.Fields [Y] .Value를 제공 ​​할 수 있습니다. – Justmade

2

귀하 코드 조각은 약간 수정 된 FastReport의 PrintStringGrid 데모에 해당합니다 (RowCount = 16 대신에 2000).

대용량 데이터를 처리해야하는 경우 TStringGrid를 컨테이너로 사용하는 것은 좋은 생각이 아닙니다. 이는 단지 프리젠 테이션이 우려 될 때만 사용해야합니다.

메모 메모 스레드에서 제안 된 teran으로 메모리 데이터 세트 (예 : ClientDataset)를 사용하면 큰 데이터를 TStringGrid에 표시 할 수 있습니다. 사용하는 경우 필수이지만 TDBGrid가 더 적절합니다.

단순한 TStringGrid를 사용하는 것보다 큰 TDataset을 반복하는 것이 빠릅니다.

FastReport의 PrintTable 데모가 PrintStringGrid 데모와 같은 구성 요소를 사용하는 것을 알고 연습으로 당신에 남아 적응 출발점이 될 수있다 :

  • TfrxReport 및
  • TfrxCrossObject 여기서 반복이 발생합니다.
+1

Clientdataset (눈금)에 현재 데이터가있는 100 행 * 100 열이 있고 사용자가 Clientdataset에 열을 추가하는 방법 1 열을 더 추가 할 것인지 물어볼 수 있습니까? 또한 해당 열을 보고서에 추가하는 방법은 무엇입니까? DBCrossTab을 사용하는 경우 CrossTab의 계산 및 정렬 오버 헤드로 인해 성능이 향상되지 않을 것입니다. – Justmade

+0

예 IRL, TDBGrid를 사용하지만 TStringGrid가 내 문제를 설명하고 응답자가 테스트하는 것이 더 쉽습니다. 나는 똑같은 성격을 가져야 만한다. TDBGrid의 문제점. – philnext

관련 문제