首页 > 代码库 > Devexpress GridView.CustomSummaryCalculate 实现自定义Group Summary

Devexpress GridView.CustomSummaryCalculate 实现自定义Group Summary

--首发于博客园, 转载请保留此链接  博客原文

DevExpress Documentation官方地址:GridView.CustomSummaryCalculate Event

 

1. 概要

界面上 GridView 显示的数据里某些字段在读出来的时候已经 SUM By FieldA ,在界面上统计时就不能简单累计总和,条件是 FieldA 相同不能重复相加。

2. 问题

view SalesOrderLineList 中,RealTimeStockTotalQuantity 与 RealTimeProductionTotalQuantity 分别是每个 ItemID 的总库存量 与 正在生产的总量

那么在 SalesOrderLineList 的 GridControl 中

(1) 当 Group By Item 时, Group Footer 只须取其中一行

(2) Grid Footer 中,应该是所有 ItemID 对应的 库存量总和

(3) 当 Group By 不是 Item 时,Group Footer 可不显示

一般来说,页脚统计多为 Sum, Count, Average 这样的 SummaryType, 用于直接在 GridColumn SummaryItem.SummaryType 选择相应的选择,无须特殊处理。

对于 RealTimeStockTotalQuantity 与 RealTimeProductionTotalQuantity 这样特殊的统计字段只能在代码实现计算逻辑。

3. 效果图

如下:

4. 思路

把 ItemID 相关的统计字段保存到本地的 Dictionary 里面,key 为 ItemID, value 为 相关字段数据,

然后在 GridView.CustomSummaryCalculate 事件中取累计值 赋给 TotalSummary,取对应 ItemID 的值赋给 GroupSummary 。

由于字段较多(实际中不止2个统计字段),定义一个类 ItemSummaryInfo,如下:

private class ItemSummaryInfo{    public int ItemID { get; set; }     public int RealTimeProductionTotalQuantity { get; set; }     public int RealTimeStockTotalQuantity { get; set; }     public decimal RealTimeProductionTotalWeight { get; set; }    public decimal RealTimeStockTotalWeight { get; set; }    public int RTProductionInProcessQuantity { get; set; }    public int RTProductionActiveQuantity { get; set; }    public int RTProductionPParcelsQuantity { get; set; }     public object this[string propertyName]    {        get { return Prop(propertyName).GetValue(this, null); }        set { Prop(propertyName).SetValue(this, value, null); }    }     System.Reflection.PropertyInfo Prop(string propertyName)    {        return this.GetType().GetProperty(propertyName);    }}

 

5. 实现过程

5.1 GridView 中添加 字段 RealTimeProductionTotalQuantity , RealTimeStockTotalQuantity

字段属性 SummaryItem.SummaryType = Custome,否则 Grid 页脚无法显示

 

5.2 在 GridView 的 Group Summary Items 里添加 相关的 Group Summary

主要 ShowInGroupColumnFooter 是 Step 1 中添加的对应字段, SummaryType 仍为 Custom

 

 5.3 GridView.DataSourceChanged 之类的事件里提取最新据保存到本地变量 _itemSums

void SalesOrderLineListManager_DataSourceChanged(object sender, EventArgs e){    RetreiveItemSummary();}private void RetreiveItemSummary(){    var gridvisibleColumnNames =        from GridColumn column in grdViewMain.Columns        where column.UnboundType == DevExpress.Data.UnboundColumnType.Bound && column.Visible        select column.FieldName;    // 隐藏字段不作处理,只处理显示字段    processNeededGroupFields = groupByItemFields.Intersect(gridvisibleColumnNames).ToArray();    _itemSums = new Dictionary<int, ItemSummaryInfo>();    DataTable dt = Manager.DataSource as DataTable;    if (dt == null)        return;    #region retreive Item Group fields    var groupByItem =        from row in dt.AsEnumerable()        group row by new { Item = row["ItemID"] } into grp        select new        {            ItemID = (int)grp.Key.Item,            Rows = grp        };    foreach (var item in groupByItem)    {        if (_itemSums.ContainsKey(item.ItemID))            continue;        ItemSummaryInfo newItem = new ItemSummaryInfo() { ItemID = item.ItemID };        // 保存数据到 Dictionary<int, ItemSummaryInfo> 中        foreach (var groupfield in processNeededGroupFields)        {            if (groupByItemWeightFields.Contains(groupfield))                newItem[groupfield] = (decimal)item.Rows.First()[groupfield];            else                newItem[groupfield] = (int)item.Rows.First()[groupfield];        }        _itemSums.Add(item.ItemID, newItem);    }    #endregion    grdViewMain.RefreshData();}

 

5.4 添加 GridView.CustomSummaryCalculate 事件,在 CustomSummaryProcess.Finalize 中显示相关数据

void grdViewMain_CustomSummaryCalculate(object sender, DevExpress.Data.CustomSummaryEventArgs e){    if (IDEHelper.DesignTime)        return;    GridView view = sender as GridView;    string fieldName = (e.Item as GridSummaryItem).FieldName;    int rowHandle = e.RowHandle;    if (processNeededGroupFields == null || !processNeededGroupFields.Contains(fieldName) || rowHandle < 0)        return;    ResetItemGroupSummary();    int itemId = 0;    switch (e.SummaryProcess)    {        case CustomSummaryProcess.Start:            break;        case CustomSummaryProcess.Calculate:            break;        case CustomSummaryProcess.Finalize:            if (e.IsTotalSummary)            {                if (groupByItemWeightFields.Contains(fieldName ))                {                    e.TotalValue = _itemSums.Sum(x => (decimal)x.Value[fieldName]);                }                else                {                    e.TotalValue = _itemSums.Sum(x => (int)x.Value[fieldName]);                }            }            else if (e.IsGroupSummary)            {                if (isGroupByItemOnly)                {                    object id = view.GetRowCellValue(e.RowHandle, colItemID);                    if (id == null) return;                    itemId = (int)id;                    if (!_itemSums.ContainsKey(itemId)) return;                    e.TotalValue = _itemSums[itemId][fieldName];                }                                 }            break;        default:            break;    }} private void ResetItemGroupSummary(){    if (isPreviousGroupByItemOnly == isGroupByItemOnly)        return;    grdViewMain.GroupSummary.BeginUpdate();    isPreviousGroupByItemOnly = isGroupByItemOnly;    if (isGroupByItemOnly)    {        // add item group summary        ItemGroupSummaryItem.ForEach(x =>        {            if (grdViewMain.GroupSummary.IndexOf(x) >= 0)                return;            grdViewMain.GroupSummary.Add(x);        });    }    else    {        // remove item group summary        ItemGroupSummaryItem.ForEach(x =>        {            if (grdViewMain.GroupSummary.IndexOf(x) >= 0)                grdViewMain.GroupSummary.Remove(x);        });    }    grdViewMain.GroupSummary.EndUpdate();} 

 

5. 5 补充相关变量定义

// 保存 ItemID Group 数据private Dictionary<int, ItemSummaryInfo> _itemSums = null;// 当前需要处理字段string[] processNeededGroupFields = null;// 备份前一次 Group ItemID, 用于判断是否需要移除/添加 Group SummaryIDbool isPreviousGroupByItemOnly {get;set;}// GridView 是否 Group ItemIDbool isGroupByItemOnly { get { return grdViewMain.GroupCount == 1 && grdViewMain.GroupedColumns[0] == colItemID; } }string[] groupByItemFields = new string[]{    "RealTimeProductionTotalQuantity",     "RealTimeStockTotalQuantity",    "RealTimeProductionTotalWeight",    "RealTimeStockTotalWeight",    "RTProductionInProcessQuantity",    "RTProductionActiveQuantity",    "RTProductionPParcelsQuantity",};string[] groupByItemWeightFields = new string[] {    "RealTimeProductionTotalWeight",    "RealTimeStockTotalWeight",};List<GridGroupSummaryItem> _itemGroupSummaryItems = null;// 要处理的 Group Summary Item ,用于格式化以及GridView 添加/移除 的访问public List<GridGroupSummaryItem> ItemGroupSummaryItem{    get    {        if (_itemGroupSummaryItems == null)        {            _itemGroupSummaryItems = new List<GridGroupSummaryItem>();            foreach (GridGroupSummaryItem summaryItem in grdViewMain.GroupSummary)            {                if (!groupByItemFields.Contains(summaryItem.FieldName))                    continue;                // synchronize displayformat                summaryItem.DisplayFormat = summaryItem.ShowInGroupColumnFooter.DisplayFormat.GetFormatString();                _itemGroupSummaryItems.Add(summaryItem);            }        }        return _itemGroupSummaryItems;    }    set { _itemGroupSummaryItems = value; }}

 

6. 总结

(1) 每次数据源发生变化时必须重新取值,否则显示的数据有可能不准确

(2) 当 GroupBy 发生变化时,需要 移除/添加 GroupSummaryItem, 假设 GroupBy ItemID 不成立而没有移除GroupSummaryItem,那么 GroupFooter 会显示一个空白方框,影响美观

(3) ItemSummaryInfo 类中属性 this[string propertyName] 根据属性名访问属性,避免逐个属性 Get / Set, 实现了简化代码

(4) 开始时在 CustomSummaryCalculate 事件中更新本地数据,CustomSummaryProcess.Start 时清空数据,CustomSummaryProcess.Calculate 时保存数据,官方文档也倾向于这种方法,但是有一个局限是每一个 Field 都会分别跑 Start, Calculate, Finalize, 当要处理的字段比较多时,特别是数据记录行较多,重复做 清除/保存 同样的数据,重复读写,效率低下,因此选择在数据源发生变化时读写一次,有利于提高效率,缺点是 数据源 限于 DataTable ,如果 是 Linq 或其他数据源类型显然是要另外处理的。