DevExpress-GridControl girdview分组,统计,排序的解决方案

 

今天有一个项目需要用到GRIDVIEW分组显示,合并行,与是通过GOOGLE搜到孟大大关于本问题的解决方案,在此做一下引用:


GridView has a lot of improvements over the DataGrid but it still lacks some very important features. A recurring requirement not available in the GridView is to create groups and summaries. To create summaries we can easily code the RowDataBound event. Grouping is a more complex task, and involves more coding and debugging. But when we need to combine these two features the things really start to get messy. With this in mind I have implemented the GridViewHelper, as the name suggests, a helper class to aid the creation of groups and summaries.

[Online sample]

[Download source code]

Using the GridViewHelper

Below we will see some GridViewHelper samples. First we show the grid to which the groups and summaries will be created. The sample data comes from Northwind database, with a few modifications:

ShipRegionShipNameOrderIdProductNameQuantityUnitPriceItemTotal
NMOld World Delicatessen10922Alice Mutton15US$ 39,00US$ 585,00
NMOld World Delicatessen10922Teatime Chocolate Biscuits35US$ 4,50US$ 157,50
NMOld World Delicatessen10925Inlagd Sill25US$ 19,00US$ 475,00
NMOld World Delicatessen10925Filo Mix12US$ 7,00US$ 84,00
ORSave-a-lot Markets10935Chai21US$ 18,00US$ 378,00
ORSave-a-lot Markets10935Carnarvon Tigers4US$ 62,50US$ 250,00
ORSave-a-lot Markets10935Tunnbröd8US$ 9,00US$ 72,00
ORGourmet Lanchonetes10959Rhönbräu Klosterbier20US$ 7,75US$ 155,00
ORSplit Rail Beer & Ale10961Filo Mix6US$ 7,00US$ 42,00
ORSplit Rail Beer & Ale10961Lakkalikööri60US$ 18,00US$ 1.080,00
ORLazy K Kountry Store10969Spegesild9US$ 12,00US$ 108,00
NMOld World Delicatessen10981Côte de Blaye60US$ 263,50US$ 15.810,00
NMWhite Clover Markets10989Grandma's Boysenberry Spread40US$ 25,00US$ 1.000,00
NMWhite Clover Markets10989Queso Cabrales15US$ 21,00US$ 315,00
NMWhite Clover Markets10989Camembert Pierrot4US$ 9,65US$ 38,60


To create a summary for the ItemTotal column we need only the promised 2 lines of code:

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


First we create the GridViewHelper setting the grid in which it will act in the constructor. Then we register the summary specifying the column name and the summary operation to be performed. The result is below:

ShipRegionShipNameOrderIdProductNameQuantityUnitPriceItemTotal
NMOld World Delicatessen10922Alice Mutton15US$ 39,00US$ 585,00
NMOld World Delicatessen10922Teatime Chocolate Biscuits35US$ 4,50US$ 157,50
NMOld World Delicatessen10925Inlagd Sill25US$ 19,00US$ 475,00
NMOld World Delicatessen10925Filo Mix12US$ 7,00US$ 84,00
ORSave-a-lot Markets10935Chai21US$ 18,00US$ 378,00
ORSave-a-lot Markets10935Carnarvon Tigers4US$ 62,50US$ 250,00
ORSave-a-lot Markets10935Tunnbröd8US$ 9,00US$ 72,00
ORGourmet Lanchonetes10959Rhönbräu Klosterbier20US$ 7,75US$ 155,00
ORSplit Rail Beer & Ale10961Filo Mix6US$ 7,00US$ 42,00
ORSplit Rail Beer & Ale10961Lakkalikööri60US$ 18,00US$ 1.080,00
ORLazy K Kountry Store10969Spegesild9US$ 12,00US$ 108,00
NMOld World Delicatessen10981Côte de Blaye60US$ 263,50US$ 15.810,00
NMWhite Clover Markets10989Grandma's Boysenberry Spread40US$ 25,00US$ 1.000,00
NMWhite Clover Markets10989Queso Cabrales15US$ 21,00US$ 315,00
NMWhite Clover Markets10989Camembert Pierrot4US$ 9,65US$ 38,60
 US$ 20.550,10

 

In this sample a new line was added to display the summary. Another option is to use the footer row to display the summary instead of creating a new one. When a new row is added to the grid, only the required cells to display the summarized columns are created. Using the footer, all the cells are created. In case of group summaries, generation of all cells or only the needed cells is a group attribute.

Now we will create a group. The code is shown below:

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


The first parameter of RegisterGroup method defines the columns to which the group must be created. It's also possible to create a composite group, consisting of an array of columns. The second parameter specifies if the group is automatic. In this case a new row will be created automatically for the group header. The third parameter specifies if the group columns must be hidden. The ApplyGroupSort method sets the sort expression of the grid as being the group columns, in this case, ShipRegion. This is required to grouping works properly, except if the data comes ordered from database.

In the above sample the column ShipRegion have been hidden:

ShipNameOrderIdProductNameQuantityUnitPriceItemTotal
NM
Old World Delicatessen10922Alice Mutton15US$ 39,00US$ 585,00
Old World Delicatessen10922Teatime Chocolate Biscuits35US$ 4,50US$ 157,50
Old World Delicatessen10925Inlagd Sill25US$ 19,00US$ 475,00
Old World Delicatessen10925Filo Mix12US$ 7,00US$ 84,00
Old World Delicatessen10981Côte de Blaye60US$ 263,50US$ 15.810,00
White Clover Markets10989Grandma's Boysenberry Spread40US$ 25,00US$ 1.000,00
White Clover Markets10989Queso Cabrales15US$ 21,00US$ 315,00
White Clover Markets10989Camembert Pierrot4US$ 9,65US$ 38,60
OR
Save-a-lot Markets10935Chai21US$ 18,00US$ 378,00
Save-a-lot Markets10935Carnarvon Tigers4US$ 62,50US$ 250,00
Save-a-lot Markets10935Tunnbröd8US$ 9,00US$ 72,00
Gourmet Lanchonetes10959Rhönbräu Klosterbier20US$ 7,75US$ 155,00
Split Rail Beer & Ale10961Filo Mix6US$ 7,00US$ 42,00
Split Rail Beer & Ale10961Lakkalikööri60US$ 18,00US$ 1.080,00
Lazy K Kountry Store10969Spegesild9US$ 12,00US$ 108,00


Let's make something more interesting, let's add a summary to the created group. We need just one more line to register the summary to the group:

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() ;
}


This time, the RegisterSummary method takes another parameter. The parameter specifies the name of the group to which the summary must be created. Group name is automatically generated from the group column names. If the group has only one column, group name will be the name of that column. If the group has more than one column, the group name will be the ordered concatenation of the columns that composes the group, joined with a plus sign ("+"): "ShipRegion+ShipName".

We can see below the grid with grouping and a summary for the group:

ShipNameOrderIdProductNameQuantityUnitPriceItemTotal
NM
Old World Delicatessen10922Alice Mutton15US$ 39,00US$ 585,00
Old World Delicatessen10922Teatime Chocolate Biscuits35US$ 4,50US$ 157,50
Old World Delicatessen10925Inlagd Sill25US$ 19,00US$ 475,00
Old World Delicatessen10925Filo Mix12US$ 7,00US$ 84,00
Old World Delicatessen10981Côte de Blaye60US$ 263,50US$ 15.810,00
White Clover Markets10989Grandma's Boysenberry Spread40US$ 25,00US$ 1.000,00
White Clover Markets10989Queso Cabrales15US$ 21,00US$ 315,00
White Clover Markets10989Camembert Pierrot4US$ 9,65US$ 38,60
 US$ 18.465,10
OR
Save-a-lot Markets10935Chai21US$ 18,00US$ 378,00
Save-a-lot Markets10935Carnarvon Tigers4US$ 62,50US$ 250,00
Save-a-lot Markets10935Tunnbröd8US$ 9,00US$ 72,00
Gourmet Lanchonetes10959Rhönbräu Klosterbier20US$ 7,75US$ 155,00
Split Rail Beer & Ale10961Filo Mix6US$ 7,00US$ 42,00
Split Rail Beer & Ale10961Lakkalikööri60US$ 18,00US$ 1.080,00
Lazy K Kountry Store10969Spegesild9US$ 12,00US$ 108,00
 US$ 2.085,00


It's possible to create more than one group in the grid, simulating a hierarchical grouping, as seen below:

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() ;
}


Result:

OrderIdProductNameQuantityUnitPriceItemTotal
NM
Old World Delicatessen
10922Alice Mutton15US$ 39,00US$ 585,00
10922Teatime Chocolate Biscuits35US$ 4,50US$ 157,50
10925Inlagd Sill25US$ 19,00US$ 475,00
10925Filo Mix12US$ 7,00US$ 84,00
10981Côte de Blaye60US$ 263,50US$ 15.810,00
White Clover Markets
10989Grandma's Boysenberry Spread40US$ 25,00US$ 1.000,00
10989Queso Cabrales15US$ 21,00US$ 315,00
10989Camembert Pierrot4US$ 9,65US$ 38,60
OR
Lazy K Kountry Store
10969Spegesild9US$ 12,00US$ 108,00
Gourmet Lanchonetes
10959Rhönbräu Klosterbier20US$ 7,75US$ 155,00
Split Rail Beer & Ale
10961Filo Mix6US$ 7,00US$ 42,00
10961Lakkalikööri60US$ 18,00US$ 1.080,00
Save-a-lot Markets
10935Chai21US$ 18,00US$ 378,00
10935Carnarvon Tigers4US$ 62,50US$ 250,00
10935Tunnbröd8US$ 9,00US$ 72,00


Visualization is compromised when there is more than one group. GridViewHelper has events to allow easy implementation of visual or functional adjusts. The list of events follows below:

GroupStartOccurs when a new group starts, it means, when new values are found in the group column.
GroupEndOccurs in the last row of the group
GroupHeaderOccurs when an automatic header row is added for the group. The event is not triggered if the group is not automatic.
GroupSummaryOccurs when the summary row is generated for the group. The event is not triggered if the group is not automatic, but will be triggered if the group is a suppression group (will be seen later on).
GeneralSummaryOccurs after the general summaries be calculated. If the summary is automatic the event occurs after the summary row be added and after the summary values be placed in the row.
FooterDataBoundOccurs in the footer databinding.


With a few more lines of code we can improve the visual aspect of the grid:

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 ;
    
}
}


The grid after the cosmetics:

OrderIdProductNameQuantityUnitPriceItemTotal
  NM
     Old World Delicatessen
10922Alice Mutton15US$ 39,00US$ 585,00
10922Teatime Chocolate Biscuits35US$ 4,50US$ 157,50
10925Inlagd Sill25US$ 19,00US$ 475,00
10925Filo Mix12US$ 7,00US$ 84,00
10981Côte de Blaye60US$ 263,50US$ 15.810,00
     White Clover Markets
10989Grandma's Boysenberry Spread40US$ 25,00US$ 1.000,00
10989Queso Cabrales15US$ 21,00US$ 315,00
10989Camembert Pierrot4US$ 9,65US$ 38,60
  OR
     Lazy K Kountry Store
10969Spegesild9US$ 12,00US$ 108,00
     Gourmet Lanchonetes
10959Rhönbräu Klosterbier20US$ 7,75US$ 155,00
     Split Rail Beer & Ale
10961Filo Mix6US$ 7,00US$ 42,00
10961Lakkalikööri60US$ 18,00US$ 1.080,00
     Save-a-lot Markets
10935Chai21US$ 18,00US$ 378,00
10935Carnarvon Tigers4US$ 62,50US$ 250,00
10935Tunnbröd8US$ 9,00US$ 72,00

 

More grouping options

There are two more interesting samples. The first presents a composite group. The second defines a suppress group, that has the same behavior of the sql GROUP BY clause. The repeating values are suppressed, and a summary operation is performed on the other columns.

Below we can see the code and the grid appearance for the composite group:

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() ;
}

 

OrderIdProductNameQuantityUnitPriceItemTotal
NM - Old World Delicatessen
10922Alice Mutton15US$ 39,00US$ 585,00
10922Teatime Chocolate Biscuits35US$ 4,50US$ 157,50
10925Inlagd Sill25US$ 19,00US$ 475,00
10925Filo Mix12US$ 7,00US$ 84,00
10981Côte de Blaye60US$ 263,50US$ 15.810,00
NM - White Clover Markets
10989Grandma's Boysenberry Spread40US$ 25,00US$ 1.000,00
10989Queso Cabrales15US$ 21,00US$ 315,00
10989Camembert Pierrot4US$ 9,65US$ 38,60
OR - Lazy K Kountry Store
10969Spegesild9US$ 12,00US$ 108,00
OR - Gourmet Lanchonetes
10959Rhönbräu Klosterbier20US$ 7,75US$ 155,00
OR - Split Rail Beer & Ale
10961Filo Mix6US$ 7,00US$ 42,00
10961Lakkalikööri60US$ 18,00US$ 1.080,00
OR - Save-a-lot Markets
10935Chai21US$ 18,00US$ 378,00
10935Carnarvon Tigers4US$ 62,50US$ 250,00
10935Tunnbröd8US$ 9,00US$ 72,00


We can add a summary to the group. This time we will define an average operation and add a label to indicate the operation:

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" ;
}

 

OrderIdProductNameQuantityUnitPriceItemTotal
NM - Old World Delicatessen
10922Alice Mutton15US$ 39,00US$ 585,00
10922Teatime Chocolate Biscuits35US$ 4,50US$ 157,50
10925Inlagd Sill25US$ 19,00US$ 475,00
10925Filo Mix12US$ 7,00US$ 84,00
10981Côte de Blaye60US$ 263,50US$ 15.810,00
AverageUS$ 3.422,30
NM - White Clover Markets
10989Grandma's Boysenberry Spread40US$ 25,00US$ 1.000,00
10989Queso Cabrales15US$ 21,00US$ 315,00
10989Camembert Pierrot4US$ 9,65US$ 38,60
AverageUS$ 451,20
OR - Lazy K Kountry Store
10969Spegesild9US$ 12,00US$ 108,00
AverageUS$ 108,00
OR - Gourmet Lanchonetes
10959Rhönbräu Klosterbier20US$ 7,75US$ 155,00
AverageUS$ 155,00
OR - Split Rail Beer & Ale
10961Filo Mix6US$ 7,00US$ 42,00
10961Lakkalikööri60US$ 18,00US$ 1.080,00
AverageUS$ 561,00
OR - Save-a-lot Markets
10935Chai21US$ 18,00US$ 378,00
10935Carnarvon Tigers4US$ 62,50US$ 250,00
10935Tunnbröd8US$ 9,00US$ 72,00
AverageUS$ 233,33


The last sample will create a suppress group. It's important to mention that if a suppress group is defined, no other group may be created. In the same way, if there is already a group defined, we can't create a suppress group and an exception will be raised if we try it.

Below we can see the code and the grid appearance for the 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() ;
}

 

ShipRegionShipNameOrderIdProductNameQuantityUnitPriceItemTotal
 Lazy K Kountry Store  9 US$ 108,00
 Gourmet Lanchonetes  20 US$ 155,00
 Old World Delicatessen  147 US$ 17.111,50
 White Clover Markets  59 US$ 1.353,60
 Split Rail Beer & Ale  66 US$ 1.122,00
 Save-a-lot Markets  33 US$ 700,00


No value is displayed for the columns that don't have a summary operation defined. This makes sense because GridViewHelper doesn't know how to proceed to summarize the values found in the group rows to a unique value. This reminds the certain known message:

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

It doesn't make sense to display the columns that don't have a summary operation, and to hide them we need to call a method:

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() ;
}


I know, it's a big big name! The resulting grid can be seen below:

ShipNameQuantityItemTotal
Lazy K Kountry Store9US$ 108,00
Gourmet Lanchonetes20US$ 155,00
Old World Delicatessen147US$ 17.111,50
White Clover Markets59US$ 1.353,60
Split Rail Beer & Ale66US$ 1.122,00
Save-a-lot Markets33US$ 700,00

 

Summary operations

GridViewHelper has 3 built-in summary operations: sum, average and row count. A very useful feature is the possibility of define custom summary operations. To achieve this we need to provide two methods to the GridViewHelper. A method will be called for each row found in the grid (or group) and the other will be called to retrieve the result of the summary operation. Below we have a sample of a custom summary operation. The semi-dummy operation will return the minimum value found:

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 ] ;
}


In the code above we can see the required methods signatures. Both receive the summarized group and column names. If the summary is not relative to a group, the group parameter will be null. The method that is called for each row found in the grid, receives also the value of the column in the current row.

The resulting grid can be seen below :

ShipRegionShipNameOrderIdProductNameQuantityUnitPriceItemTotal
NMOld World Delicatessen10922Alice Mutton15US$ 39,00US$ 585,00
NMOld World Delicatessen10922Teatime Chocolate Biscuits35US$ 4,50US$ 157,50
NMOld World Delicatessen10925Inlagd Sill25US$ 19,00US$ 475,00
NMOld World Delicatessen10925Filo Mix12US$ 7,00US$ 84,00
ORSave-a-lot Markets10935Chai21US$ 18,00US$ 378,00
ORSave-a-lot Markets10935Carnarvon Tigers4US$ 62,50US$ 250,00
ORSave-a-lot Markets10935Tunnbröd8US$ 9,00US$ 72,00
ORGourmet Lanchonetes10959Rhönbräu Klosterbier20US$ 7,75US$ 155,00
ORSplit Rail Beer & Ale10961Filo Mix6US$ 7,00US$ 42,00
ORSplit Rail Beer & Ale10961Lakkalikööri60US$ 18,00US$ 1.080,00
ORLazy K Kountry Store10969Spegesild9US$ 12,00US$ 108,00
NMOld World Delicatessen10981Côte de Blaye60US$ 263,50US$ 15.810,00
NMWhite Clover Markets10989Grandma's Boysenberry Spread40US$ 25,00US$ 1.000,00
NMWhite Clover Markets10989Queso Cabrales15US$ 21,00US$ 315,00
NMWhite Clover Markets10989Camembert Pierrot4US$ 9,65US$ 38,60
 4 

 

Limitations

In one sample we said that we can simulate a hierarchical grouping. Although the grid appears to present a hierarchical grouping, the actual implementation isn't hierarchical. There's no group or subgroup. There are only sequentially registered groups. This becomes a problem if we need to create a summary for an inner group. Below we can see what happens in this situation:

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 ] +  " ]" ;
}

 

OrderIdProductNameQuantityUnitPriceItemTotal
NM
Old World Delicatessen
10922Alice Mutton15US$ 39,00US$ 585,00
10922Teatime Chocolate Biscuits35US$ 4,50US$ 157,50
10925Inlagd Sill25US$ 19,00US$ 475,00
10925Filo Mix12US$ 7,00US$ 84,00
10981Côte de Blaye60US$ 263,50US$ 15.810,00
[ Summary for ShipName Old World Delicatessen ]US$ 17.111,50
White Clover Markets
10989Grandma's Boysenberry Spread40US$ 25,00US$ 1.000,00
10989Queso Cabrales15US$ 21,00US$ 315,00
10989Camembert Pierrot4US$ 9,65US$ 38,60
OR
[ Summary for ShipName White Clover Markets ]US$ 1.353,60
Lazy K Kountry Store
10969Spegesild9US$ 12,00US$ 108,00
[ Summary for ShipName Lazy K Kountry Store ]US$ 108,00
Gourmet Lanchonetes
10959Rhönbräu Klosterbier20US$ 7,75US$ 155,00
[ Summary for ShipName Gourmet Lanchonetes ]US$ 155,00
Split Rail Beer & Ale
10961Filo Mix6US$ 7,00US$ 42,00
10961Lakkalikööri60US$ 18,00US$ 1.080,00
[ Summary for ShipName Split Rail Beer & Ale ]US$ 1.122,00
Save-a-lot Markets
10935Chai21US$ 18,00US$ 378,00
10935Carnarvon Tigers4US$ 62,50US$ 250,00
10935Tunnbröd8US$ 9,00US$ 72,00
[ Summary for ShipName Save-a-lot Markets ]US$ 700,00
 US$ 20.550,10


As we can see, the summary is created after the header of the outer group. This occurs because the event sequence is:

Group1_Start
Group1_End
Group2_Start
Group2_End

To a hierarchical grouping, the event sequence should be:

Group1_Start
Group2_Start
Group2_End
Group1_End

 

Implementation

GridViewHelper was implemented as a standalone class instead of an inherited class. This makes possible to use the GridViewHelper with any GridView, and doesn't force the developer to inherit a specific GridView, what could affect the classes design. There's another four classes in the solution: GridViewSummary, GridViewGroup, GridViewSummaryList and GridViewGroupList. The "list" classes were created to allow access by a string indexer: helper.GeneralSummaries["ItemTotal"].Value.

When the GridViewHelper is created, a reference to the target GridView is saved, and the RowDataBound event is bound to the method that does the hard work:

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) ;
}

Some methods used internally by the GridViewHelper were defined public because they provide some useful features that may be needed for some customizations. There are a few other options that wasn't shown in the samples but that can be easily verified with Visual Studio intellisense.

 

Known issues

Performance might be compromised with the excessive boxing and unboxing of value types. To solve this we could implement the built-in summary operations with generics, but this is not easy as we would like, as can be seen at Using Generics for Calculations. Another possibility: Operator Overloading with Generics. In real life this will not affect the application except if there are a million rows, or if there are thousand of users grouping and summarizing data concurrently.

The online sample keeps the GridView EnableViewState false. This is required because when EnableViewState is true, if the page is in a PostBack the GridView will be rebuilt from the ViewState, and won't trigger the RowDataBound event. We can securely disable the ViewState in ASP.Net 2.0 because the ControlState will still be saved.

Feel free to send comments and to implement more features to the GridViewHelper. Let me know and I will update the article.

Did you like this article? Please check out our solution for content management and e-mail marketing. Know it here!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值