Blue Theme Orange Theme Green Theme Red Theme
 
Home | Forums | Videos | Photos | Blogs | Beginners
 | Consulting  
Submit an Article Submit a Blog 
 Jump to
Skip Navigation Links
TechnologyExpand Technology
WebsiteExpand Website
 Resources  
Close
 Our Network  
Close
Search :       Advanced Search »
Home » ASP.NET and Web » SumColumn: Custom DataGrid Column that automatically shows Sum/Count/Average in DataGrid Footer.

SumColumn: Custom DataGrid Column that automatically shows Sum/Count/Average in DataGrid Footer.


A normal requirement in any ASP.Net application is to sum/average up the values in a DataGrid column to the Footer. In this article we will compare the normal methods and later create our own custom DataGrid column "SumColumn", derived from BoundColumn to avoid repetition of the same code.

Total page views :  41006
Total downloads :  1066
   Print Read/Post comments Post a comment  Similar Articles  
   Email to a friend  Bookmark  Author's other articles  
Download Files:
SumColumn_src2.zip | SumColumn_demo2.zip
 
Become a Sponsor

Introduction

A normal requirement in any ASP.Net application is to sum/average up the values in a DataGrid column to the Footer. In this article we will compare the normal methods and later create our own custom DataGrid column "SumColumn", 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 Name, Salary and Bonus. Normal solutions are

  • Query database for the sum:

    One way is to compute the sum of the data through a SQL query. For example, to compute the sum of all salaries in Employee Table, the following SQL statement could be used:

    SELECT SUM(Salary) FROM Employees

    After firing the query using ADO.Net, we will save the result of query in a local variable e.g. internalSum. Dividing internalSum by the no of Items in DataGrid will yield average. Then we have to loop through the DataGrid Items Collection to find the Footer and set the value.

    For Each dgItem In Me.dgEmployee.Items

        If dgItem.ItemType = ListItemType.Footer Then

              values = "Sum : " & internalSum & "<br>"

              values += "Average : " & (internalSum / Me.dgEmployee.Items.Count)

              dgItem.Cells(0).Text = values

        End If

    Next


    Disadvantages:


    • Two round trips to the database server; one for records and one for sum.

    • Also, if you want to compute sum for Bonus as well, then one will need one more SQL query i.e. no of queries will be (n+1) where n is the number of columns you want to sum up.

    • To show the sum in the Footer, one has to loop through the Items collection of the DataGrid, checking the Footer ItemType, setting the value. 

  • Use DataTable  Compute() Method:

    Another way is to use the Compute() method of the DataTable i.e. to get the same output as in first approach, we have to do like

    internalSum = dtEmployee.Compute("SUM(Salary)", String.Empty).ToString
    internalAvg = dtEmployee.Compute("AVG(Salary)",
    String.Empty).ToString

    Now do the above described looping technique and set the value in DataGrid footer.

    Disadvantages:

    • The results of the SQL query, the one that's being bound to the DataGrid, must be retrieved in a DataTable; no custom collection, Array or ArrayList will work.

    • To show the sum in the Footer, one has to loop through the Items collection of the DataGrid.

  • Use DataGrid ItemDataBound Event:

    The idea is to use the ItemDataBound event of the DataGrid, grabbing the corresponding item value of all the rows of the DataGrid and summing it up. We have to keep the count of Items as well to calculate average. Then we could display the values in the Footer.

    Private Sub dgEmployee_ItemDataBound(ByVal sender As Object, _

         ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs)

      Handles dgEmployee.ItemDataBound

            Select Case e.Item.ItemType

                Case ListItemType.Item, ListItemType.AlternatingItem

                    internalSum += CType(e.Item.Cells(1).Text, Double)

                    internalCount += 1

                Case ListItemType.Footer

                    e.Item.Cells(0).Text = "Sum : " & Me.FormatDataValue(internalSum) & "<br>"

                    e.Item.Cells(0).Text += "Count : " & internalCount & "<br>"

                    e.Item.Cells(0).Text += "Average : " & _

                       Me.FormatDataValue(internalSum / internalCount)

            End Select

    End Sub

    ADVANTAGES:

    • The DataSource of the DataGrid can be of any type. It is not restricted to be DataTable as in second solution.

    • Only one query as compare to (n+1) queries in first solution.

    • No extra loop of DataGrid Item Collection to find Footer as in first and second solution. 

Problem

At this point you will be thinking that if the last solution does not have problems, what we are going to do. If it ain't broken, why fix it. Actually I am tired of repeating the same old code in code behind of pages containing DataGrid. Are you not tired as well? I want to do it in an easier way; a way that is elegant as well. What if we do not have to write a single line of code and it will be done automatically. I am sure you will enjoy it as I do. May be you have heard that quote too

Laziness is the mother of invention.

SumColumn for the Rescue

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", 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.

Before we dive into creating our custom column class, let's first look at an example that simply uses a normal BoundColumn control to display the data. For this, we set up a simple DataGrid that displays the Name, Salary and Bonus from Employee Table.

<asp:datagridid="dgEmployee"runat="server"ShowFooter="True"AutoGenerateColumns="False" ID="Datagridid1" NAME="Datagridid1">
      <FooterStyleForeColor="#330099"></FooterStyle>
      <SelectedItemStyleForeColor="#663399" </SelectedItemStyle>
      <ItemStyleForeColor="#330099"></ItemStyle> 
      <HeaderStyleFont-Bold="True"ForeColor="#FFFFCC"></HeaderStyle>
      <Columns>
            < asp:BoundColumn DataField ="Name" HeaderText ="Name"></ asp:BoundColumn >
            < asp:BoundColumn DataField ="Bonus" HeaderText ="Bonus">
</ asp:BoundColumn >
            < asp:BoundColumn DataField ="Salary" HeaderText ="Salary">
</ asp:BoundColumn >
      </Columns>
</
asp:datagrid>

And in the code behind, we would do in the page load event.

dgEmployee.DataSource = GetEmployees()
dgEmployee.DataBind()

Where GetEmployees() will return a DataTable containing the employee records. Normal output will be like (depending upon the data)

1st Pass: Creating the Initial Draft

If you have Visual Studio .NET, create a new VB.Net project of type Class Library named CustomWebControls. You will need to add the reference of System.Web.dll assembly in your project. Otherwise create a file SumColumn.vb. We will make the class step by step as we proceed.

Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.ComponentModel
Public Class SumColumn
     Inherits BoundColumn
End Class

BoundColumn class has two methods that are normally overridden to provide some custom functionality. One is FormatDataValue() and other is InitializeCell(). The FormatDataValue method is normally used along with the DataFormatString property to format numeric and date information. Therefore we are going to use the second one i.e. InitializeCell. This method is much like the DataGrid ItemCreated method. The signature of the method is

Public Overrides Sub InitializeCell(ByVal cell As System.Web.UI.WebControls.TableCell, _
ByVal columnIndex As Integer, ByVal itemType As System.Web.UI.WebControls.ListItemType)

We do not have the value of cell at this time because cell is just being created. We should somehow link ourselves with the binding of the cell. This can be done by attaching a handler on the fly to the cell DataBound event

Public Overrides Sub InitializeCell(ByVal cell As System.Web.UI.WebControls.TableCell, _
   ByVal columnIndex As Integer, ByVal itemType As System.Web.UI.WebControls.ListItemType)
        MyBase.InitializeCell(cell, columnIndex, itemType)
        Select Case itemType
            Case ListItemType.AlternatingItem, ListItemType.Item, ListItemType.Footer
                AddHandler cell.DataBinding, AddressOf CellItemDataBound
        End Select
End Sub

We have called the InitializeCell method of the base class so that it can do the routine work. We have also filter the ItemType using select case because we only want to deal with Item, AlternalteItem and Footer. Our method, CellItemDataBound has been attached with the DataBinding event of cell. Inside this method we will do our magic. Just recall the third solution described previously.

Private Sub CellItemDataBound(ByVal sender As Object, ByVal e As EventArgs)
        Dim cell As TableCell = CType(sender, TableCell)
        Dim DGI As DataGridItem = CType(cell.NamingContainer, DataGridItem)
        Dim dValue As Decimal
        Dim dataItem As Object = DGI.DataItem
        Select Case DGI.ItemType
            Case ListItemType.AlternatingItem, ListItemType.Item
                dValue = DGI.DataItem(DataField)
                internalSum += dValue
                internalCount += 1
                cell.Text = Me.FormatDataValue(dValue)
            Case ListItemType.Footer
                cell.Text = "Sum : " & Me.FormatDataValue(internalSum) & "<br>"
                cell.Text += "Count : " & internalCount & "<br>"
                cell.Text += "Average : " & Me.FormatDataValue(internalSum / internalCount)
        End Select
End Sub

Code is pretty straight forward. We just grabbed the DataItem out of the sender object, and then check the ItemType. If it is Item or AlternateItem, we set the text of the cell; add to internalSum and increment the internalCount. If it is Footer, we just concatenate the values and set the text of cell. Please note that we have called FormatDataValue method of base class to format the output. No complications till yet.

Compile the class library project. Add the reference of its output to your web project. Register the tag at the top of aspx page.

<%@ Register TagPrefix="Custom" Namespace="CustomWebControls" Assembly="CustomWebControls" %>

Use the new SumColumn instead of BoundColumn for Salary in the aspx.

<Columns>
    < asp:BoundColumn DataField ="Name" HeaderText ="Name"></ asp:BoundColumn >
    < asp:BoundColumn DataField ="Bonus" HeaderText ="Bonus">
</ asp:BoundColumn >
    <Custom:SumColumn DataField="Salary" HeaderText="Salary" DataFormatString="{0:C}"></Custom:SumColumn>
</
Columns>

If we run our web project, the output will be more or less like

 

2nd Pass: Bindable with all DataSources

Isn't it great? We did not write a single line of code in the code behind of WebForm and we are getting sum/count/average in the Footer. You can do the same with the bonus column by just changing the column type to SumColumn. But there is just one little problem. This control works well if the DataSource of DataGrid is DataTable or DataReader. If you try to use Array or ArrayList of custom objects, you will probably see the output like

We have to think of a way that can handle any type of DataSource. Hmmm.. What about using PropertyDescriptor class of System.ComponentModel namespace to get the value regardless of the underlying object. We will update the following line in  CellItemDataBound  

dValue = DGI.DataItem(DataField)

to the line below 

dValue = Me.GetUnderlyingValue(dataItem)

and add a new method GetUnderlyingValue in our class.

Protected Function GetUnderlyingValue(ByVal dataItem As Object) As Decimal

 

        Dim boundFieldDesc As PropertyDescriptor = _

          TypeDescriptor.GetProperties(dataItem).Find(Me.DataField, True)

 

        If (boundFieldDesc Is Nothing) Then

            Throw New HttpException("Field Not Found: " + Me.DataField)

        End If

 

        Dim dValue As Object = boundFieldDesc.GetValue(dataItem)

        Return Decimal.Parse(dValue.ToString())

 

End Function

Instead of just pulling the value out of DataItem, we are depending on GetUnderlyingValue Method to get value of the DataItem for us which in turn use TypeDescriptor class to check whether the DataField exists in the underlying object. If succeed, it returns the value to the calling method otherwise throw Exception. Now you can check the output with (nearly) all kinds of DataSource.

3rd Pass: Customizable Output

All seems fine now but there is no control on the output. What I mean is may be you want to just show sum, no average, no count; someone else wants to show sum and average. There are different possibilities so there should be some way to customize the output as per needed. Here is what we can do.

#Region " Attributes " Private internalSum As Decimal

    Private internalCount As Integer

    Private _ShowSum As Boolean = True

    Private _ShowCount As Boolean = True

    Private _ShowAverage As Boolean = True

#End Region

#Region " Properties "

    Public Property ShowSum() As Boolean

        Get

            Return _ShowSum

        End Get

        Set(ByVal Value As Boolean)

            _ShowSum = Value

        End Set

    End Property

 

    Public Property ShowCount() As Boolean

        Get

            Return _ShowCount

        End Get

        Set(ByVal Value As Boolean)

            _ShowCount = Value

        End Set

    End Property

 

    Public Property ShowAverage() As Boolean

        Get

            Return _ShowAverage

        End Get

        Set(ByVal Value As Boolean)

            _ShowAverage = Value

        End Set

    End Property

#End Region

We exposed three public properties i.e. ShowSum, ShowCount, ShowAverage in our SumColumn class. One can use these properties in aspx to customize the output e.g.

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

OR

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

Internally in our class, we can check the values of exposed properties in the CellItemDataBound method to customize the output according to requirement.

Case ListItemType.Footer

 If Me._ShowSum = True Then

  cell.Text = "Sum : " & Me.FormatDataValue(internalSum) & "<br>"

 End If

 

 If Me._ShowCount = True Then

  cell.Text += "Count : " & internalCount & "<br>"

 End If

 

 If Me._ShowAverage = True Then

  cell.Text += "Average : " & Me.FormatDataValue(internalSum / internalCount)

 End If

End If

If you update the code and aspx, you may get the output as below

4th Pass: Tweaking the Design Time Output

Yes, I know that now you all are feeling sleepy but please give me just 5 more minutes. If you are not interested in changing the design time output (i.e. when we see the DataGrid in design time using Visual Studio), then you can skip this section. For those who are still reading, check the change in code below.

Case ListItemType.AlternatingItem, ListItemType.Item

 

 If Me.DesignMode = False Then

  dValue = Me.GetUnderlyingValue(dataItem)

  .....

  .....

  cell.Text = Me.FormatDataValue(dValue)

 Else

  cell.Text = "SumColumn"

 End If

 

Case ListItemType.Footer

 If Me.DesignMode = False Then

  If Me._ShowSum = True Then

  .....

  .....

  End If

 Else

  cell.Text = "Total"

 End If

I think that the above code is quite self explanatory. We simply used the DesignMode property of the base class BoundColumn to tweak the design time output of the SumColumn .

 

Conclusion

Now we have it. Our own new custom column SumColumn derived from BoundColumn having the functionality of showing the sum/average/count 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.

  • The code contains a custom class Employee which return the records in different formats e.g. DataTable, ArrayList and Array. One can use the sample page Test.aspx to check with all three type of DataSource.

  • This control has not been tested using XML DataSource.

References


Login to add your contents and source code to this article
 About the author
 
Syed Aziz ur Rahman
Software Developer currenlty working in SEC, Riyadh, Saudi Arabic
Looking for C# Consulting?
C# Consulting is founded in 2002 by the founders of C# Corner. Unlike a traditional consulting company, our consultants are well-known experts in .NET and many of them are MVPs, authors, and trainers. We specialize in Microsoft .NET development and utilize Agile Development and Extreme Programming practices to provide fast pace quick turnaround results. Our software development model is a mix of Agile Development, traditional SDLC, and Waterfall models.
Click here to learn more about C# Consulting.
 
Introducing MaxV - one click. infinite control. Hyper-V Hosting from MaximumASP.
Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Dynamic PDF
ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications.
SQL and .NET performance profiling in one place
Investigate SQL and .NET code side-by-side with ANTS Performance Profiler 6, so you can see which is causing the problem without switching tools.
Go.NET
Build custom interactive diagrams, network, workflow editors, flowcharts, or software design tools. Includes many predefined kinds of nodes, links, and basic shapes. Supports layers, scrolling, zooming, selection, drag-and-drop, clipboard, in-place editing, tooltips, grids, printing, overview window, palette. 100% implemented in C# as a managed .NET Control. Document/View/Tool architecture with many properties&events. Optional automatic layout.
Dundas Software
Dundas Chart for .NET is the most advanced .NET charting package available today.  With an extremely complete feature set, elegant architecture and easy implementation, Dundas Chart can quickly add advanced Charting functionality to enhance and transform ASP.NET and Windows Forms applications.  Whether you are implementing charting into internal projects, or building applications for clients, Dundas Chart offers advanced technology and advanced results to get the most out of data.
60 FREE UI Controls from DevExpress
Register for your FREE copy on over 60 free presentation controls from DevExpress - Absolutely Free-of-Charge without any royalties or distribution costs. Visit Devexpress.com/60 today. Free controls include advanced lists box, dropdown calendar, rich text edit, spin edit, tab control and so much more!

DevExpress engineers feature rich presentation controls and reporting tools for WinForms, ASP.NET, WPF, and Silverlight. Our technologies help you build your best, see complex software with greater clarity and deliver compelling business solutions for Windows and the web in the shortest possible time.
Clickatell's SMS Gateway
Clickatell's Developer Solutions allow you to SMS enable any website or application via a range of API's. Learn More about our API connections.
Free access to .NET Memory Management video
Everything you need to know about Garbage Collection, Temporary Objects, Fragmentation, Finalization and common causes of memory leaks in .NET. Watch the video here.
Microsoft Visual Studio 2010
Visualize your workspace with new multiple monitor support, powerful Web development, new SharePoint support with tons of templates and Web parts, and more accurate targeting of any version of the .NET Framework. Get set to unleash your creativity.
Nevron Chart for .NET 2010.1 Now Available
The leading .NET charting control now features PDF, Flash and Silverlight export, visualization of large datasets and more. Deliver true charting functionality to your BI, Scorecard, Presentation or Scientific apps. Download evaluation now.
Developer-Ready ASP.NET 2.0 Web Hosting with 3 MONTHS FREE
Now supporting .NET 3.0 Framework with Windows Workflow Foundation, Windows Communication Foundation (WCF), Windows Presentation Foundation (WPF), windows CardSpace (WCS)! Providing more flexibility for Developers with Web Services Support and a User/Permission Manger. Also supporting MS SQL 2005/2000 with Real-Time Backups, FREE Automated Attach .MDF Tool, FREE SQL Restore and Shrink SQL DB Tools, and SQL
Read the Top 10 Books for Microsoft Developers, 15 Days FREE
Read the Top 10 Books for Microsoft Developers, 15 Days FREE
Try Safari Books Online - 15 Days FREE + 15% Off for 1 Year
Try Safari Books Online - 15 Days FREE + 15% Off for 1 Year
 
 Post a Feedback, Comment, or Question about this article
Subject:
Comment:
Become a Sponsor
 Comments
sum column by rama On February 21, 2006

It is very useful application. while way of telling the application is also very good. my email is ramakishoreiic@gmail.com

 

 

Regards,

ramakishore.

[edit]
Reply | Email | Delete | Modify | 
Nice by Jorge On May 20, 2009
Very useful, i'm using this right away.
Reply | Email | Delete | Modify | 
Getting Compile Error by shafaqat On May 21, 2009
System.Web.UI.WebControls.DataControlFieldCollection must have items of type 'System.Web.UI.WebControls.DataControlField'. 'Custom:CustomColumn' is of type 'Custom.CustomColumn'.

"Custom"=My ClassLibraryName
"CustomClass"= my class name
Reply | Email | Delete | Modify | 
Why not backend it all? by Steve On May 26, 2009
I did something mildly similar before. Rather than write ASP.Net code to summarize data, I used T-SQL and temporary tables.

One grid that represents the detail of the data, a second one that summarizes, in this case money, values such as Month-To-Date and Year-To-Date.

Both are temporary tables returned by the stored procedure but who's life-span is the execution of the procedure. It's quick and clean, and all the logic and work is back-ended to the server.

The ASP.Net code is just a few lines, amounting to assigning the result set from the procedure to the grid.
Reply | Email | Delete | Modify | 
Re: Why not backend it all? by Syed Aziz ur Rahman On May 26, 2009

Hi,
You are right and there are several other methods to perform the task done in the artcile. But you missed the point. My focus was just to do some kind of datagrid related task encapsulated inside a custom column so that it can be reuse in every other applications. The task can range from formatting to other repeated complex operations in a particular application.

I hope i cleared the point.

Reply | Email | Delete | Modify | 

 Hosted by MaximumASP  |  Found a broken link?  |  Contact Us  |  Terms & conditions  |  Privacy Policy  |  Site Map  |  Suggest an Idea  |  Media Kit
Current Version: 5.2010.8.14
 © 2010  contents copyright of their authors. Rest everything copyright Mindcracker. All rights reserved.