程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 如何快速自動生成並定制報表

如何快速自動生成並定制報表

編輯:關於C++

在各種管理信息系統應用中,需要產生大量的報表,通常的做法是由編程人員一個個手工制作,工作效率較低;另外,用戶希望能夠將在應用程序裡查詢得到的結果生成報表以便打印。為了解決以上兩種問題,本文利用動態生成技術實現了快速自動產生報表,允許用戶手工對報表進行修飾,並將實現過程封裝成一個類。

1.設計思路

使用過C++ Builder或Delphi的編程人員知道,有一個TDBGrid控件,它能以表格的形式顯示和操作用戶查詢的數據記錄;而要制作一個可供打印的報表,則需要使用TQuickRep控件,在它上面增加TQRLabel、TQRDBText、TQRShape等控件,設置它們對應的數據集、數據字段等屬性,然後編排它們的位置,以表格或其它格式顯示出來供預覽和打印,這是一個很繁瑣的過程。有時,用戶希望能將查詢出來的顯示在TDBGrid控件的數據打印出來,按照以往的做法,就需要由編程人員按照TDBGrid的顯示內容手工設計報表。在這裡,本文利用動態生成技術,讀出TDBGrid的有關顯示信息,在TQuickRep控件裡動態生成相應的TQRLabel、TQRDBText、TQRShape等控件,設置各字段的標題和數據以及表格分割條。這是完全可行的,因為在C++ Builder裡所有的控件都可以由程序動態生成,不僅僅是在設計階段才產生的。另外,如果用戶對產生的報表表格布局不太滿意,本文提供了接口使用戶可對報表進行手工調整,調整表格的高度、寬度等布局,實現用戶對報表的一定程度的定制。

利用C++的封裝性特點,將自動產生並定制報表的實現封裝成一個新類TGridPrint,對外提供編程人員關心的公用接口,屏蔽了內部信息和具體實現,體現了面向對象的設計思想,為編程人員帶來方便。編程人員還可以在它基礎上進一步擴充功能。這樣設計的新類減輕了編程人員的工作量,同時為用戶提供了定制報表的接口,提高了報表的質量和用戶參與的積極性。

2.實現過程

自動產生並定制報表的實現過程包括自動產生和定制兩部分。新類的定義和實現分別在GridPrint.h GridPrint.cpp文件裡,另包含3個文件RepRst.h、RepRst.cpp、RepRst.dfm,它們是已產生的一個窗口FrmRepRst,在它裡面已增加一個TQuickRep控件,它的屬性Bands的各子屬性的值全為true。

2.1 自動產生報表

先定義一個表示表格某一列信息的結構,在報表裡一列有固定標題和顯示的數據文本兩種信息,為了能畫出表格,每一列固定標題欄和數據欄右邊分別增加一個分隔條。在類TGridPrint的構造函數裡,先根據傳入的TQuickRep *pSrcQuickRep(報表指針),TDBGrid * pSrcDBGrid(數據表格指針),TQRBand *SrcTitleBand1(報表中的總標題欄指針), TQRBand *SrcColumnHeaderBand1(報表中的字段標題欄指針),TQRBand * SrcDetailBand1(報表中的數據欄指針)參數設置類的私有變量。再動態生成並設置總標題文本、字段標題欄矩形框、數據欄矩形框的屬性。然後通過一個循環,讀出TDBGrid中各字段的標題和數據信息,動態生成報表中各字段的標題標簽控件、數據文本控件以及對應的表格分割豎條控件。在類的析構函數裡,刪除所有由構造函數動態生成的對象。類的打印預覽函數實現報表的打印預覽功能。其它的函數說明略。

自動生成報表類的定義(GridPrint.h)

#include <DBGrids.hpp> //包含的相關頭文件
#include <Db.hpp>
#include <Printers.hpp>
#include <QuickRpt.hpp>
#include <Qrctrls.hpp>
typedef struct tagFieldType{ //表示表格某一列信息的結構
AnsiString sTitle; //字段標題名稱
int iWidth; //表格單元的寬度
TQRLabel *pLabel; //字段標題控件
TQRDBText *pDBText; //顯示的數據控件
TQRShape *pShapeTitle, *pShapeData; //字段標題和數據的表格分隔條
} NEWFIELDTYPE;
class TGridPrint{
public:
TGridPrint(TQuickRep *pSrcQuickRep,TDBGrid * pSrcDBGrid,TQRBand *SrcTitleBand1,
TQRBand *SrcColumnHeaderBand1,TQRBand * SrcDetailBand1); //構造函數
~TGridPrint(); //析構函數
void DoPreview(); //報表的打印預覽
void SetPrntTitle(AnsiString sTitle); //手工設置表格的總標題
void SetColumnsWidth(int *ColumnsWidth); //手工設置表格各列寬度
void SetHeadRectHeight(int iHeight); //手工設置字段標題行的高度
void SetDetailRectHeight(int iHeight); //手工設置數據行的高度
private:
TDBGrid * pDBGrid; //將要顯示的DBGrid
TDataSet * pDataSet; //DBGrid對應數據集
TQuickRep * pQuickRep; //報表控件
TQRBand * TitleBand1; //報表的總標題欄
TQRBand * ColumnHeaderBand1; //報表的字段標題欄
TQRBand * DetailBand1; //報表的數據欄
TQRLabel * pTitleLabel; //總標題控件
TQRShape * pHeadRect, * pDetailRect; //整個字段標題欄、數據欄的表格矩形控件
int iHeadRectHeight, iDetailRectHeight; //對應表格矩形框的高度,它們寬度相同
int _iTotalWidth; //整個表格的總寬度
int _iIntClearance; //表格內部數據列到左表格的距離
int _iFieldCount; //將要打印的字段數目
NEWFIELDTYPE _arrayFieldType[40]; //支持到40個字段的打印
void AutoAdjustColumnsWidth(); //程序自動調整各列寬度
};

類的主要公用方法的實現(GridPrint.cpp)

TGridPrint::TGridPrint(TQuickRep * pSrcQuickRep,TDBGrid * pSrcDBGrid,TQRBand *SrcTitleBand1, TQRBand *SrcColumnHeaderBand1,TQRBand * SrcDetailBand1) //構造函數
{ int i,PreLeft;
pQuickRep = pSrcQuickRep; //根據傳入參數設置私有變量
pDBGrid = pSrcDBGrid;
pDataSet = pDBGrid->DataSource->DataSet;
pQuickRep->DataSet = pDataSet;
TitleBand1 = SrcTitleBand1;
ColumnHeaderBand1 = SrcColumnHeaderBand1;
DetailBand1= SrcDetailBand1;
PTitleLabel = new TQRLabel(pQuickRep); //生成並設置總標題標簽的屬性
pTitleLabel->Parent = TitleBand1;
pTitleLabel->Caption = "報表標題";
pTitleLabel->Left= (TitleBand1->Width - pTitleLabel->Width)/2;
memset(_arrayFieldType,0,sizeof(NEWFIELDTYPE)*40);
_iTotalWidth=0; //計算出表格各列單元寬度和整個表格的總寬度
for(i=0; i< pDBGrid->FieldCount;i++)
{ _arrayFieldType[i].iWidth= pDBGrid->Columns->Items[i]->Width;
_iTotalWidth += _arrayFieldType[i].iWidth; }
if(_iTotalWidth > TitleBand1->Width)
{ //如果原DBGird各列寬度和大於總標題欄寬度,就調整各列寬度
_iTotalWidth= TitleBand1->Width;
AutoAdjustColumnsWidth(); }
PreLeft= ( TitleBand1->Width - _iTotalWidth)/2; //使整個表格居中
pHeadRect= new TQRShape(pSrcQuickRep); //生成並設置字段標題欄的矩形框
pHeadRect->Parent= ColumnHeaderBand1;
pHeadRect->Left = PreLeft; pHeadRect->Top = 0;
pHeadRect->Width = _iTotalWidth; pHeadRect->Height= pHeadRect->Parent->Height;
pDetailRect= new TQRShape(pSrcQuickRep); //生成設置數據行的矩形框
pDetailRect->Parent= DetailBand1;
pDetailRect->Left = PreLeft; pDetailRect->Top = -1;
pDetailRect->Width = _iTotalWidth; pDetailRect->Height= pDetailRect->Parent->Height+1 ;
_iIntClearance= 1;
_iFieldCount= pDBGrid->FieldCount; //設置表格總列數
for(i=0; i< _iFieldCount;i++) //動態生成各字段
{ //該字段的固定標題欄
_arrayFieldType[i].pLabel= new TQRLabel(pQuickRep); //標題控件
_arrayFieldType[i].pLabel->Parent= ColumnHeaderBand1;
//字段標題名稱
_arrayFieldType[i].pLabel->Caption= pDBGrid->Columns->Items[i]->Title->Caption;
//字段標題的字體
_arrayFieldType[i].pLabel->Font= pDBGrid->Columns->Items[i]->Title->Font;
_arrayFieldType[i].pLabel->Alignment=
pDBGrid->Columns->Items[i]->Title->Alignment; //對齊方式
_arrayFieldType[i].pLabel->Left = PreLeft+_iIntClearance;
_arrayFieldType[i].pLabel->Width = _arrayFieldType[i].iWidth-2*_iIntClearance;
_arrayFieldType[i].pLabel->Height= _arrayFieldType[i].pLabel->Font->Height;
_arrayFieldType[i].pLabel->Top=
pHeadRect->Top+(pHeadRect->Height+_arrayFieldType[i].pLabel->Height)/2;
_arrayFieldType[i].pShapeTitle= new TQRShape(pQuickRep); //該字段右邊的分隔豎條
_arrayFieldType[i].pShapeTitle->Parent= ColumnHeaderBand1;
_arrayFieldType[i].pShapeTitle->Left = PreLeft + _arrayFieldType[i].iWidth;
_arrayFieldType[i].pShapeTitle->Top = 0;
_arrayFieldType[i].pShapeTitle->Width= 1;
if( i == pDBGrid->FieldCount-1) //最後一列的分隔豎條寬度為0
_arrayFieldType[i].pShapeTitle->Width= 0;
_arrayFieldType[i].pShapeTitle->Height=_arrayFieldType[i].pShapeTitle->Parent->Height;
//顯示的數據欄
_arrayFieldType[i].pDBText= new TQRDBText(pQuickRep); //該字段對應的文本控件
_arrayFieldType[i].pDBText->Parent = DetailBand1;
_arrayFieldType[i].pDBText->DataSet = pDataSet; //數據集
_arrayFieldType[i].pDBText->DataField = pDBGrid->Columns->Items[i]->FieldName; //字段名
_arrayFieldType[i].pDBText->Font = pDBGrid->Columns->Items[i]->Font; //字體
_arrayFieldType[i].pDBText->Alignment= pDBGrid->Columns->Items[i]->Alignment; //對齊方式
_arrayFieldType[i].pDBText->Left = PreLeft+_iIntClearance;
_arrayFieldType[i].pDBText->Width = _arrayFieldType[i].iWidth-2*_iIntClearance;
_arrayFieldType[i].pDBText->Height= _arrayFieldType[i].pDBText->Font->Height;
_arrayFieldType[i].pDBText->Top=
pDetailRect->Top+(pDetailRect->Height+_arrayFieldType[i].pDBText->Height)/2;
_arrayFieldType[i].pShapeData= new TQRShape(pQuickRep); //該數據右邊的分隔豎條
_arrayFieldType[i].pShapeData->Parent= DetailBand1;
_arrayFieldType[i].pShapeData->Left = PreLeft+_arrayFieldType[i].iWidth;
_arrayFieldType[i].pShapeData->Top = 0;
_arrayFieldType[i].pShapeData->Width= 1;
if( i== pDBGrid->FieldCount-1) //最後一列的分隔豎條寬度為0
_arrayFieldType[i].pShapeData->Width= 0;
_arrayFieldType[i].pShapeData->Height=_arrayFieldType[i].pShapeData->Parent->Height;
PreLeft= _arrayFieldType[i].pShapeTitle->Left; //下列字段的左位置
}
}
TGridPrint::~TGridPrint() //析構函數
{ delete pTitleLabel; delete pHeadRect; delete pDetailRect;
for(int i=0; i< pDBGrid->FieldCount;i++)
{ delete _arrayFieldType[i].pDBText; delete _arrayFieldType[i].pShapeData;
delete _arrayFieldType[i].pLabel; delete _arrayFieldType[i].pShapeTitle;
} }
void TGridPrint::DoPreview() //打印預覽函數
{ pQuickRep->Preview(); }

2.2 定制報表

類的公共接口提供有4個函數SetPrntTitle、SetColumnsWidth、SetHeadRectHeight、SetDetailRectHeight,分別是手工設置表格的總標題、表格各列寬度、字段標題行的高度和數據行的高度。編程人員可自己設計相關界面,供用戶來對報表的布局做進一步的修正,滿足用戶的要求,提高了報表的質量和用戶參與的積極性。實際上,這樣做將編程人員在報表設計階段的工作轉交給用戶來做,避免了原來設計好不能改變的問題。

2.3 調用方法

完成類TGridPrint的設計後,編程人員可以在需要的地方很方便地調用它。調用前注意包含兩個頭文件。調用時,需將下面構造函數裡的DBGrid1換成程序界面上實際使用的TDBGrid控件名字,其它不用改動。

#include "GridPrint.h"
#include "RepRst.h"
TFrmRepRst *pFrmRepRst= new TFrmRepRst(NULL);
TGridPrint *pGridPrint= new TGridPrint(pFrmRepRst->QuickRep1, DBGrid1, pFrmRepRst->TitleBand1, pFrmRepRst->ColumnHeaderBand1, pFrmRepRst->DetailBand1);
pGridPrint->DoPreview();
delete pGridPrint; delete pFrmRepRst;

3.結束語

在前人設計的基礎上,根據任務的需要,采用面向對象的設計方法,可設計出實現目標的新類,並能被繼承和擴充。本文只針對數據表格TDBGrid控件設計的,起拋磚引玉的作用,讀者可在上述思路的基礎上,針對其它格式的報表,進行自動生成報表的設計。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved