Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Custom DataGrid Column: Custom DataGrid BoundColumn that automatically shows Sum/Average in DataGrid Footer. And Assigning Custom Data Type in DataField.

0.00/5 (No votes)
14 Feb 2007 1  
I want to show the sample which you can find custom design of your bound column of datagrid. Generally our basic requirement is the sum and average of the value in footer with great performance. here I have created one more custom column where I am assignning custom datatype.

Download source code - 20.9 Kb

Sample image

Introduction:


I want to show the sample which you can find custom design of your bound column of datagrid. Generally our basic requirement is the sum and average of the value in footer with great performance. In this simple sample application I am showing custom design of bound column showing sum/average in footer. And also I am showing how to assign custom data type in datafield like Address which is custom data type includes area and city as string data type. In this article we will compare the normal methods and later create our own custom DataGrid column "SumColumn", "AddressColumn" which are derived from BoundColumn to avoid repetition of the same code.

Background:


Let us consider the example of a DataGrid showing the list of employees. Columns are EmpID, EmpName, EmpAge, EmpAddress and Salary. To showing sum and average salary on footer there many solutions. And one of solution is:

DataGrid DataItemBound event :

Here I set up a simple DataGrid that displays the EmpID, Name and Salary from Employee Table.

<asp:DataGrid id="dgEmployee" runat="server" Width="183px" Height="154px" AutoGenerateColumns=False ShowFooter=True>

<HeaderStyle Font-Bold="True" ForeColor="#000033" BackColor="#993366"></HeaderStyle>

<ItemStyle ForeColor="#330099" BackColor="#f0f2f8"></ItemStyle>

<AlternatingItemStyle ForeColor="#330070" BackColor="#d8e4f8"></AlternatingItemStyle>

<FooterStyle ForeColor="#330099" BackColor="#ffffe1"></FooterStyle>

<SelectedItemStyle ForeColor="#663399" BackColor="#d4d0c8"></SelectedItemStyle>

<Columns>

<asp:BoundColumn DataField="EmpID" HeaderText="ID"></asp:BoundColumn>

<asp:BoundColumn DataField="EmpFirstName" HeaderText="Name"></asp:BoundColumn>

<asp:BoundColumn DataField="Salary" HeaderText="Salary"></asp:BoundColumn>

</Columns>

</asp:DataGrid>

And in the code behind:

private void Page_Load(object sender, System.EventArgs e)

{

DataClass _dataClass = new DataClass();

dgEmployee.DataSource = _dataClass.GetEmpInfo();

dgEmployee.DataBind();

}

private void dgEmployee_ItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)

{

decimal decValue = 0M;

if((e.Item.ItemType == ListItemType.Item) || (e.Item.ItemType == ListItemType.AlternatingItem))

{

decValue = decimal.Parse(e.Item.Cells[2].Text);

_decSum += decValue;

_intCount++;

}

if(e.Item.ItemType == ListItemType.Footer)

{

string s = "Sum: " + _decSum.ToString();

if(_intCount>0)

s += ("<BR>"+"Average:" + Convert.ToString(Math.Round((_decSum/_intCount),2)));

e.Item.Cells[2].Text = s;

}

}

OutPut:

Sample image

Comments:

There is no any problem with above code. But let us consider many columns have the data and it needs to show the sum of value in footer. So using the custom datagrid column, I am avoiding the repeatition of same code. This solution is providing you to make reusable of same code and more flexibility.

Custom SumColumn and AddressColumn:
Introduction to Solution:

Now it is the time to pull the cat out of the bag. Solution to this problem is to build the desired functionality right into a custom DataGridColumn that we can then use on any page! Confused, let me explain. We are going to create our own custom DataGrid column which we will refer as "SumColumn","AddressColumn", inherited from DataGrid BoundColumn. Due to its base class, the new SumColumn class will have all of the built-in functionality already present in the BoundColumn class. We just need to implement our sum functionality. Inheriting powerful controls like the BoundColumn class and adding new functionality to them is one of the OO features of the .NET Framework.

SumColumn I assigned decimal datafield and the AddressColumn I assigned custom datatype of Address. The custom datatype Address is like this:

public struct Address

{

private string _strArea;

private string _strCity;

public string Area

{

get

{

return _strArea;

}

set

{

_strArea = value;

}

}

public string City

{

get

{

return _strCity;

}

set

{

_strCity = value;

}

}

}

Simply I have created one employee class to keep employee information: "Employee.cs"

using System;

namespace WebPractice

{

/// <summary>

/// Summary description for Employee.

/// </summary>

public class Employee

{

#region constructor

public Employee()

{

//

// TODO: Add constructor logic here

//

}

#endregion

#region custom data type Address

public struct Address

{

private string _strArea;

private string _strCity;

public string Area

{

get

{

return _strArea;

}

set

{

_strArea = value;

}

}

public string City

{

get

{

return _strCity;

}

set

{

_strCity = value;

}

}

}

#endregion

#region Private variable

private int _intEmpID;

private decimal _decAge;

private string _strEmpFirstName;

private string _strEmpLastName;

private decimal _salary;

private Address _Address;

#endregion

#region Public Property

public int EmpID

{

get

{

return _intEmpID;

}

set

{

_intEmpID = value;

}

}

public decimal Age

{

get

{

return _decAge;

}

set

{

_decAge = value;

}

}

public decimal Salary

{

get

{

return _salary;

}

set

{

_salary = value;

}

}

public string EmpFirstName

{

get

{

return _strEmpFirstName;

}

set

{

_strEmpFirstName = value;

}

}

public string EmpLastName

{

get

{

return _strEmpLastName;

}

set

{

_strEmpLastName = value;

}

}

public Address EmpAddress

{

get

{

return _Address;

}

set

{

_Address = value;

}

}

#endregion

}

}

I have created one SumColumn class which is inherited from the BoundColumn class as "SumColumn.cs":

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.ComponentModel;

namespace WebPractice

{

/// <summary>

/// Summary description for SumColumn.

/// </summary>

public class SumColumn:BoundColumn

{

#region Constructor

public SumColumn()

{

//

// TODO: Add constructor logic here

//

}

#endregion

#region Private Variables

private decimal _decSum = 0M;

private int _intCount = 0;

private bool _showSum = false;

private bool _showAverage = false;

#endregion

#region Public Property

public bool ShowSum

{

get

{

return _showSum;

}

set

{

_showSum = value;

}

}

public bool ShowAverage

{

get

{

return _showAverage;

}

set

{

_showAverage = value;

}

}

#endregion

#region Methods

public override void InitializeCell(TableCell cell, int columnIndex, ListItemType itemType)

{

base.InitializeCell (cell, columnIndex, itemType);

switch(itemType)

{

case ListItemType.Item:

cell.DataBinding += new EventHandler(this.CellItemDataBound);

break;

case ListItemType.AlternatingItem:

cell.DataBinding += new EventHandler(this.CellItemDataBound);

break;

case ListItemType.Footer:

cell.DataBinding += new EventHandler(this.CellItemDataBound);

break;

}

}

private void CellItemDataBound(object s,EventArgs e)

{

TableCell tblCell = (TableCell)s;

DataGridItem dgiAge = (DataGridItem)tblCell.NamingContainer;

decimal _decValue = 0;

switch(dgiAge.ItemType)

{

case ListItemType.Item:

_decValue = this.GetUnderlyingValue(dgiAge.DataItem);

_decSum += _decValue;

_intCount++;

break;

case ListItemType.AlternatingItem:

_decValue = this.GetUnderlyingValue(dgiAge.DataItem);

_decSum += _decValue;

_intCount++;

break;

case ListItemType.Footer:

if(_showSum && _showAverage)

{

tblCell.Text = "Average: " + this.FormatDataValue(_decSum/_intCount)+

"<BR>"+ "Total: " + this.FormatDataValue(_decSum);

}

else if(_showSum)

tblCell.Text = "Total: " + this.FormatDataValue(_decSum);

else if(_showAverage)

tblCell.Text = "Average: " + this.FormatDataValue(_decSum/_intCount);

break;

}

}

protected decimal GetUnderlyingValue(object dataItem)

{

PropertyDescriptor _prpDesc = TypeDescriptor.GetProperties(dataItem).Find(this.DataField,true);

if(_prpDesc == null)

{

throw new HttpException("Field Not Found: " + this.DataField);

}

return decimal.Parse((_prpDesc.GetValue(dataItem)).ToString());

}

#endregion

}

}

And also I have created one AddressColumn class, derived from BoundColumn class to customize my address as follows with class name "AddressColumn.cs":

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.HtmlControls;

using System.ComponentModel;

using System.Text;

namespace WebPractice

{

/// <summary>

/// Summary description for AddressColumn.

/// </summary>

public class AddressColumn:BoundColumn

{

#region Constructor

public AddressColumn()

{

//

// TODO: Add constructor logic here

//

}

#endregion

#region Variables

private string _strCssClass;

#endregion

#region Public Property

public string CssClass

{

get

{

return _strCssClass;

}

set

{

_strCssClass = value;

}

}

#endregion

public override void InitializeCell(TableCell cell, int columnIndex, ListItemType itemType)

{

base.InitializeCell (cell, columnIndex, itemType);

switch(itemType)

{

case ListItemType.Item:

cell.DataBinding += new EventHandler(this.CellItemDataBound);

break;

case ListItemType.AlternatingItem:

cell.DataBinding += new EventHandler(this.CellItemDataBound);

break;

case ListItemType.Footer:

cell.DataBinding += new EventHandler(this.CellItemDataBound);

break;

}

}

#region Methods

private void CellItemDataBound(object s,EventArgs e)

{

TableCell tblCell = (TableCell)s;

DataGridItem dgiAge = (DataGridItem)tblCell.NamingContainer;

Employee.Address _adr = new WebPractice.Employee.Address();

StringBuilder _sbHtmlTag;

switch(dgiAge.ItemType)

{

case ListItemType.Item:

_adr = this.GetUnderlyingValue(dgiAge.DataItem);

_sbHtmlTag = new StringBuilder();

if(_adr.Area != null && _adr.Area.Trim() != "")

{

_sbHtmlTag.Append("<div class='" + CssClass + "'>");

_sbHtmlTag.Append("Area: ");

_sbHtmlTag.Append(_adr.Area);

_sbHtmlTag.Append("</div>");

}

if(_adr.City != null && _adr.City.Trim() != "")

{

_sbHtmlTag.Append("<div class='" + CssClass + "'>");

_sbHtmlTag.Append("City: ");

_sbHtmlTag.Append(_adr.City);

_sbHtmlTag.Append("</div>");

}

tblCell.Text = _sbHtmlTag.ToString();

break;

case ListItemType.AlternatingItem:

_adr = this.GetUnderlyingValue(dgiAge.DataItem);

_sbHtmlTag = new StringBuilder();

if(_adr.Area != null && _adr.Area.Trim() != "")

{

_sbHtmlTag.Append("<div class='" + CssClass + "'>");

_sbHtmlTag.Append("Area: ");

_sbHtmlTag.Append(_adr.Area);

_sbHtmlTag.Append("</div>");

}

if(_adr.City != null && _adr.City.Trim() != "")

{

_sbHtmlTag.Append("<div class='" + CssClass + "'>");

_sbHtmlTag.Append("City: ");

_sbHtmlTag.Append(_adr.City);

_sbHtmlTag.Append("</div>");

}

tblCell.Text = _sbHtmlTag.ToString();

break;

case ListItemType.Footer:

break;

}

}

protected Employee.Address GetUnderlyingValue(object dataItem)

{

PropertyDescriptor _prpDesc = TypeDescriptor.GetProperties(dataItem).Find(this.DataField,true);

if(_prpDesc == null)

{

throw new HttpException("Field Not Found: " + this.DataField);

}

return ((Employee.Address)_prpDesc.GetValue(dataItem));

}

#endregion

}

}

My Aspx page is like this: Here in item template i am calling a function "ConcatnatFirstNameLastName(string,string)" which will concatnate employee first name and employee last name and gives employee name. this function is defined in code behind of aspx page and it's access modifier is protected, private cannot be access

<asp:datagridid="dgEmployee"runat="server"ShowFooter="True"AutoGenerateColumns="False" ID="Datagridid1" NAME="Datagridid1">
<FooterStyle ForeColor="#330099"></FooterStyle>
<SelectedItemStyle ForeColor="#663399" </SelectedItemStyle>
<ItemStyle ForeColor="#330099"></ItemStyle>
<HeaderStyle Font-Bold="True" ForeColor="#FFFFCC"></HeaderStyle>
<Columns>

<asp:BoundColumn DataField="EmpID" HeaderText="Emp ID"></asp:BoundColumn>

<asp:TemplateColumn HeaderText="Employee Name">

<ItemTemplate>

<label>

<%# ConcatnatFirstNameLastName((string)DataBinder.Eval(Container.DataItem,"EmpFirstName"),DataBinder.Eval(Container.DataItem,"EmpLastName").ToString()) %>

</label>

</ItemTemplate>

</asp:TemplateColumn>

<Custom:AddressColumn DataField="EmpAddress" HeaderText="Address" CssClass="BlueBlock"></Custom:AddressColumn>

<Custom:SumColumn DataField="Age" ShowAverage="true" DataFormatString="{0:F}" HeaderText="Age"></Custom:SumColumn>

<Custom:SumColumn DataField="Salary" ShowSum="true" ShowAverage="true" DataFormatString="{0:C}" HeaderText="Salary"></Custom:SumColumn>

</Columns>

</asp:datagrid>

And in the code behind, we would do in the page load event like below:

private void Page_Load(object sender, System.EventArgs e)

{

DataClass _dataClass = new DataClass();

dgEmployee.DataSource = _dataClass.GetEmpInfo();

dgEmployee.DataBind();

}

protected string ConcatnatFirstNameLastName(string subject,string teacher)

{

string strResult = "";

if(subject != null)

{

strResult += subject;

}

if(teacher != null)

{

if(strResult != "")

{

strResult += " ";

}

strResult += teacher;

}

return strResult;

}

The above example DataClass is nothing but a class which creates a dummy data of employee and GetEmpInfo() method returns a arraylist containing the employee records.

I think that the above code is quite self explanatory

Conclusion:

In this small example I tried to show you how to customize our datagrid column according to our requirement with good structured manner and also a good performance. That is why I have given here three different example with three different column "EmpName" which shows the employee name with concatenating the first name and last name of employee, "Age" and "Salary" which are achieved by Our own new custom column SumColumn derived from BoundColumn having the functionality of showing the sum/average of values of the column in the footer of the DataGrid. This is just one example of a reusable DataGrid column and it is up to you to examine your own applications and find out what could be neatly wrapped up into a custom DataGrid column.

Note

  • The downloadable code is not of production quality. The whole sole purpose was to initiate a thought that repetitive code can be wrapped inside a module.

References:

Download source code - 20.9 KbDownload source code - 20.9 Kb

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here