一、問(wèn)題的提出
---- Delphi作為強(qiáng)大的數(shù)據(jù)庫(kù)開(kāi)發(fā)工具,正被愈來(lái)愈多的編程人員所采用,"聰明的程序員用Delphi"更形象生動(dòng)的道出廣大程序員的心聲,但這并不意味著所有功能的實(shí)現(xiàn)都非常容易,例如,筆者在開(kāi)發(fā)軍隊(duì)的某個(gè)信息系統(tǒng)中,就在為數(shù)據(jù)分析模塊中DecisionGrid1控件的數(shù)據(jù)進(jìn)行報(bào)表輸出時(shí)走了不少的彎路。廣大的Delphi的愛(ài)好者在今后的學(xué)習(xí)或工作中也有可能會(huì)遇到類(lèi)似的問(wèn)題,而在許多參考書(shū)中,很少有甚至沒(méi)有關(guān)于它們的解決方法,于是,我想花費(fèi)一點(diǎn)時(shí)間把它整理出來(lái),以供大家參考。本文中報(bào)表動(dòng)態(tài)生成的公用模塊具有很大的靈活性和易操作性,其中的思路、實(shí)現(xiàn)的功能和通用性等方面的優(yōu)缺點(diǎn)就由大家看了本文后自有定論。
二、建立報(bào)表的動(dòng)態(tài)輸出公用模塊
---- 下面,結(jié)合公司人事管理信息系統(tǒng)說(shuō)明其實(shí)現(xiàn)的方法和技術(shù)。
---- 1、基本思路:首先從DecisionGrid1中獲得報(bào)表所需數(shù)據(jù),放到二維數(shù)組PA中,然后在C:\DataWork中動(dòng)態(tài)創(chuàng)建一個(gè)數(shù)據(jù)表tjb.dbf,存放報(bào)表數(shù)據(jù),最后用T able1與tjb.dbf相連接,以后工作就與一般的動(dòng)態(tài)輸出報(bào)表(如查詢報(bào)表)相類(lèi)似,在這里我就無(wú)須贅述了。
---- 2、建立窗體文件:放入六個(gè)用于數(shù)據(jù)分析的常用控件DecisionQuery1、DecisionSource1、DecisionCube1、DecisionGraph1、DecisionPivot1、DecisionGrid1,設(shè)置DecisionSource1的decisionCube屬性為decisionCube1,decisionCube1的Dataset屬性為decisionQuery1、decisionQuery1的DatabaseName屬性為c:\datawork;一個(gè)Table1控件,用于連接數(shù)據(jù)表tjb.dbf;一個(gè)QuickRep1控件,用于數(shù)據(jù)的報(bào)表輸出;兩個(gè)Button1和Button2控件,其Caption分別設(shè)為"報(bào)表輸出"和"返回"。分別設(shè)置decisionCube1的Dataset屬性為decisionQuery1、decisionQuery1的DatabaseName屬性為c:\datawork.。
---- 3、單元文件的主要控件代碼 Button1控件的代碼如下(定義變量部分略),主要分以下8個(gè)功能塊來(lái)加以說(shuō)明:
---- ⑴刪除同名或上一次建立的數(shù)據(jù)表
if FileExists('c:\DataWork\tjb.dbf') then deletefile('c:\ DataWork \tjb.dbf');
---- ⑵根據(jù)DecisionGrid1控件的cells屬性,獲得報(bào)表所需數(shù)據(jù),并將其默認(rèn)的'Sum'值漢化成'總計(jì)'、'合計(jì)'、'小計(jì)'以符合漢語(yǔ)的習(xí)慣要求,所求得的數(shù)據(jù)存放于二維數(shù)組PA中 for i:=1-DecisionGrid1.FixedCols to DecisionGrid1. ColCount-DecisionGrid1.FixedCols-1 do for j:=0-DecisionGrid1.FixedRows to DecisionGrid1. RowCount-DecisionGrid1.FixedRows-1 do begin pa[i,j]:=DecisionGrid1.cells[i,j]; //處理DecisionGrid1控件中固定列的值為'Sum'的數(shù)據(jù)項(xiàng) if ((i=1-DecisionGrid1.fixedcols) and (pa[i,j]='Sum')) then pa[i,j]:='總 計(jì)' else if ((i = -1) and (pa[i,j]='Sum')) then pa[i,j]:='小 計(jì)' else if ((i<-1) and (i>1-DecisionGrid1 .FixedCols) and (pa[i,j]='Sum')) then pa[i,j]:='合 計(jì)'; //處理DecisionGrid1控件中固定行的值為'Sum'的數(shù)據(jù)項(xiàng) if (pa[i,j]='Sum' ) and (j=-1) then pa[i,j]:='總 計(jì)'; end;
---- ⑶用T able1動(dòng)態(tài)創(chuàng)建數(shù)據(jù)表tjb.dbf Table1.Active:=false; with Table1 do begin DatabaseName := 'c:\DataWork'; TableName := 'tjb'; TableType := ttDBase; with FieldDefs do begin Clear; for i:=1 to 40 do Add(IntToStr(i),ftString,30, False); end; CreateTable; end; //下面將DecisionGrid1控件中的數(shù)據(jù)放入數(shù)據(jù)表中 Table1.Active:=true; for j:=1-DecisionGrid1.FixedRows to DecisionGrid1 .RowCount-DecisionGrid1.FixedRows-1 do begin K:=0; Table1.Insert; for i:=1-DecisionGrid1.FixedCols to DecisionGrid1 .ColCount-DecisionGrid1.FixedCols-1 do begin Table1.Fields[K].AsString:=pa[i,j]; K:=K+1; end; Table1.Post; Table1.Next; end;
---- ⑷下面代碼確定輸出報(bào)表的每列寬度 SetLength(M,DecisionGrid1.ColCount);//動(dòng)態(tài)設(shè)置數(shù)組 copy(M,1-DecisionGrid1.FixedCols,DecisionGrid1 .ColCount-DecisionGrid1.FixedCols-1); //重新設(shè)置動(dòng)態(tài)數(shù)組的起始位置 for i:=1-DecisionGrid1.FixedCols to DecisionGrid1 .ColCount-DecisionGrid1.FixedCols-1 do begin M[i]:=0; for j:=1-DecisionGrid1.FixedRows to DecisionGrid1 .RowCount-DecisionGrid1.FixedRows-1 do IF M[i]< Length (Trim (PA[I,J]))*8 THEN M[i]:= Length (Trim (PA[I,J]))*8; end;
---- ⑸如果要求輸出報(bào)表的列寬相同(除DecisionGrid1控件的固定列,下同),可將數(shù)據(jù)項(xiàng)的最大列寬作為輸出報(bào)表的列度,如果不要求,可跳過(guò)下面代碼 max:=0; for i:=0 to DecisionGrid1.ColCount -DecisionGrid1.FixedCols-1 do if m[i]>max then max:=m[i]; for i:=0 to DecisionGrid1.ColCount -DecisionGrid1.FixedCols-1 do m[i]:=max; ZK:=0;//報(bào)表總寬 for i:=1-DecisionGrid1.FixedCols to DecisionGrid1 .ColCount-DecisionGrid1.FixedCols-1 do ZK:=ZK+M[i]+1;
---- ⑹判斷報(bào)表的寬度,超寬?橫向報(bào)表?還是縱向報(bào)表? if ZK>1123 then begin Application.MessageBox('報(bào)表超寬, 請(qǐng)調(diào)整再輸出!','警告', 1);//輸出對(duì)話框 exit; end else if ZK>794 then QuickRep.Page.Orientation:=poLandscape //橫向 else QuickRep.Page.Orientation:=poPortrait;//縱向
---- ⑺以下代碼完成了動(dòng)態(tài)數(shù)據(jù)報(bào)表,與一般的動(dòng)態(tài)輸出報(bào)表功能相類(lèi)似, for i:=1 to QuickRep.Bands.TitleBand. ControlCount DO//取消系統(tǒng)對(duì)控件的控制,下同 QuickRep.Bands.TitleBand.RemoveControl (QuickRep.Bands.TitleBand.Controls[0]); for i:=1 to QuickRep.Bands.DetailBand.ControlCount DO QuickRep.Bands.DetailBand.RemoveControl (QuickRep.Bands.DetailBand.Controls[0]); SetLength(QRShape,DecisionGrid1.ColCount) ; //動(dòng)態(tài)設(shè)置數(shù)組 SetLength(QRDBText, DecisionGrid1.ColCount) ; //動(dòng)態(tài)設(shè)置數(shù)組 K:=0;//動(dòng)態(tài)生成對(duì)象的數(shù), Lx:=(QuickRep.Width-ZK)DIV 2;//生成對(duì)象的左坐標(biāo) //報(bào)表的動(dòng)態(tài)生成 For j:=1-DecisionGrid1.FixedCols to DecisionGrid1 .ColCount-DecisionGrid1.FixedCols-1 do begin QRShape[K]:=TQRSHAPE.Create(tj1);//自定義對(duì)象的創(chuàng)建(下同) QRShape[K].Parent:=QuickRep.Bands .DetailBand;// 自定義對(duì)象的父類(lèi)對(duì)象(下同) QRDBText[K]:=TQRDBText.Create(tj1); QRDBText[K].parent :=QuickRep.Bands.DetailBand; QRShape[K].LEFT:=Lx;//生成對(duì)象的左坐標(biāo) QRShape[K].WIDTH:=M[J]+2; //生成對(duì)象的寬度 QRShape[K].HEIGHT:=QuickRep.Bands .DetailBand.Height+1; //生成對(duì)象的高度 QRShape[K].TOP:=-1; //生成對(duì)象的縱坐標(biāo) QRDBText[K].WIDTH:=QRShape[K].WIDTH-10; QRDBText[K].Left :=QRShape[K].LEFT+1; QRDBText[K].HEIGHT:=QRShape[K].Height div 2; QRDBText[K].Top :=QRDBText[K] .Height div 2+QRShape[K].Top; QRDBText[K].AutoSize:=false; QRDBText[K].Alignment:=taCenter; //生成對(duì)象居中 QRDBText[K].DataSet:=Table1; QRDBText[K].DataField:=IntToStr(k+1); Lx:=Lx+M[J]+1; Inc(k); end; //動(dòng)態(tài)生成報(bào)表的標(biāo)題 Caption := TQRLabel.Create(tj1); Caption.Parent := QuickRep.Bands.TitleBand; Caption.Alignment:=taCenter;//標(biāo)題居中 Caption.Width:= Length (Trim (ptitle))*8; //標(biāo)題的寬度 Caption.Left:=(QuickRep.Width- Length (Trim (ptitle))*8)div 2; //標(biāo)題的左坐標(biāo) Caption.Height:=QuickRep.Bands .TitleBand.Height-1; //標(biāo)題的高度 Caption.Top:= 0; Caption.Caption:=ptitle; //標(biāo)題的名稱(chēng) ,ptitle為調(diào)用該公用模塊時(shí)的輸入?yún)?shù) QuickRep.DataSet:=Table1; QuickRep.Preview; ⑻動(dòng)態(tài)生成對(duì)象的內(nèi)存釋放 k:=0; for j:=1-DecisionGrid1.FixedCols to DecisionGrid1 .ColCount-DecisionGrid1.FixedCols-1 do begin QRShape[K].Free; QRDBText[K].Free; inc(k); end; Caption.Free; Table1.Active:=false;//關(guān)閉數(shù)據(jù)表 end;
---- 以上程序是在Delphi 4.0中調(diào)試通過(guò),其數(shù)據(jù)文件應(yīng)放在C:\DataWork,類(lèi)型為DB或DBF。 三、應(yīng)注意以下幾個(gè)問(wèn)題
---- 1、QuickRep1的Bands中的HasColumnHeader、HasDetail、HasTitle三個(gè)屬性必須設(shè)置為true;
---- 2、不能忘記公用模塊中QuickRep對(duì)象的DataSet屬性設(shè)置,即源代碼中的QuickRep.DataSet:=Table1語(yǔ)句;
---- 3、動(dòng)態(tài)生成組件的寬度計(jì)算必須放在定義其字體屬性完成后進(jìn)行;
---- 4、另外,動(dòng)態(tài)數(shù)組給定的內(nèi)存(即數(shù)組容量)以及指定動(dòng)態(tài)數(shù)組的起始位置(不一定為0,根據(jù)DecisionGrid1控件的固定列確定)很重要,因?yàn)橐环矫娈?dāng)數(shù)據(jù)庫(kù)很大時(shí)它會(huì)大大減少內(nèi)存的消耗,另一方面便于操作該數(shù)組,大大增強(qiáng)了程序的靈活性和通用性。
---- 5、如果讓QRDBText控件的數(shù)據(jù)居中,必須先設(shè)置其AutoSize屬性為false,然后才能設(shè)置其Alignment屬性為taCenter。這一點(diǎn)往往容易忽略,直接設(shè)置Alignment屬性為taCenter,往往達(dá)不到數(shù)據(jù)居中的目的。
四、結(jié)束語(yǔ)
---- 當(dāng)然,由于客戶對(duì)數(shù)據(jù)報(bào)表的可能特殊要求,此公用模塊或許不能完全解決。但是,作為公用模塊,能實(shí)現(xiàn)實(shí)現(xiàn)代碼的重復(fù)利用,提高我們開(kāi)發(fā)程序的效率,當(dāng)然可以在此模塊的基礎(chǔ)上進(jìn)行一些修改或補(bǔ)充,以滿足大多數(shù)用戶的要求,用以下兩點(diǎn)加以說(shuō)明。
---- 1、如果要對(duì)數(shù)據(jù)表的字段進(jìn)行動(dòng)態(tài)選擇輸出,則可以將動(dòng)態(tài)產(chǎn)生的數(shù)據(jù)表字段傳遞到另一個(gè)窗體進(jìn)行輸出選擇,然后根據(jù)你所選擇的字段進(jìn)行報(bào)表輸出,源程序幾乎無(wú)須修改即可實(shí)現(xiàn)。
---- 2、若要改變數(shù)據(jù)分析的內(nèi)容(即直方圖的維)時(shí),只須修改SQL語(yǔ)句即可,動(dòng)態(tài)、靜態(tài)均可,具體的操作也就無(wú)須我多言了。各位Delphi編程愛(ài)好者不妨一試。
|