Spice Up Your Ad Reports - Using the ASP.NET Repeater Control to Group Data and Add Totals

Posted by: Suprotim Agarwal , on 9/25/2008, in Category ASP.NET
Views: 61816
Abstract: The Repeater control is truly amazing. Being template driven, it gives you a lot of flexibility to manipulate and render the final output. In this article, we will exploit this flexibility of the ASP.NET Repeater and build a sample to demonstrate how to use the templates to group and display data. We will also use a little code to ‘total’/sum up our data and display it on the fly.
Spice Up Your Ad Reports - Using the ASP.NET Repeater Control to Group Data and Add Totals
 
The Repeater control is truly amazing. Being template driven, it gives you a lot of flexibility to manipulate and render the final output. In this article, we will exploit this flexibility of the ASP.NET Repeater and build a sample to demonstrate how to use the templates to group and display data. We will also use a little code to ‘total’/sum up our data and display it on the fly. At the end, we will apply css to design a beautiful report from the Repeater control. I have borrowed the css from the blog of a great designer guy called Veerle
I have chosen to take the example of displaying ‘advertisement’ data through the Repeater control. For simplicity sake, I will assume two tables related to website advertisement and then show you how to use the template driven Repeater control to display and group advertisement data and add a total at runtime. I assume you have used the Repeater control and are familiar with it. So here we go…
Database Script
This is a sample db script on which I will base my demonstration. Please note that this script in no way portrays a good DB design. I have kept the design simple so as to keep the focus more on the Repeater control, rather than turning this article into ‘DB Normalization Best Practices’ one. The database of our sample application is called ‘MYWEBSITE’ and the two tables are ‘Ads’ and ‘AdsStats’
Sample Schema
CREATE DATABASE MYWEBSITE
GO
 
USE [MYWEBSITE]
GO
CREATE TABLE [dbo].[Ads](
      [AdId] [int] IDENTITY(1,1) NOT NULL,
      [AdName] [nvarchar](100) NULL,
      [Comments] [nvarchar](40) NULL
) ON [PRIMARY]
GO
 
USE [MYWEBSITE]
GO
CREATE TABLE [dbo].[AdsStats](
      [Id] [int] IDENTITY(1,1) NOT NULL,
      [AdId] [int] NOT NULL,
      [AdDate] [datetime] NULL,
      [AdType] [int] NULL
) ON [PRIMARY]
GO
Sample Data
INSERT INTO [Ads] VALUES('AD1','Leaderboard');
INSERT INTO [Ads] VALUES('AD2','Banner');
INSERT INTO [Ads] VALUES('AD3','TextAds');
 
INSERT INTO [AdsStats] VALUES(1,'2008-09-23 04:47:33.000',0);
INSERT INTO [AdsStats] VALUES(1,'2008-09-23 06:17:33.000',0);
INSERT INTO [AdsStats] VALUES(2,'2008-09-23 07:42:33.000',0);
INSERT INTO [AdsStats] VALUES(2,'2008-09-23 08:32:33.000',1);
INSERT INTO [AdsStats] VALUES(3,'2008-09-23 08:41:33.000',1);
INSERT INTO [AdsStats] VALUES(1,'2008-09-23 19:27:33.000',0);
INSERT INTO [AdsStats] VALUES(2,'2008-09-23 19:37:33.000',0);
INSERT INTO [AdsStats] VALUES(1,'2008-09-23 19:47:33.000',0);
INSERT INTO [AdsStats] VALUES(1,'2008-09-24 01:47:33.000',0);
INSERT INTO [AdsStats] VALUES(1,'2008-09-24 02:17:33.000',1);
INSERT INTO [AdsStats] VALUES(3,'2008-09-24 03:22:33.000',0);
INSERT INTO [AdsStats] VALUES(2,'2008-09-24 04:32:33.000',0);
INSERT INTO [AdsStats] VALUES(1,'2008-09-24 05:11:33.000',0);
INSERT INTO [AdsStats] VALUES(1,'2008-09-25 02:27:33.000',0);
INSERT INTO [AdsStats] VALUES(3,'2008-09-25 03:47:33.000',0);
INSERT INTO [AdsStats] VALUES(2,'2008-09-25 04:17:33.000',0);
INSERT INTO [AdsStats] VALUES(3,'2008-09-25 05:12:33.000',1);
INSERT INTO [AdsStats] VALUES(2,'2008-09-25 05:32:33.000',1);
INSERT INTO [AdsStats] VALUES(3,'2008-09-25 06:41:33.000',0);
INSERT INTO [AdsStats] VALUES(1,'2008-09-25 07:47:33.000',0);
 
Now with the sample schema and data ready, we will move ahead to design our page that will group and display the advertisement data kept in these two tables. To understand what the data is about and what we will be doing with it, here’s a brief up. We will assume that there are 3 different kinds of ads our sample ‘MyWebSite’ serves : Leaderboard, Banner and TextAds. The ‘AdType’ column in AdsStats table displays the impressions and clicks that an advertisement receives. 0 means Impressions and 1 means a Click.
Our purpose is to display a daily report; that is to group the advertisement data based on date. We will also add a total at the end of each date, thereby displaying how many impressions and clicks were received from the different ads displayed, per day. I hope the case scenario is clear. If it isn’t, just wait till the end of this article and it will be. Let us now implement the functionality.
Create an ASP.NET website. Add a web.config file to the website to store the connection string as shown below:
<connectionStrings>
      <remove name="AdConnString"/>
      <add name="AdConnString" connectionString="Data Source=SUPROTIM; Initial Catalog=MYWEBSITE; Integrated Security = True; "providerName="System.Data.SqlClient"/>
</connectionStrings>
To display a group wise data, we will have to use Nested Repeaters. So our parent repeater will contain a label(lblDate) control to display the Date (for grouping). Based on this grouping, we will then pull related data in our child repeater. The Nested child repeater will be contained in the <ItemTemplate> of the Parent Repeater to replicate a display of hierarchical data.
SQL Queries:
The Repeaters will be bound to two different SQLDataSources. The queries for the two data sources are as follows:
Query for Parent Repeater - The Parent Repeater will display the date, to group the data. The query for it will look similar to the following:
SELECT DISTINCT CONVERT(VARCHAR(12), AdDate, 106) as AdDate
FROM AdsStats ORDER BY AdDate
As shown above, the query selects distinct date from the AdsStats table. Running the query displays the following result:
Date Query
Query for Child Repeater – The Child Repeater will display the daily advertisement data. This data will be grouped based on the date in our Parent Repeater control. The query for displaying data in the child repeater will look similar to the following:
SELECT DATEADD(dd,DATEDIFF(dd,0,ast.AdDate),0) as AdDate,al.Comments as AdComments,
SUM(CASE WHEN AdType=0 THEN 1 ELSE 0 END) AS AdImpression,
SUM(CASE WHEN AdType=1 THEN 1 ELSE 0 END) AS AdClicks
FROM Ads al INNER JOIN AdsStats ast ON ast.AdId =al.AdId
GROUP BY DATEADD(dd,DATEDIFF(dd,0,AdDate),0), al.Comments
Running the query will display the following result:
Grouped Data Query
With our queries ready, we now need to add markup to display the data. The markup for grouping and displaying data is as shown below. I have also added css to beautify it:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>My Ad Reports</title>
<style type="text/css">
body
{
font: normal 11px auto "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;   
background-color: #ffffff;
color: #4f6b72;      
}
 
#mytable
{
width: 700px;
padding: 10px;
margin: 10px;
}
 
th {
font: bold 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #4f6b72;
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
border-top: 1px solid #C1DAD7;
letter-spacing: 2px;
text-transform: uppercase;
text-align: left;
padding: 6px 6px 6px 12px;
background: #D5EDEF;
}
 
td {
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
background: #fff;
padding: 6px 6px 6px 12px;
color: #4f6b72;
}
 
 
td.alt
{
background: #F5FAFA;
color: #797268;
}
 
td.boldtd
{
font: bold 13px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
background: #D5EDEF;
color: #797268;
}
</style>
 
</head>
<body style="background-color:White">
<form id="form1" runat="server">
<div>
 
<h3>Daily Activity Reports</h3><br />
<asp:Repeater ID="ParentRepeater" runat="server" DataSourceID="SqlDataSource1">               
<ItemTemplate>            
   <p style="margin-left:10px"><b><asp:Label ID="lblDate" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "AdDate", "{0: dd MMM}")%>'></asp:Label></b></p>
     <asp:Repeater ID="ChildRepeater" runat="server" DataSourceID="SqlDataSource2">
     <HeaderTemplate>
         <table id="mytable"> 
         <tr>
                <th>Banner Type</th>
                <th>Impressions</th>
                <th>Clicks</th>
        </tr>                           
    </HeaderTemplate>
    <ItemTemplate>
     <tr>
       <td>
        <%# Eval("AdComments")%></td>
        <td> <%# Eval("AdImpression")%> </td>
        <td> <%# Eval("AdClicks")%></td>
     </tr>               
    </ItemTemplate>
   
    <AlternatingItemTemplate>
         <tr>
            <td class="alt">
            <%# Eval("AdComments")%></td>
            <td class="alt"> <%# Eval("AdImpression")%> </td>
            <td class="alt"> <%# Eval("AdClicks")%></td>
        </tr>               
    </AlternatingItemTemplate>
   
    <FooterTemplate>      
        </table><br />
    </FooterTemplate>
</asp:Repeater>
   
    <asp:SqlDataSource ID="SqlDataSource2" runat="server" ConnectionString="<%$ ConnectionStrings:AdConnString %>"
        SelectCommand="SELECT DATEADD(dd,DATEDIFF(dd,0,ast.AdDate),0) as AdDate,al.Comments as AdComments, SUM(CASE WHEN AdType=0 THEN 1 ELSE 0 END) AS AdImpression, SUM(CASE WHEN AdType=1 THEN 1 ELSE 0 END) AS AdClicks FROM Ads al INNER JOIN AdsStats ast ON ast.AdId =al.AdId WHERE AdDate >= DATEADD(dd, DATEDIFF(dd, 0, @AdDate), 0) and AdDate <DATEADD(dd, DATEDIFF(dd, 0, @AdDate), 1) GROUP BY DATEADD(dd,DATEDIFF(dd,0,AdDate),0), al.Comments">
   <SelectParameters>
      <asp:ControlParameter ControlID="lblDate" Name="AdDate" />
    </SelectParameters></asp:SqlDataSource>           
  
</ItemTemplate>
</asp:Repeater>
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:AdConnString %>"
    SelectCommand="SELECT DISTINCT CONVERT(VARCHAR(12), AdDate, 106) as AdDate FROM AdsStats ORDER BY AdDate"></asp:SqlDataSource>
</div>
<br />       
 
</form>
</body>
</html>
 
The parent repeater contains a label control (lblDate). The lblDate gets its value from the SqlDataSource1 using DataBinder.Eval as shown below:
<asp:Label ID="lblDate" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "AdDate", "{0: dd MMM}")%>'></asp:Label>
Note that the <SelectCommand> query for the child repeater has been modified to accept the date parameter(@AdDate). This parameter takes its value from the label control (lblDate) kept in the Parent Repeater.
<asp:SqlDataSource ID="SqlDataSource2" runat="server" ConnectionString="<%$ ConnectionStrings:AdConnString %>"
    SelectCommand="SELECT DATEADD(dd,DATEDIFF(dd,0,ast.AdDate),0) as AdDate,al.Comments as AdComments, SUM(CASE WHEN AdType=0 THEN 1 ELSE 0 END) AS AdImpression, SUM(CASE WHEN AdType=1 THEN 1 ELSE 0 END) AS AdClicks FROM Ads al INNER JOIN AdsStats ast ON ast.AdId =al.AdId WHERE AdDate >= DATEADD(dd, DATEDIFF(dd, 0, @AdDate), 0) and AdDate <DATEADD(dd, DATEDIFF(dd, 0, @AdDate), 1) GROUP BY DATEADD(dd,DATEDIFF(dd,0,AdDate),0), al.Comments">
<SelectParameters>
 <asp:ControlParameter ControlID="lblDate" Name="AdDate" />
</SelectParameters>
This is more or less like displaying data in a hierarchy, where the ‘Date’ is the parent and data related to that ‘Date’ becomes the child data.
The <HeaderTemplate> in the Child Repeater displays the headings for the columns. The <ItemTemplate> and <AlternateItemTemplate> displays the advertisement data and receive the values from SQLDataSource2.
Generate a ‘Total’ in the Repeater control at runtime
The last step is to add a running total to display the total number of impressions and clicks received from all the 3 different advertisements on that particular day. To do so, we will modify the <FooterTemplate> of the child repeater control to add two label controls to display a total of the Impressions and Clicks.
<FooterTemplate>
    <tr class="boldtr" >
        <td class="boldtd">Totals</td>
        <td class="boldtd">
        <asp:Label ID="lblImpressionCount" runat="server" Text=""></asp:Label></td>
        <td class="boldtd"><asp:Label ID="lblClickCount" runat="server" Text=""></asp:Label></td>
    </tr>
    </table><br />
</FooterTemplate>
We will also add the ItemDataBound event handler to the ChildRepeater
<asp:Repeater ID="ChildRepeater" runat="server" DataSourceID="SqlDataSource2" OnItemDataBound="ChildRepeater_ItemDataBound">
We need to add some code to the ItemDataBound event of the Child repeater to calculate the totals. As a note, the ItemDataBound event of the Repeater control is raised when an item is being data-bound, just before the item gets rendered. This gives us an ideal location to calculate our Total on the fly before the data is rendered. The code contains two class level variables cntView and cntClick which are calculated when the ListItemType is either an Item or an AlternatingItem. Finally when we reach the Footer, the data is displayed in the two label controls (lblImpressionCount and lblClickCount). Once displayed, the variables are reset to 0. This operation is repeated for each day (group) in the AdsStats table. Use the following code:
C#
Int64 cntView;
Int64 cntClick;
 
public void ChildRepeater_ItemDataBound(object sender, System.Web.UI.WebControls.RepeaterItemEventArgs e)
{
ListItemType lt = e.Item.ItemType;
if (lt == ListItemType.Item || lt == ListItemType.AlternatingItem)
{
    DataRowView dv = e.Item.DataItem as DataRowView;
    if (dv != null)
    {
        cntView += Convert.ToInt64(dv["AdImpression"]);
        cntClick += Convert.ToInt64(dv["AdClicks"]);
    }
 
}
else if (e.Item.ItemType == ListItemType.Footer)
{
    Label lblCount = e.Item.FindControl("lblImpressionCount") as Label;
    lblCount.Text = cntView.ToString();
 
    Label lblSum = e.Item.FindControl("lblClickCount") as Label;
    lblSum.Text = cntClick.ToString();
    cntView = 0;
    cntClick = 0;
}
}
VB.NET
Private cntView As Int64
Private cntClick As Int64
 
Public Sub ChildRepeater_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs)
Dim lt As ListItemType = e.Item.ItemType
If lt = ListItemType.Item OrElse lt = ListItemType.AlternatingItem Then
      Dim dv As DataRowView = TryCast(e.Item.DataItem, DataRowView)
      If Not dv Is Nothing Then
            cntView += Convert.ToInt64(dv("AdImpression"))
            cntClick += Convert.ToInt64(dv("AdClicks"))
      End If
 
ElseIf e.Item.ItemType = ListItemType.Footer Then
      Dim lblCount As Label = TryCast(e.Item.FindControl("lblImpressionCount"), Label)
      lblCount.Text = cntView.ToString()
 
      Dim lblSum As Label = TryCast(e.Item.FindControl("lblClickCount"), Label)
      lblSum.Text = cntClick.ToString()
      cntView = 0
      cntClick = 0
End If
End Sub
That’s it. A template based, beautifully designed Advertisement Report is ready, all thanks to the wonderful and powerful ASP.NET Repeater control and the nice css designed by Veerle.
When you run the report, the data will be grouped date wise, with a total at the end of each day as shown below:
Ad Page Report
I hope this article was useful and I thank you for viewing it. The source code of the article in both C# and VB.NET can be downloaded over here.

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
Suprotim Agarwal, MCSD, MCAD, MCDBA, MCSE, is the founder of DotNetCurry, DNC Magazine for Developers, SQLServerCurry and DevCurry. He has also authored a couple of books 51 Recipes using jQuery with ASP.NET Controls and The Absolutely Awesome jQuery CookBook.

Suprotim has received the prestigious Microsoft MVP award for Sixteen consecutive years. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that offers Digital Marketing and Branding services to businesses, both in a start-up and enterprise environment.

Get in touch with him on Twitter @suprotimagarwal or at LinkedIn



Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by Raghu on Monday, November 10, 2008 5:14 AM
You project to download has no project except solution file .
Comment posted by Tester on Monday, November 10, 2008 2:00 PM
The zip file does not conatin the source code!
Comment posted by Gustavo on Monday, November 10, 2008 2:30 PM
Sólo está el archivo de solucion, no existe el código fuente del ejmplo.
Comment posted by Suprotim Agarwal on Tuesday, November 11, 2008 12:00 PM
Raghu, Tester, Gustavo:   Apologies. That was a mistake on my part. The correct zip file has been added for download.
Thanks.
Comment posted by michelle sollicito on Thursday, February 18, 2010 9:17 PM
When I type this code in I get an error that says it cant find lblDate - I think I typed everything in correctly - I have double checked everything
Comment posted by Duncan on Friday, March 19, 2010 12:26 PM
Hi, great bit of code I found it very useful. I have one problem though, I have a link at the end of each result that says new line. It basically adds another line to the database based on the line you click. When I do this and try to rebind the data in the repeater I get "The ObjectDatasource control 'ObjectDatasource4' does not have a naming container. Ensure that the the control is added to the page before calling DataBind" - the datasource in question is the one within the ParentRepeater and needs to be there so the header parameter can be passed to it. Any ideas?
Comment posted by Suprotim Agarwal on Monday, March 22, 2010 11:08 PM
Duncan: Are you using any Binding Expressions in your code?
Comment posted by Book Reports on Tuesday, June 15, 2010 4:09 AM
Really great stuff!
Comment posted by Jimmy on Monday, March 12, 2012 8:08 AM
Great Article! Gonna use it for my topic to <a href="http://write-my-essay.com">write my essay</a> on it!
Comment posted by Jose Saiz on Saturday, October 26, 2013 12:21 PM
Great and fast solutions grouping data using the repeater you did a great job I give you 100+
Comment posted by useful reference on Thursday, July 17, 2014 9:58 AM
This web site is really a walk-through for all of the info you wanted about this and did not know who to ask.
Comment posted by Suprotim Agarwal on Saturday, July 19, 2014 5:33 AM
@usefulreference Glad to hear that :)