引言
上回負責報表這塊,說不能再像以前的項目一樣的做報表了,以前項目300多張報表,一張一張的畫,一張一張的寫存儲過程,工作量大啊,當然現在的項目報表沒有那麼多。這麼說我的工作就要是要節省工作量,我經過分析,得出兩個結論:第一:報表的資料源得由程式員自己去取來,短時間想做個像報表設計器那樣能根據複雜的業務而“制作”資料源是不可能的;第二:報表的布局複雜多變,即使減少工作量,也無法避免對一個複雜變量的指派工作。
開始思考
我們的每張報表分頁頭資料部分,明細資料部分,頁腳資料部分三塊。頁頭資料和頁腳資料都隻是一條記錄,每個字段的值都顯示在它的标題後面,如:姓名:×××。明細資料比較多,而且顯示格式像下面這樣:
标題1
标題2
标題3
1
a
張
2
b
李
它們每行顯示的列數多少都是可以自定義的,如果定義的頁頭資料的列數是3,而頁頭資料有5個字段,多出來的就要顯示在下一行;同時每個字段占用的列數也是可以自定義的,假如頁頭資料有5個字段,每字段分别占用1、2、1、1、1列,每行顯示3列,那麼它就會顯示成:
姓名:×××
住址:*****************
性别:男
籍貫:××
出生年月:×××
/// <summary>
/// 頁标頭列數(預設3)
/// </summary>
private int mHeaderTableColumnCount = 3;
/// 明細表列數(預設10)
private int mDetailTableColumnCount = 10;
/// 頁腳列數(預設5)
private int mFooterTableColumnCount = 5;
我想其中最難的就是每個要顯示的字段的位置大小問題和用什麼資料結構存儲這樣的布局問題,我用了一個三維數組,它的說明如下:
/// 表示報表布局的數組[i][j][k]
/// i=0:頁頭布局,i=1:明細布局,i=2:頁腳布局
/// j:按順序顯示的字段(j=0:報表标題字段)
/// k=0:字段名,k=1:标題,k=2:列跨度,k=3:格式化字元串(可選)
public string[][][] ReportLayout = null;
所有資料存儲在一個DataSet中,DataSet裡的第一個Table是頁頭資料,第二個是明細資料,第三個是頁腳資料。
/// 報表資料源,0頁頭表1明細表2頁腳表
public DataSet mDs = null;
開始工作
接下來就是用程式定義各個XRTable(表)、XRTableRow(行)、XRTableCell(列),并根據ReportLayout數組的值設定它們的位置和大小等等屬性。這裡就放上對明細資料布局的代碼(其中一直用ReportLayout[1],這裡的1就是指的明細,如果是頁頭資料就是0了)。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-YWan5yY5IzYjJjYkBjNmZGMyYmNlNTZiZmY2kDOkhDZ4UGOj9CXwMzLcdDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLzM3Lc9CX6MHc0RHaiojIsJye.gif)
if
(mReportLayout.Length
>
)
{
columnWidth = PageWidth / mDetailTableColumnCount;
tempHeight = 0;
for (tempCount = 0, i = 0; i < ReportLayout[1].Length; i++)
tempCount += Convert.ToInt32(ReportLayout[1][i][2]);
XRTable detailTable = new XRTable();
detailTable.Location = new System.Drawing.Point(0, tempHeight);
detailTable.Size = new System.Drawing.Size(PageWidth,
mDetailRowHeight * (tempCount + mDetailTableColumnCount - 1) / mDetailTableColumnCount);
detailTable.Borders = (DevExpress.XtraPrinting.BorderSide)((DevExpress.XtraPrinting.BorderSide.Left | DevExpress.XtraPrinting.BorderSide.Right) | DevExpress.XtraPrinting.BorderSide.Bottom);
for (j = 0, i = 0; j < (tempCount + mDetailTableColumnCount - 1) / mDetailTableColumnCount; j++)
XRTableRow r = new XRTableRow();
r.Size = new System.Drawing.Size(PageWidth, mDetailRowHeight);
for (int k = 0; i < mReportLayout[1].Length && (k + Convert.ToInt32(ReportLayout[1][i][2])) <= mDetailTableColumnCount; k += Convert.ToInt32(ReportLayout[1][i][2]), i++)
XRTableCell c = new XRTableCell();
c.Size = new System.Drawing.Size(columnWidth * Convert.ToInt32(ReportLayout[1][i][2]), mDetailRowHeight);
c.Font = new System.Drawing.Font("宋體", 9F);
c.TextAlignment = (ReportLayout[1][i][0].Substring(0, 1) == "N" || ReportLayout[1][i][0].Substring(0, 1) == "n") ? DevExpress.XtraPrinting.TextAlignment.MiddleRight : DevExpress.XtraPrinting.TextAlignment.MiddleLeft;
c.DataBindings.AddRange(new DevExpress.XtraReports.UI.XRBinding[]
new DevExpress.XtraReports.UI.XRBinding("Text", mDs.Tables[1], ReportLayout[1][i][0], ReportLayout[1][i].Length >= 4 ? ReportLayout[1][i][3] : string.Empty) });
r.Cells.AddRange(new DevExpress.XtraReports.UI.XRTableCell[]
{ c });
c.Tag = ReportLayout[1][i][2];
}
detailTable.Rows.AddRange(new DevExpress.XtraReports.UI.XRTableRow[]
{ r });
}
mReport.Detail.Controls.AddRange(new DevExpress.XtraReports.UI.XRTable[]
{ detailTable });
}
這裡是資料綁定(c.DataBindings),如果是頁頭裡面的話就應該是:
c.Text = mReportLayout[0][i][1] + ": " + mDs.Tables[0].Rows[0][mReportLayout[0][i][0]];
對于明細還應該在頁頭裡面加個XRTable,來放置标題,這個就不說了,根據上面的代碼很容易了解怎麼做。
還有分組和合計,也用數組來存儲它們的布局:
/// 表示統計字段的布局[i][j]
/// i:按順序顯示的字段
/// j=0:字段名,j=1:列跨度,j=2:彙總函數,j=3:格式化字元串(可選)
public string[][] TotalLayout = null;
/// 表示分組字段的布局[i][j][k]
/// i:第i個分組
/// j:每次分組要顯示的字段
/// k=0:字段名,k=1:列跨度,k=2:彙總函數,k=3:格式化字元串(可選)
public string[][] GroupLayout = null;
根據注釋,同上面的那段代碼的一個道理,隻是要對每個XRTableCell加下面的代碼:
if (GroupLayout[i][j].Length >= 3)
XRSummary Summary = new XRSummary();
Summary.Func = (DevExpress.XtraReports.UI.SummaryFunc)Enum.Parse(typeof(DevExpress.XtraReports.UI.SummaryFunc), GroupLayout[i][j][2], true);
Summary.FormatString = GroupLayout[i][j].Length >= 4 ? GroupLayout[i][j][3] : string.Empty;
Summary.Running = SummaryRunning.Group;//如果是合計則應該是SummaryRunning.Report
c.Summary = Summary;
}
在做的過程中還發現單元格的線對不齊的事情,總是相差1個象素,後來得到同僚的建議,每次定義一個XRTable就計算它的位置的大小把它設定好,就想現在上面的代碼那樣,而不是在将該XRTable中的XRTableRow和XRTableCell都定義完了也設定好它們的屬性了再來設定XRTable的屬性,這樣就對齊了,看來XRTableRow和XRTableCell的位置大小屬性都是根據XRTable來的,剛剛定義的XRTable它的大小應該是100px,綁定到它的XRTableRow和XRTableCell會根據自身的大小按比例縮放使總大小為100px,之後你再設定XRTable的大小到1000px,它們再按原來的比例縮放到總大小為1000px,這樣一來二去就有點誤差了,呵呵,這也是很符合道理的。
實施
以後我們做報表就隻要定義個我的類(假如類名是:Report)的一個執行個體(rpt),定義的同時給構造函數傳參(我在這個類中定義了個這樣的構造函數,參數中包含了我們要對報表布局以及資料源DataSet的所有變量),然後一個rpt.ShowPreview();就顯示報表了(我的類中的有這個方法,這個方法調用了上面說的設定報表的布局等代碼,之後用DevExpress.XtraReports報表本身的ShowPreview()來顯示報表),一切OK。下面這張圖PS了下。