Click here to Skip to main content
Click here to Skip to main content

Grouping and row detail grid

, 23 Jan 2013
Rate this:
Please Sign up or sign in to vote.
Custom DataGrid than can group data, display row details, and filter data.

Introduction

This is my first article I have ever written. Whenever I search for a solution I get a ready made solution for my needs in code project most of the times. I hope this article will provide idea if not a reusable grid. Its just another Data grid customization simplified for developers.

Background

Recently I had a requirement to display data grouping in web page. After few tries I achieved it using MVC 3 partial views. This sparked a thought of customizing the data grid for grouping and row detail template (like in WPF DataGrid RowDetails). 

Grid Properties 

I have added two public properties to the Grid

  1. GroupBy - gets and sets the group by column name. 
  2. DetailsSourcePath - Child object property name in the grid's datasource. 
  3. DetailFields - Properties for row details in child object.  
<gg:GroupGrid id="gridViewList" runat="Server" 
       DetailsSourcePath="Orders" GroupBy="Name">
     <DetailFields>
         <gg:DetailedItem DataField="ProductName"></gg:DetailedItem>
         <gg:DetailedItem DataField="OrderDate"></gg:DetailedItem>
     </DetailFields>
</gg:GroupGrid>

Below is the source object properties marked in bold which are assigned to the Grid. 

GroupData gd1 = new GroupData() { Id = 1, Name = "Tom", 
           City = "London", Product = "TomTom", OrderValue = 110, Orders = new List<Order>()
{
    new Order(){ OrderId = 2, ProductName = "P1", OrderDate = DateTime.Now.AddDays(-5)},
    new Order(){ OrderId = 3, ProductName = "P2", OrderDate = DateTime.Now.AddDays(-3)},
    new Order(){ OrderId = 4, ProductName = "P3", OrderDate = DateTime.Now.AddDays(-6)}
}

Grouping  

Grouping is achieved by reflection in following private methods in GroupGrid

  1. void SetDataSourceObject ():  Convert the GroupGrid.DataSource to generic List<> collection.
  2. void GetGroupByMethod():  Get the GroupBy method List<> through reflection.
  3. object GetGroupedSource(string propertyName): Invokes GroupBy method and return IEnumerable<IGrouping<Tkey,TSource>> where Tkey is property type of "Name" which is string, Tsource is  GroupData

The above methods are called from overridden CreateControlHierarchy()  method.

Toggle group expand and collapse:  

item.CssClass = "gg_groupHR";
item.Attributes.Add("onclick", this.ID + ".toggleGroupExpandCollapse(this,false)");

groupGrid.prototype.toggleGroupExpandCollapse = function (hC, e)
{
	var c="";
	
	var a = hC.nextSibling;
	c = a.style.display;
	while(a!=null){
	    if (a.className == "gg_groupDR") {
	        
	        if (c == "")
	            a.style.display = "none";
	        else
	            a.style.display = "";
	        a = a.nextSibling;
	    }
	    if (a.className == "gg_groupHR" || a.tagName == null ||a.tagName != "TR")
	        break;
	}
};

RowDetails 

DetailFields property in the group grid is based on pattern used in article Child Collections in Asp.Net Custom Controls by Luke Foust.  

DataRowClick event handles the data row click to display the row details.

item.Attributes.Add("onclick", Page.ClientScript.GetPostBackEventReference(this, "gg_DR_index=" + item.ItemIndex)); 

GroupGrid implements IPostBackEventHandler to handle post back event on row click. There are two different ways to render the row details section:

The registered DataRowClick event handler can pass a new control through GridGroupRowEventArgs.RowDetailsItem.

void gridViewList_DataRowClick(object sender, CustomControls.GridGroupRowEventArgs e)
{
    if (e.RowDataItem != null)
    {
        Table t = new Table();
        TableRow row = new TableRow();
        TableCell cell = new TableCell();
        cell.Text = "row details";
        row.Cells.Add(cell);
        t.Rows.Add(row);
        e.RowDetailsItem = t;
    }
} 

Details section is derived from the DetailsSourcePath and DetailFields. If the details source object is collection the row detail will be table. 

GroupGrid currently supports a generic collection (DataTable and DataSet supported in future versions) as datasource.   

Filter Template

Filter can be enabled in the GroupGrid by EnableFilter property.

<gg:GroupGrid id="gridViewList" Width="70%" runat="Server" DetailsSourcePath="Orders" GroupBy="Name" EnableFilter="true">  

When the filter is enabled filter template is generated automatically for all the columns displayed in the GroupGrid as show below:

 

Creating Filter template and applying the filter to the grid is achieved in following steps:

Initialize filters : Get all properties from the data source object and add it to a collection

foreach (var column in columns)
{
   BoundColumn bcolumn = column as BoundColumn;
   if (bcolumn != null)
   {
      FilterCriteria fc = new FilterCriteria();
      string propName = bcolumn.DataField;
      Type propType = _genericParamObject.GetType().GetProperty(propName).PropertyType;

      string controlId = propName + "_filter_" + colIndex;
      fc.ColumnName = propName;
      fc.ColumnType = propType;
      fc.ControlId = controlId;
      fc.FilterColumnIndex = colIndex;
      _filters.Add(fc);
    }
    foreach(var filter in _filters){ TextBox tb = new TextBox() ........}// 

Applying filter: Invoke Enumerable.Where on the datasource collection though reflection 

ParameterExpression parameter = Expression.Parameter(_genericParamObject.GetType(), "x");
var delegateType = typeof(Func<,>).MakeGenericType(_genericParamObject.GetType(), typeof(bool));
Delegate whereParamDelegate;
foreach (var item in _filters.Where(x => x.IsSet))
{
  MemberExpression property = Expression.Property(parameter, item.ColumnName);
  Expression prop1 = LambdaExpression.Equal(property, Expression.Constant(item.FilterValue));

  whereParamDelegate = Expression.Lambda(delegateType, prop1, parameter).Compile();

  var genericMethodInfo = _whereFilterMethod.MakeGenericMethod(new[] { _genericParamObject.GetType() });

  _genericDataSource = genericMethodInfo.Invoke(_genericDataSource, new object[] { _genericDataSource, whereParamDelegate });
            } 

Using the code

To use the GroupGrid follow the below steps:

  1. Add a reference to the CustomControls library project to your web application
  2. Register the GroupGrid in your page  
  3. <%@ Register Assembly="CustomControls"  Namespace="CustomControls" TagPrefix="gg" %> 
  4. Add groupGrid.js or copy the content to your script file.

Points of Interest

My initial thought was to create only grouping then wanted to add row details. This is just an initial version, I have plans to add more features and enable Ajax.

History

  • 20-Jan-2013 - Article published.
  • 23-Jan-2013 - Added Filter template.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Rajamohan Dhanushkodi
Software Developer (Senior)
United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 PinmemberVelarasu Sekkilar21-Jan-13 4:37 
GeneralRe: My vote of 5 PinmemberRajamohan Dhanushkodi21-Jan-13 6:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 23 Jan 2013
Article Copyright 2013 by Rajamohan Dhanushkodi
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid