資料包表開發技巧:自動為資料包表新增【小計】、【總計】行

zhuweisky發表於2018-04-18

  在開發ERP系統的資料包表時,幾乎都是需要看到【小計】、【總計】這樣的彙總資料的,在資料包表的顯示列表中,最下面的一行通常就是【小計】或者【總計】的彙總行。如果手動為每個報表都增加彙總行,那也是一份不小的工作量。

  所以,如果能自動為每個資料包表自動新增【小計】、【總計】彙總行,那將可以節省不少的開發時間。本文將給出實現這種方案的思路原理以及原始碼。

  本文中,報表資料的顯示使用的是WinForm的DataGridView控制元件,如果是Web專案,其原理和思路是一樣的。

  舉個栗子,零售店POS機上查詢銷售單的效果如下圖所示:

  

  在報表的最下面有一行【總計】,對數量和金額項進行了彙總。

  下面,我們來詳細講解是如何自動為其新增【總計】這一彙總行的。

 

一.基本原理

1.一般而言,資料包表的現實的核心資料是DataGridView繫結的一個列表List<T>,每一個T物件對應著報表中的一行資料。

2.在資料包表中,【小計】、【總計】所對應的彙總行與上面列表中的其它普通行,是有區別的,所以,它們對應的T物件也是有區別的。

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的文字設定為【小計】或【總計】。

 

二.思路實現

  就上面描述的思路來看,稍微有點難度的地方在於最後兩點,下面我們就詳細講解一下。

1.為List<T>增加彙總行

  增加彙總行,所用到的主要技術就是反射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列表中,作為最後一個物件。

2.繫結到DataGridView 

    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)有些資料的彙總,可能是要進行絕對值的彙總,那又該如何處理?

         這些問題,我們都將在下篇文章的示例中一併解決,敬請期待。

 

相關文章