在开发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)有些数据的汇总,多是要进行绝对值的汇总,那又该如何处理?
这些问题,咱们都将在下篇文章的示例中一并解决,敬请期待。