girdview分組,統計,排序的解決方案

 本文出處:http://www.cnblogs.com/rexying/archive/2007/09/29/910560.html

girdview分組,統計,排序的解決方案


導言:
GridView控件與DataGrid相比有了很大的改進,但仍不夠完美,比如進行分組(group)和統計(summary).爲了實現統計功能,我們可以在RowDataBound事件裏進行編碼;而分組則要複雜一些。如果同時實現這2種功能就更棘手了,基於這個難題我們可以考慮使用GridViewHelper類,就像其名稱一樣,它的用處在於構建分組和統計.

使用GridViewHelper

下面我們將看一些GridViewHelper的示例.首先我們展示groups 和 summaries創建的方格.該示例的數據來自Northwind數據庫,做了些許修改:


                                                                                                   圖1


要爲ItemTotal列創建統計功能,我只需要2行代碼:

protected void Page_Load(object sender, EventArgs e)
{
    GridViewHelper helper = new GridViewHelper(this.GridView1);
    helper.RegisterSummary("ItemTotal", SummaryOperation.Sum);
}

首先,我們創建GridViewHelper,然後對指定的列註冊summary功能,那麼就可以執行summary操作了,結果如下圖所示.


                                                                                                  圖2:


在該示例裏,最下面新添加了一行以顯示統計結果.不過,我們也可以在頁腳行顯示統計結果,而用不着新添加一行.不同的是,新添加一行時,只生成一個單元格來顯示結果,而在頁腳行顯示時,所有的單元格都會顯示出來.

現在我們來創建分組,代碼如下:
protected void Page_Load(object sender, EventArgs e)
{
    GridViewHelper helper = new GridViewHelper(this.GridView1);
    helper.RegisterGroup("ShipRegion", true, true);
    helper.ApplyGroupSort();
}

其中,RegisterGroup方法的第一個參數定義了分組依據,也就是按哪個列來進行分組.當然也可以創建交叉分組(composite group),也就是按照幾個列的組合進行分組.第二個參數指定了是否自動分組,就本例而言,爲每個組的標頭新創建一行.第三個參數指定是否把作爲分組依據的那個列強制隱藏.而ApplyGroupSort方法將分組依據的那個列作爲排序標準(sort expression),就本例而言,自然就是ShipRegion了.這麼做是很有必要的,因爲有可能數據從數據庫檢索來時已經進行了某種排序.如下圖,ShipRegion列已經被隱藏了:


                                                                                                   圖3

讓我們來看一些更有趣的事情,向各個分組添加統計功能.如下:

protected void Page_Load(object sender, EventArgs e)
{
    GridViewHelper helper = new GridViewHelper(this.GridView1);
    helper.RegisterGroup("ShipRegion", true, true);
    helper.RegisterSummary("ItemTotal", SummaryOperation.Sum, "ShipRegion");
    helper.ApplyGroupSort();
}

這次,RegisterSummary方法多了一個參數。該參數指定了創建統計功能的組的名稱。組的名稱自動由作爲分組依據的那些列的名字生成.如果分組依據只有一個列,那麼組的名稱就是那一列的名稱;如果分組依據有多個列,那麼組名由這些列名按順序串聯起來,用加號("+")連接,如"ShipRegion+ShipName".下圖爲進行了分組且對每組添加統計功能的情況:


                                                                                                圖4


我們還可以創建"等級組",如下所示:

protected void Page_Load(object sender, EventArgs e)
{
    GridViewHelper helper = new GridViewHelper(this.GridView1);
    helper.RegisterGroup("ShipRegion", true, true);
    helper.RegisterGroup("ShipName", true, true);
    helper.ApplyGroupSort();
}


結果如下:


                                                                              圖5

GridViewHelper有一些事件可以很容易的實現視覺或功能上的調整,如下:

                                                                                        圖6:

GroupStart:當新的分組開始時發生,意思就是說,當在作爲分組依據的列裏發現新的值時.

GroupEnd:某個組的最後一行結束時發生.

GroupHeader:當自動的爲某個組添加一個標頭時發生.如果不是自動的分組的話將不會觸發該事件.

GroupSummary:當爲某個組生成統計功能時發生。如果不是自動分組的話將不會觸發該事件,不過分組類型是suppression group(我們將在後面介紹)的話,另當別論.

GeneralSummary:當算出最終累計數時發生.如果最終累計數是自動生成的,那麼在添加統計行,且數字填充到行之後才觸發該事件.


通過寥寥幾行代碼我們就可以改善界面,如下:

protected void Page_Load(object sender, EventArgs e)
{
    GridViewHelper helper = new GridViewHelper(this.GridView1);
    helper.RegisterGroup("ShipRegion", true, true);
    helper.RegisterGroup("ShipName", true, true);
    helper.GroupHeader += new GroupEvent(helper_GroupHeader);
    helper.ApplyGroupSort();
}
   
private void helper_GroupHeader(string groupName, object[] values, GridViewRow row)
{
    if ( groupName == "ShipRegion" )
    {
        row.BackColor = Color.LightGray;
        row.Cells[0].Text = "  " + row.Cells[0].Text;
    }
    else if (groupName == "ShipName")
    {
        row.BackColor = Color.FromArgb(236, 236, 236);
        row.Cells[0].Text = "     " + row.Cells[0].Text;
    }
}


改善後的界面如下:


                                                                                    圖7:

更多分組選項

這裏還有2個案例。第一個是交叉分組(composite group).第二個案例定義了一個suppress group,其行爲與sql GROUP BY字句一樣,重複的值都過濾掉,且將summary操作建立在其它列的基礎上.

下面我們將看到這些交叉組的代碼以及界面:

protected void Page_Load(object sender, EventArgs e)
{
    GridViewHelper helper = new GridViewHelper(this.GridView1);
    string[] cols = new string[2];
    cols[0] = "ShipRegion";
    cols[1] = "ShipName";
    helper.RegisterGroup(cols, true, true);
    helper.ApplyGroupSort();
}


                                                                                             圖8

我們可以向該分組添加統計功能.這次,我們將定義一個求平均數的操作,並添加一個顯示該操作的label控件:

protected void Page_Load(object sender, EventArgs e)
{
    GridViewHelper helper = new GridViewHelper(this.GridView1);
    string[] cols = new string[2];
    cols[0] = "ShipRegion";
    cols[1] = "ShipName";
    helper.RegisterGroup(cols, true, true);
    helper.RegisterSummary("ItemTotal", SummaryOperation.Avg, "ShipRegion+ShipName");
    helper.GroupSummary += new GroupEvent(helper_GroupSummary);
    helper.ApplyGroupSort();
}

private void helper_GroupSummary(string groupName, object[] values, GridViewRow row)
{
    row.Cells[0].HorizontalAlign = HorizontalAlign.Right;
    row.Cells[0].Text = "Average";
}


                                                                                     圖9:

最後這個示例將創建一個suppress group.有一點很重要,如果定義了一個suppress group就不能創建其它的分組.同理,如果已經一個分組,也不能再定義一個suppress group,如果你非要硬來的話將拋出一個異常.

下面我們將看到suppress group的代碼以及界面:

protected void Page_Load(object sender, EventArgs e)
{
    GridViewHelper helper = new GridViewHelper(this.GridView1);
    helper.SetSuppressGroup("ShipName");
    helper.RegisterSummary("Quantity", SummaryOperation.Sum, "ShipName");
    helper.RegisterSummary("ItemTotal", SummaryOperation.Sum, "ShipName");
    helper.ApplyGroupSort();
}


                                                                                                     圖10

那些沒有在summary操作裏定義的列,其包含的值沒有顯示出來.它提示某種信息:

"Column 'column_name' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."

沒有必要將那些無關的列顯示出來。爲此,我們調用一個方法將它們隱藏起來:

protected void Page_Load(object sender, EventArgs e)
{
    GridViewHelper helper = new GridViewHelper(this.GridView1);
    helper.SetSuppressGroup(rdBtnLstGroup.SelectedValue);
    helper.RegisterSummary("Quantity", SummaryOperation.Sum, "ShipName");
    helper.RegisterSummary("ItemTotal", SummaryOperation.Sum, "ShipName");
    helper.SetInvisibleColumnsWithoutGroupSummary();
    helper.ApplyGroupSort();
}

結果如下:

 

                                                      圖11


Summary操作

GridViewHelper有3個內置的統計操作:sum, average 以及 row count.我們可以定製自己的統計操作.爲此,我們要爲GridViewHelper提供2個方法.第一個方法將會被方格(或組)裏的每一行所調用,第二個方法將會被調用來返回結果.下面我們將展示一個用戶自定義統計操作的示例.其返回最小值:

private List<int> mQuantities = new List<int>();

protected void Page_Load(object sender, EventArgs e)
{
    GridViewHelper helper = new GridViewHelper(this.GridView1);
    helper.RegisterSummary("Quantity", SaveQuantity, GetMinQuantity);
}

private void SaveQuantity(string column, string group, object value)
{
    mQuantities.Add(Convert.ToInt32(value));
}

private object GetMinQuantity(string column, string group)
{
    int[] qArray = new int[mQuantities.Count];
    mQuantities.CopyTo(qArray);
    Array.Sort(qArray);
    return qArray[0];
}

在上面的代碼中,2個方法都接受group 和 column名稱.如果統計方法不涉及到group,那麼該參數爲null.這些方法都被每一行所調用,同時接受當前行的某列的值.最終效果如下:


                                                                                            圖12


侷限性

在上面的例子我們模擬(simulate)了一個"等級組"。雖然該方格看起來具有"層次感",但執行起來絕不會按層次進行.沒有group 或 subgroup,只有sequentially registered groups.不過,如果我們想對一個內鑲組添加統計功能時,這倒是個問題.下面,我們將看在這種情況下將會發生什麼情況:
protected void Page_Load(object sender, EventArgs e)
{
    GridViewHelper helper = new GridViewHelper(this.GridView1);
    helper.RegisterGroup("ShipRegion", true, true);
    helper.RegisterGroup("ShipName", true, true);
    helper.RegisterSummary("ItemTotal", SummaryOperation.Sum, "ShipName");
    helper.RegisterSummary("ItemTotal", SummaryOperation.Sum);
    helper.GroupSummary += new GroupEvent(helper_Bug);
    helper.ApplyGroupSort();
}


private void helper_Bug(string groupName, object[] values, GridViewRow row)
{
    if (groupName == null) return;

    row.BackColor = Color.Bisque;
    row.Cells[0].HorizontalAlign = HorizontalAlign.Center;
    row.Cells[0].Text = "[ Summary for " + groupName + " " + values[0] + " ]";
}

 

                                                                        圖13

如上圖所示,summary在外部組(outer group)的標頭創建後再創建.原因是,本來事件的發生順序是:
Group1_Start
Group1_End
Group2_Start
Group2_End

對hierarchical grouping而言,事件發生順序爲:

Group1_Start
Group2_Start
Group2_End
Group1_End

貫徹

GridViewHelper是作爲一個獨立類(standalone class)而不是繼承類來貫徹的,這就使的利用GridViewHelper來處理任何的GridView成爲了可能。另外還有4個類:GridViewSummary, GridViewGroup, GridViewSummaryList 以及 GridViewGroupList.這些"list" classes可以利用一個字符串索引來進行訪問,如:
helper.GeneralSummaries["ItemTotal"].Value.

當創建GridViewHelper時,將對目標GridView的一個引用進行保存,且RowDataBound事件將綁定到一個方法完成實際的工作:

public GridViewHelper(GridView grd, bool useFooterForGeneralSummaries, SortDirection groupSortDirection)
{
    this.mGrid = grd;
    this.useFooter = useFooterForGeneralSummaries;
    this.groupSortDir = groupSortDirection;
    this.mGeneralSummaries = new GridViewSummaryList();
    this.mGroups = new GridViewGroupList();
    this.mGrid.RowDataBound += new GridViewRowEventHandler(RowDataBoundHandler);
}

GridViewHelper內部使用的一些方法被定義爲public,因爲如果必要的話可以將這些有用的方法進行用戶定製.還有一些其它的操作選擇沒有在示例裏進行演示,不過我們可以通過Visual Studio的智能感知系統來查證.

紀要:

對值類型的過多的的裝箱和拆箱操作會對執行性能產生影響.爲此,我們可以用泛型來貫徹這些內置的統計操作,但這並沒有想象的那麼容易,可以參閱文章《Using Generics for Calculations》(http://www.codeproject.com/csharp/genericnumerics.asp)或《Operator Overloading with Generics》(http://www.codeproject.com/csharp/genericoperators.asp).在現實生活中,這種影響不太顯著,除非有數百萬行,或有數千個用戶分組,且同時進行數據統計操作.

在線案例將GridView的EnableViewState設置爲false. 這是必要的,因爲當其爲true,在一個頁面回傳時,GridView控件將從ViewState進行重新綁定,而不會觸發RowDataBound事件.我們可以在ASP.Net 2.0裏放心的禁用該ViewState,因爲ControlState仍然進行了保存.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章