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
GroupBy
- gets and sets the group by column name. -
DetailsSourcePath
- Child object property name in the grid's datasource. 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
void SetDataSourceObject ()
: Convert the GroupGrid.DataSource
to generic
List<>
collection.void GetGroupByMethod()
: Get the GroupBy
method List<>
through reflection.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 <code>Group
Grid 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:
- Add a reference to the CustomControls library project to your web application
- Register the GroupGrid in your page
<%@ Register Assembly="CustomControls" Namespace="CustomControls" TagPrefix="gg" %>
- 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.