1. 程式人生 > >在Delphi中動態生成QuickReport報表

在Delphi中動態生成QuickReport報表

----筆者在前一段使用Delphi開發資料庫的工作中,使用者提出了這樣一個需求:要根據自己的的查詢結果動態生成報表然後進行列印。幾經摸索,筆者使用動態生成QuickReport控制元件的方法滿足了使用者的需求。現將此方法說明如下,希望能為有類似工作要做的朋友們提供一點有益的提示。

 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

一、基本思路

----先將查詢的一些引數(如SQL命令,欄位名稱,欄位寬度等)按照一定格式寫入一個臨時檔案中。在生成報表時,根據臨時檔案中所記錄的引數動態生成各種

QuickReport控制元件即可。

二、程式實現

2.1臨時檔案格式

----臨時檔案的格式可以根據需要自定義,筆者採用了INI的檔案格式。Delphi提供了一個TInifile類,使得在Delphi中操作INI格式檔案,非常方便。關於INI檔案的格式和具體操作相關的文章有不少,我這裡不再贅述。臨時檔案格式如下:

Report.ini 

:報表細節

[rep_detail]

Title=XXXXX

:列印紙設定,1A4,2B5,316K

Page=1

:列印方式,1為橫打,0為豎打

Orientation=1

:報表包含的欄位數目

columns=8

:TQurey元件資訊

[QureyData]

QuickReport元件中Tqurey元件的SQL命令的內容

Sql_command=select V_XM,V_JGZW,V_BMMC,V_DWMC,V_DWZW,V_ZY,V_ZC,V_BGDH from Hvzzjg where V_XMLIKE '%'

[col_0]

Caption=

DataFiled=V_XM

Width=60

……

……

2.2動態生成QuickReport報表

--- 報表的主要控制元件及其主要屬性設定如下

控制元件名稱

類名

屬性

屬性值

Form_rep

TForm

caption

動態報表

QuickRep

TQuickRep

DataSet

REP_QUERY

DetailBand1

TQRBand

BandType

rbDetail

ColumnHeaderBand1

TQRBand

BandType

rbColumnHeader

REP_DataSource

TDataSource

DataSet

Rep_Query

Rep_Query

TQuery

DatabaseName

REPDATABASE

Rep_Database

TDatabase

Connected

True

Params.Strings

'SERVER NAME=XXX

'USER MAME=XXX'

'PASSWORD=XXX'

DatabaseName

REPDATABASE

上表所示控制元件是在程式中手工建立的。其他的控制元件則要在程式中動態建立。

2.2.2主要程式

unit f_rep;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, ExtCtrls, QuickRpt, QRCtrls, DB, DBTables,PRINTERS,QRPrntr,inifiles,

TeeProcs, TeEngine, DbChart, QRTEE;

type

TForm_rep = class(TForm)

QuickRep: TQuickRep;

DetailBand1: TQRBand;

ColumnHeaderBand1: TQRBand;

REP_DataSource: TDataSource;

REP_QUERY: TQuery;

rep_Database: TDatabase;

procedure TForm_rep.QuickRepAfterPreview(Sender: TObject);//瀏覽完畢,釋放所有建立的元件

private

{ Private declarations }

public

{ Public declarations }

end;

varForm_rep:TForm_Rep;

type //報表的摘要資訊

C_rep_Summary=record

Title:string;//報表的標題

Page:TQRPaperSize;//報表的頁面設定,採用何種型號的紙

Orientation:TPrinterOrientation;//報表的頁面設定,是橫打還是豎打

Columns:integer;//報表包含的列數

end;

type

C_Rep_Col_Summary=record//報表列的摘要資訊

Caption:string;//報表的列名

DataFiled:string;//報表的列所對應的資料庫中的欄位名

Width:integer;//報表的列寬

end;

type

C_Rep_Col_Sum_store=record //儲存報表列的摘要資訊

Caption_array:array of string;

DataFiled_array:array of string;

Width_array:array of integer;

end;

var

rep_Summary:C_rep_Summary;

Rep_Col_Summary:C_Rep_Col_Summary;

Rep_Col_Sum_store:C_Rep_Col_Sum_store;

Colum_Name:array of tQRRichText;

Colum_Data:array of TQRDBRichText;

C_Query:TQuery;

procedure Form_rep_init();

procedure DynCreat_TQRDBText(Colum_Num:integer;Colum_Height:integer;DataSet_Name:TQuery);//動態建立TQRDBText控制元件

procedureDynCreat_TQRRichtext(Colum_Num:integer);//動態建立TQRRichtext控制元件

procedureDynCreat_TQuery(Inifile_Name:Tinifile);//動態建立TQuery控制元件的SQL語句

procedureGet_PageCount();//取得列印總頁數

functionOpen_IniFile():Tinifile;//開啟臨時檔案

procedureRead_Col_Summary(Inifile_Name:Tinifile);//讀取報表列的摘要資訊

procedureRead_Rep_Summary(Inifile_Name:Tinifile);//讀取報表的摘要資訊

functionrep_chanslateOrientation(var Rep_Orientation:integer):TPrinterOrientation;//將列印方式設定進行轉換

function rep_chanslatepage(var Rep_Page:integer):TQRPaperSize;//將列印頁尺寸設定進行轉換

implementation

{$R *.dfm}

function rep_chanslatepage(var Rep_Page:integer):TQRPaperSize;//將列印頁型別設定進行轉換

begin

caseRep_Page of

1:begin

result:=A4;

Form_rep.QuickRep.PrinterSettings.PaperSize:=A4;

end;

2:begin

result:=B5;

Form_rep.QuickRep.PrinterSettings.PaperSize:=B5

end;

3:begin

result:=Executive;

Form_rep.QuickRep.PrinterSettings.PaperSize:=Executive;

end;

end;

end;

function rep_chanslateOrientation(var Rep_Orientation:integer):TPrinterOrientation;//將列印方式設定進行轉換

begin

caseRep_Orientation of

0:begin

result:=poPortrait;//0為豎直

Form_rep.QuickRep.PrinterSettings.Orientation:=poPortrait;

end;

1:begin

result:=poLandscape;//1為水平

Form_rep.QuickRep.PrinterSettings.Orientation:=poLandscape;

end;

end;

end;

functionOpen_IniFile():Tinifile;//開啟臨時檔案

var Filename:string;

Ini_Filename:string;

begin

Filename:=’Report.ini’;

Ini_Filename:=File_Path+Filename;

Result:=Tinifile.Create(Ini_Filename);

end;

procedure Read_Rep_Summary(Inifile_Name:Tinifile);//讀取報表的摘要資訊

varRep_Page,Rep_Orientation:integer;

begin

rep_Page:=Inifile_Name.Readinteger('rep_detail','Page',1);

Rep_Orientation:=Inifile_Name.Readinteger('rep_detail','Orientation',0);

withrep_Summary do

begin

Columns:=Inifile_Name.Readinteger('rep_detail','columns',0);

Title:=Inifile_Name.Readstring('rep_detail','Title','未命名報表');

page:=rep_chanslatepage(Rep_Page);//將列印頁尺寸進行轉換

Orientation:=rep_chanslateOrientation(Rep_Orientation);//將列印方式設定進行轉換

end;

end;

procedureRead_Col_Summary(Inifile_Name:Tinifile);//讀取報表列的摘要資訊

var i_count:integer;

begin

//將列資訊儲存在陣列中

with Rep_Col_Sum_store do

begin

SetLength(Caption_array,rep_Summary.Columns);

SetLength(DataFiled_array,rep_Summary.Columns);

SetLength(Width_array,rep_Summary.Columns);

end;

for i_count:=0 to rep_Summary.Columns-1 do

begin

with Rep_Col_Summary do

begin

Caption:=Inifile_Name.Readstring('col_'+inttostr(i_count),'Caption','未命名');

DataFiled:=Inifile_Name.Readstring('col_'+inttostr(i_count),'DataFiled','');

Width:=Inifile_Name.Readinteger('col_'+inttostr(i_count),'Width',0);

end;

with Rep_Col_Sum_storedo

begin

Caption_array[i_count]:=Rep_Col_Summary.Caption;

DataFiled_array[i_count]:=Rep_Col_Summary.DataFiled;

Width_array[i_count]:=Rep_Col_Summary.Width;

end;

end;

end;

procedure DynCreat_TQRRichtext(Colum_Num:integer);//動態建立TQRRichtext控制元件,此控制元件用來顯示報表每列的中文名稱

var Colum_Name_list:TStrings;

begin

Colum_Name[Colum_Num]:=tQRRichtext.Create(application); //建立TQRRichtext控制元件

Colum_Name[Colum_Num].Parent:=Form_rep.ColumnHeaderBand1;

Colum_Name[Colum_Num].Frame.DrawTop:=true;

Colum_Name[Colum_Num].Frame.DrawBottom:=true;

Form_rep.ColumnHeaderBand1.Height:=40;

Colum_Name[Colum_Num].Height:=40;

Colum_Name[Colum_Num].Font.Height:=-14;

Colum_Name[Colum_Num].Font.Name:='黑體';

Colum_Name[Colum_Num].Top:=0;

Colum_Name[Colum_Num].Alignment:=taCenter;

Colum_Name[Colum_Num].AutoStretch:=false;

//畫表格線

Colum_Name[Colum_Num].Frame.Style:=psSolid;

Colum_Name[Colum_Num].Frame.Width:=1;

Colum_Name[Colum_Num].Frame.DrawRight:=true;

Colum_Name[Colum_Num].Frame.DrawBottom:=true;

if Colum_Num=0 then

begin

Colum_Name[Colum_Num].Frame.DrawLeft:=true;

end;

//將記錄RRep_Col_Sum_store中的資訊賦給Colum_Name

Colum_Name_list:=TStringList.Create;

Colum_Name_list.Add(Rep_Col_Sum_store.Caption_array[Colum_Num]);

Colum_Name[Colum_Num].Lines:=Colum_Name_list;

Colum_Name[Colum_Num].Width:=Rep_Col_Sum_store.Width_array[Colum_Num];

Colum_Name[Colum_Num].Visible:=true;

//計算左邊界

if Colum_Num>0 then

Colum_Name[Colum_Num].Left:=Colum_Name[Colum_Num-1].Left+Colum_Name[Colum_Num-1].Width

else

Colum_Name[Colum_Num].Left:=0;

end;

說明:此處採用TQRRichtext控制元件是因為當名稱過長時,TQRRichtext控制元件可以自動換行。

procedure DynCreat_TQRDBText(Colum_Num:integer;Colum_Height:integer;DataSet_Name:TQuery);//動態建立TQRDBText控制元件,此控制元件用來顯示每列的值

begin

Colum_Data[Colum_Num]:=tQRDBText.Create(application);

Colum_Data[Colum_Num].Parent:=Form_rep.DetailBand1;

//設定資料集

Colum_Data[Colum_Num].DataSet:=DataSet_Name;

//將陣列Colum_Data.DateField屬性設定為C_Rep_Col_Sum_store中的欄位名

Colum_Data[Colum_Num].DataField:=Rep_Col_Sum_store.DataFiled_array[Colum_Num];

Colum_Data[Colum_Num].Width:=Rep_Col_Sum_store.Width_array[Colum_Num];

Colum_Data[Colum_Num].Height:=Colum_Height;

Form_rep.DetailBand1.Height:=Colum_Height;

Colum_Data[Colum_Num].Top:=0;

Colum_Data[Colum_Num].AutoSize:=false;

Colum_Data[Colum_Num].AutoStretch:=false;

Colum_Data[Colum_Num].WordWrap:=false;

Colum_Data[Colum_Num].Visible:=true;

//畫表格線

Colum_Data[Colum_Num].Frame.Style:=psSolid;

Colum_Data[Colum_Num].Frame.DrawRight:=true;

Colum_Data[Colum_Num].Frame.DrawBottom:=true;

if Colum_Num=0 then

Colum_Data[Colum_Num].Frame.DrawLeft:=true;

//計算左邊界

if Colum_Num>0 then

Colum_Data[Colum_Num].Left:=Colum_Data[Colum_Num-1].Left+Colum_Data[Colum_Num-1].Width

else

Colum_Data[Colum_Num].Left:=0;

end;

procedure DynCreat_TQuery(Inifile_Name:Tinifile);//動態設定TQuery控制元件的SQL語句

var

Sql_command:string;

begin

Flag_CreatQuery:=false;

Sql_command:=Inifile_Name.Readstring('QureyData','Sql_Command','');

Form_rep.REP_QUERY.Close;

Form_rep.REP_QUERY.SQL.Clear;

Form_rep.REP_QUERY.SQL.Append(Sql_command);

if not Form_rep.REP_QUERY.Prepared then

Form_rep.REP_QUERY.Prepare;

try

Form_rep.REP_QUERY.ExecSQL;

Form_rep.REP_QUERY.Active:=true;

Form_rep.REP_QUERY.AutoCalcFields:=true;

Flag_CreatQuery:=true;

except

Application.MessageBox('SQL語句錯誤!','系統提示',MB_ICONWARNING);

Flag_CreatQuery:=false;

end;

end;

procedure Form_rep_init();

vari_count:integer;

Rep_IniFile:Tinifile;//開啟的INI檔名

Col_Height:integer;

Flag_Sum:boolean;

begin

Rep_IniFile:=Open_IniFile);//開啟臨時檔案檔案

Read_Rep_Summary(Rep_IniFile);//讀取報表的摘要資訊

Read_Col_Summary(Rep_IniFile);//讀取報表列的摘要資訊

//根據讀取的報表的各項引數,動態建立報表控制元件

withForm_repdo

begin

Flag_CreatTQRExpr:=false;//表示現在還沒有建立TQRExpr控制元件

DynCreat_TQuery(Rep_IniFile);

if Flag_CreatQuery then

begin

QRLabel_title.Caption:=rep_Summary.Title;

QRLabel_Header.Caption:=rep_Summary.Title;

//設定頁面資訊

QuickRep.Page.Orientation:=rep_Summary.Orientation;

QuickRep.Page.PaperSize:=rep_Summary.Page;

SetLength(Colum_Data,rep_Summary.Columns);

SetLength(Colum_Name,rep_Summary.Columns);

fori_count:=0 to rep_Summary.Columns-1 do

begin

DynCreat_TQRRichtext(i_count);//動態建立TQRRichtext控制元件

DynCreat_TQRDBRichText(i_count,Col_Height,Form_rep.REP_QUERY)//動態建立TQRDBText控制元件

end;

//關閉rep_x.ini檔案

Rep_IniFile.Destroy();

end

else

Form_rep.Close;

end;

end;

procedure TForm_rep.QuickRepAfterPreview(Sender: TObject);//瀏覽完畢,釋放所有建立的元件

var i_count:integer;

begin

for i_count:=0 to rep_Summary.Columns-1 do

begin;

Colum_Name[i_count].free;

Colum_Data[i_count].free;

end;

end;

end.

三、需要注意的問題

----QuickReport中繪製表格線,除了文中提到的方法外,還可以使用QRShape控制元件。但不管使用那種方法,都要仔細計算,要根據列印的效果反覆調整,才能保證表格線不產生偏差。

----所有動態建立的控制元件使用過後,一定要釋放掉。

----臨時檔案在使用過後,也要清空。

----上文所示程式僅僅是筆者為了說明思路而簡化的例程。在筆者實際的應用中,所有用到的控制元件(包括表單Form,都是動態生成的。而且在生成報表時,還可以動態生成統計資訊,插入圖表等,有興趣的朋友可以發信到 和我一起討論細節。