在開發ERP系統的數據報表時,幾乎都是須要看到【小計】、【總計】這樣的彙總數據的,在數據報表的顯示列表中,最下面的一行一般就是【小計】或者【總計】的彙總行。若是手動爲每一個報表都增長彙總行,那也是一份不小的工做量。html
因此,若是能自動爲每一個數據報表自動添加【小計】、【總計】彙總行,那將能夠節省很多的開發時間。本文將給出實現這種方案的思路原理以及源碼。this
本文中,報表數據的顯示使用的是WinForm的DataGridView控件,若是是Web項目,其原理和思路是同樣的。spa
舉個栗子,零售店POS機上查詢銷售單的效果以下圖所示:code
在報表的最下面有一行【總計】,對數量和金額項進行了彙總。orm
下面,咱們來詳細講解是如何自動爲其添加【總計】這一彙總行的。htm
1.通常而言,數據報表的現實的核心數據是DataGridView綁定的一個列表List<T>,每個T對象對應着報表中的一行數據。對象
2.在數據報表中,【小計】、【總計】所對應的彙總行與上面列表中的其它普通行,是有區別的,因此,它們對應的T對象也是有區別的。blog
3.咱們經過T的一個名爲 IsStatistics 的bool屬性來區別普通行與彙總行。若是IsStatistics爲true,表示其對應的行就爲彙總行。接口
4.爲了達到上面描述的這一目的,咱們讓T必須實現接口IStatisticabled這個接口。開發
public interface IStatisticabled { bool IsStatistics { set; get; } }
5.爲報表添加彙總行,實際上就是向List<T>列表中添加一個 IsStatistics 爲true的 T 對象。
6.而後,經過反射來統計須要彙總的那些項(即T的某些屬性),而後將彙總獲得的結果賦值給彙總T對象對應的屬性。
7.在綁定到DataGridView時,經過判斷列表中 T 對象的IsStatistics屬性,若是爲true,就將該行Row對應的RowHeader的文本設置爲【小計】或【總計】。
就上面描述的思路來看,稍微有點難度的地方在於最後兩點,下面咱們就詳細講解一下。
增長彙總行,所用到的主要技術就是反射Relection。
/// <summary> /// 爲數據報表增長一個彙總行。 /// </summary> /// <typeparam name="T">報表記錄對象的類型</typeparam> /// <param name="list">數據行對象列表</param> /// <param name="statColumns">須要進行統計的列</param> public static void AddSumRow<T>(List<T> list ,params string[] statColumns) where T :IStatisticabled, new() { T sum = new T(); sum.IsStatistics = true; foreach (string column in statColumns) //針對每個彙總項 { double total = 0; foreach (T t in list) //統計 { object val = ReflectionHelper.GetProperty(t, column); total += double.Parse(val.ToString()); } object newTotal = TypeHelper.ChangeType(typeof(T).GetProperty(column).PropertyType, total); ReflectionHelper.SetProperty(sum, column, newTotal); } list.Add(sum); }
(1)爲了能夠動態new一個統計行,必需要讓T有 new() 這個約束。
(2)將統計行的 IsStatistics 標記設置爲true。
(3)針對每個統計項進行統計:經過反射拿到每一行該項的屬性值,並轉換成double類型(由於double兼容了全部的數值類型),進行累加,而後將累加的結果轉換成正確的類型,最後,賦值給統計行對應的屬性。
(4)將統計行添加到list列表中,做爲最後一個對象。
List<RetailOrder> list = this.GetOrderList(); AddSumRow(list, new string[] { "Count", "Money" }); this.dataGridView1.DataSource = list; DataGridViewCellStyle style = new DataGridViewCellStyle(); style.Font = new Font(this.dataGridView1.DefaultCellStyle.Font.Name, this.dataGridView1.DefaultCellStyle.Font.Size, FontStyle.Bold); this.dataGridView1.RowHeadersDefaultCellStyle = style; this.dataGridView1.Rows[list.Count - 1].HeaderCell.Value = "總計"; this.dataGridView1.Rows[list.Count - 1].DefaultCellStyle = style;
(1)經過GetOrderList方法獲取到銷售單列表後,咱們經過AddSumRow方法爲其添加一個彙總行,並對【Count】、【Money】進行彙總。
(2)將包含了彙總行的列表綁定到DataGridView。
(3)將最後一行的RowHeader的Cell的value設置爲【總計】 。
(4)將【總計】行的全部數據顯示都變成粗體。
1.本文Demo源碼:DataReportsSample.rar
2.該Demo中用到了ESBasic的反射幫助類ReflectionHelper,這裏能夠下載個人開源基礎類庫:ESBasic 源碼。
本文示例是一個【小計】、【總計】彙總功能的基礎展現,實際應用中,一般還會牽涉到如下問題:
(1)當報表數據存在分頁時,通常會同時存在【小計】、【總計】行,【小計】是本頁的彙總,【總計】是全部業的彙總。
(2)當DataGridView綁定的某些列對應着Entity的某個只讀屬性,而且這個只讀屬性不會返回null和string.Empty時,彙總行的這一列就會有文字顯示(這是一個非彙總行,目標單元格應該是空的),要如何處理?
(3)當DataGridView的某一些是一個操做列,即對應着DataGridViewLinkColumn,若是讓彙總行的目標單元格中不出現Link,而是留空了?
(4)有些數據的彙總,多是要進行絕對值的彙總,那又該如何處理?
這些問題,咱們都將在下篇文章的示例中一併解決,敬請期待。