Tuesday, 29 January 2013

Group Total and Grand Total in GridView – Part 10

I last post we had seen how to show Group Total in GridView, where the group total will show while starting of the group instead of end of group. This gives functionality such as pivot table in Excel Sheet.

In this post, I am planning to extend the same examples and add one more functionality such as expanding and collapsing the groups with corresponding images. This provides a way to hide/show particular groups to analyze much better way.

As shown in last post, I have two examples which are extended with below functionalities.

First Example (One level Grouping)
  1. The records defined in the XML should bound to the Grid View in a normal way.
  2. The records should be grouped by Year and the Group Total should be shown at the beginning of each group.
  3. The Group Total must be displayed with different background color to differentiate the groups.
  4. The groups must extendable/collapsible with images on each group total.

Second Example (Three level Grouping)

This example shows profit and loss sheet of market shares. The data has taken from a public a site.
  1. The records defined in the XML should bound to the Grid View in a normal way.
  2. The records should be grouped by Sector in first level, Name of the Company in second level and the Income/Expense details in third level. The Group Total should be shown at the beginning of each group.
  3. The Sector, Company Name, Income/Expense groups must be displayed with different background color to differentiate the groups.
  4. At each of the group total, there much be an expendable/collapsible images which can be used to expand/collapse that particular group.
  5. When collapsing a particular group, entire child group under the parent group must get collapsed. When expanding the same group, all the child groups must get expanded.
  6. In another example, the GridView also must provide a way to expand only the next level group. So when expanding a parent group, the GridView must expand only the next level child group. To expand the other next level child group, user action required. This will be useful when user what to analyze only a particular parent group and next level groups.

This requirement talks about having three different groups, Sector is the first group and Company Name is the second group and Income/Expense is the third group. So the grid will have one or more Sector and each sector will have one or more Company Name. Each company name will have one or more Income/Export group.

Before going for actual implementation, please note the following points -
  1. To implement these examples, all the records must show in a single page of the Grid View (So, no pagination). Because for calculating the Group Total, the code required all the records must be in loop.
  2. The records must be sorted on the group wise. So all the records related to a particular group will show one after another. It will be useful for calculating cumulative values together. Keeping records in different group will be considered as separate group and cumulative values will be calculated as another separate group. As we have three groups in this example, we must sort by Sector at first and then Company Name and then Income/Export.

The First Example implementation goes as below –
The XML source which bond to the GridView
<?xml version="1.0" encoding="utf-8" ?>
<RevenueReport>
     
    <Data Year="2008" Period="Q1" AuditedBy="Maria Anders" DirectRevenue="12500.00" ReferralRevenue="2500.00" />
    <Data Year="2008" Period="Q2" AuditedBy="Ana Trujillo" DirectRevenue="21000.00" ReferralRevenue="8000.00" />
    <Data Year="2008" Period="Q3" AuditedBy="Antonio Moreno" DirectRevenue="20000.00" ReferralRevenue="5000.00" />
    <Data Year="2008" Period="Q4" AuditedBy="Thomas Hardy" DirectRevenue="25000.00" ReferralRevenue="1200.00" />
   
    <Data Year="2009" Period="Q1" AuditedBy="Christina Berglund" DirectRevenue="72500.00" ReferralRevenue="5000.00" />
    <Data Year="2009" Period="Q2" AuditedBy="Hanna Moos" DirectRevenue="15000.00" ReferralRevenue="6500.00" />
    <Data Year="2009" Period="Q3" AuditedBy="Thomas Hardy" DirectRevenue="25000.00" ReferralRevenue="1520.00" />
    <Data Year="2009" Period="Q4" AuditedBy="Martín Sommer" DirectRevenue="42000.00" ReferralRevenue="2580.00" />
   
    <Data Year="2010" Period="Q1" AuditedBy="Laurence Lebihan" DirectRevenue="12500.00" ReferralRevenue="1500.00" />
    <Data Year="2010" Period="Q2" AuditedBy="Elizabeth Lincoln" DirectRevenue="25000.00" ReferralRevenue="5500.00" />
    <Data Year="2010" Period="Q3" AuditedBy="Hanna Moos" DirectRevenue="12000.00" ReferralRevenue="1800.00" />
    <Data Year="2010" Period="Q4" AuditedBy="Antonio Moreno" DirectRevenue="10000.00" ReferralRevenue="1200.00" />

</RevenueReport>
The ASPX script
<asp:GridView ID="grdViewProducts" runat="server" AutoGenerateColumns="False" TabIndex="1"
    Width="100%" DataSourceID="XmlDataSource1" CssClass="grdViewOrders"
    CellPadding="4" ForeColor="Black" GridLines="Vertical"
    OnRowDataBound="grdViewProducts_RowDataBound"
    onrowcreated="grdViewProducts_RowCreated" 
    OnDataBound="grdViewProducts_DataBound"
    BackColor="White" BorderColor="#999999" BorderStyle="Solid" BorderWidth="1px" >
    <Columns>
        <asp:BoundField DataField="" HeaderText="Year">
            <ItemStyle HorizontalAlign="Left" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="Period" HeaderText="Period">
            <ItemStyle HorizontalAlign="Left" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="AuditedBy" HeaderText="Audited By">
            <ItemStyle HorizontalAlign="Left" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="DirectRevenue" HeaderText="Direct">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="ReferralRevenue" HeaderText="Referral">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:TemplateField HeaderText="Total">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell" />
            <HeaderStyle CssClass="DataCell" />
            <ItemTemplate>
                <asp:Label runat="server" ID="lblTotalRevenue" Text="0" />
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
    <RowStyle BackColor="#F7F7DE" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
    <FooterStyle BackColor="#CCCC99" />
    <PagerStyle BackColor="#F7F7DE" ForeColor="Black" HorizontalAlign="Right" />
    <SelectedRowStyle BackColor="#CE5D5A" ForeColor="White" Font-Bold="True" />
    <HeaderStyle BackColor="#6B696B" Font-Bold="True" ForeColor="White" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
    <AlternatingRowStyle BackColor="White" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
    <SortedAscendingCellStyle BackColor="#FBFBF2" />
    <SortedAscendingHeaderStyle BackColor="#848384" />
    <SortedDescendingCellStyle BackColor="#EAEAD3" />
    <SortedDescendingHeaderStyle BackColor="#575357" />
</asp:GridView>
<asp:XmlDataSource ID="XmlDataSource1" runat="server" DataFile="Data/RevenueReport.xml"></asp:XmlDataSource>
The C# Code behind
// To keep track of the previous row Group Identifier
string strPreviousRowID = string.Empty;
int intGroupStartRowIndex = 0;

// To keep track the Index of Group Total
int intSubTotalIndex = 1;

// To temporarily store Sub Total
double dblSubTotalDirectRevenue = 0;
double dblSubTotalReferralRevenue = 0;
double dblSubTotalTotalRevenue = 0;

IList<Total> TotalList;

protected void Page_Load(object sender, EventArgs e)
{
    TotalList = new List<Total>();
}

/// <summary>
/// Event fires for every row creation
/// Used for creating SubTotal row when next group starts by adding Group Total at previous row manually
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void grdViewProducts_RowCreated(object sender, GridViewRowEventArgs e)
{
    bool IsSubTotalRowNeedToAdd = false;

    if ((strPreviousRowID == string.Empty) && (e.Row.RowType == DataControlRowType.DataRow))
    {
        IsSubTotalRowNeedToAdd = true;
        intSubTotalIndex = 1;
    }

    if ((strPreviousRowID != string.Empty) &&
        (e.Row.RowType == DataControlRowType.DataRow) &&
        (strPreviousRowID != DataBinder.Eval(e.Row.DataItem, "Year").ToString())
        )
        IsSubTotalRowNeedToAdd = true;

    if (e.Row.RowType == DataControlRowType.Footer)
        IsSubTotalRowNeedToAdd = false;

    // To add the runing total into List
    if ((e.Row.RowType == DataControlRowType.Footer) ||
        ((e.Row.RowType == DataControlRowType.DataRow) && (IsSubTotalRowNeedToAdd == true) && (strPreviousRowID != string.Empty))
        )
    {
        Total total = new Total();
        total.RowIndex = intGroupStartRowIndex;
        total.DirectRevenue = dblSubTotalDirectRevenue;
        total.ReferralRevenue = dblSubTotalReferralRevenue;
        total.TotalRevenue = dblSubTotalTotalRevenue;
        TotalList.Add(total);
    }

    if (IsSubTotalRowNeedToAdd)
    {
        GridView grdViewProducts = (GridView)sender;

        GridViewRow SubTotalRow = new GridViewRow(0, 0, DataControlRowType.DataRow, DataControlRowState.Insert);

        TableCell cell = new TableCell();

        System.Web.UI.HtmlControls.HtmlImage img = new System.Web.UI.HtmlControls.HtmlImage();
        img.Src = "images/minus.png";
        img.Attributes.Add("alt", DataBinder.Eval(e.Row.DataItem, "Year").ToString() + ",Expanded");
        img.Attributes.Add("class", "ExpandCollapseStyle");
        cell.Controls.Add(img);

        System.Web.UI.HtmlControls.HtmlGenericControl title = new System.Web.UI.HtmlControls.HtmlGenericControl();
        title.InnerText = DataBinder.Eval(e.Row.DataItem, "Year").ToString();
        cell.Controls.Add(title);
        cell.HorizontalAlign = HorizontalAlign.Left;
        cell.ColumnSpan = 3;
        cell.CssClass = "FirstCellSubTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", dblSubTotalDirectRevenue);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SubTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", dblSubTotalReferralRevenue);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SubTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", dblSubTotalTotalRevenue);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SubTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        //Adding the Row at the RowIndex position in the Grid
        grdViewProducts.Controls[0].Controls.AddAt(e.Row.RowIndex + intSubTotalIndex, SubTotalRow);
        intGroupStartRowIndex = e.Row.RowIndex + intSubTotalIndex;
        intSubTotalIndex++;

        dblSubTotalDirectRevenue = 0;
        dblSubTotalReferralRevenue = 0;
        dblSubTotalTotalRevenue = 0;
    }
}

/// <summary>
/// Event fires when data binds to each row
/// Used for calculating Group Total 
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void grdViewProducts_RowDataBound(object sender, GridViewRowEventArgs e)
{
    // This is for calculation of column (Total = Direct + Referral)
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        strPreviousRowID = DataBinder.Eval(e.Row.DataItem, "Year").ToString();

        double dblDirectRevenue = Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "DirectRevenue").ToString());
        double dblReferralRevenue = Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "ReferralRevenue").ToString());

        Label lblTotalRevenue = ((Label)e.Row.FindControl("lblTotalRevenue"));
        lblTotalRevenue.Text = string.Format("{0:0.00}", (dblDirectRevenue + dblReferralRevenue));

        dblSubTotalDirectRevenue += dblDirectRevenue;
        dblSubTotalReferralRevenue += dblReferralRevenue;
        dblSubTotalTotalRevenue += (dblDirectRevenue + dblReferralRevenue);
        e.Row.CssClass = "Row" + strPreviousRowID;
    }
}

protected void grdViewProducts_DataBound(object sender, EventArgs e)
{
    foreach (Total total in TotalList)
    {
        GridViewRow row = (GridViewRow)grdViewProducts.Controls[0].Controls[total.RowIndex];
        row.Cells[1].Text = string.Format("{0:0.00}", total.DirectRevenue);
        row.Cells[2].Text = string.Format("{0:0.00}", total.ReferralRevenue);
        row.Cells[3].Text = string.Format("{0:0.00}", total.TotalRevenue);
    }
}
The Style Sheet
.SubTotalRowStyle{
    border:solid 1px Black;
    background-color:#81BEF7;
    font-weight:bold;
}
.FirstCellSubTotalRowStyle {
    border:solid 1px Black;
    background-color:#81BEF7;
    font-weight:bold;
}
.GrandTotalRowStyle{
    border:solid 1px Black;  
    background-color:Gray;
    font-weight:bold;
}   
.DataCell
{
    border:solid 1px Black;
}
.ExpandCollapseStyle {
    border:0px;
    cursor:pointer;
    padding-left:3px;
    padding-right:5px;
    width:12px;
    height:12px;
}
The JavaScript
$(document).ready(function () {
    $('.ExpandCollapseStyle').click(function () {
        var selectedTrackId = $(this).attr('alt');

        if (selectedTrackId.split(",")[1] == "Expanded") {
            $('.Row' + selectedTrackId).css("display", "none"); // Collapse the rows
            $(this).attr('alt',selectedTrackId.split(",")[0] + ",Collapsed");
            $(this).attr('src', 'images/plus.png');
        }
        else {
            $('.Row' + selectedTrackId).css("display", "block"); // Expand the rows
            $(this).attr('alt', selectedTrackId.split(",")[0] + ",Expanded");
            $(this).attr('src', 'images/minus.png');
        }
    })
});
Here is the output of this example


The Second Example implementation goes as below –
The XML source which bond to the GridView
<?xml version="1.0" encoding="utf-8" ?>
<StockFinancials>
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Income" Sep12="13061.75" Sep11="10644.86" Sep10="8104.31" Sep09="6164.06" Sep08="5731.63" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Income" Sep12="1211.07" Sep11="928.32" Sep10="641.57" Sep09="484.49" Sep08="670.82" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Income" Sep12="11850.68" Sep11="9716.54" Sep10="7462.74" Sep09="5679.57" Sep08="5060.81" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Income" Sep12="32.01" Sep11="16.33" Sep10="20.59" Sep09="11.29" Sep08="-2.22" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Income" Sep12="37.33" Sep11="331.74" Sep10="158.36" Sep09="-214.24" Sep08="89.23" />

  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Expenditure" Sep12="8590.59" Sep11="7615.2" Sep10="5315.14" Sep09="3613.2" Sep08="3645.42" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Expenditure" Sep12="618.51" Sep11="436.91" Sep10="405.08" Sep09="285.54" Sep08="294.88" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Expenditure" Sep12="513.69" Sep11="446.75" Sep10="378.17" Sep09="316.82" Sep08="275.71" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Expenditure" Sep12="50.47" Sep11="133.66" Sep10="110.92" Sep09="76.17" Sep08="87.87" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Expenditure" Sep12="0" Sep11="578.18" Sep10="535.89" Sep09="446.66" Sep08="387.64" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Expenditure" Sep12="853.75" Sep11="23.84" Sep10="39.63" Sep09="21.51" Sep08="20.44" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Expenditure" Sep12="0" Sep11="0" Sep10="0" Sep09="0" Sep08="0" />

  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Income" Sep12="6148.59" Sep11="5247.57" Sep10="3956.29" Sep09="5490.32" Sep08="3195.71" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Income" Sep12="502.51" Sep11="449.39" Sep10="279.16" Sep09="556.21" Sep08="400.64" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Income" Sep12="5646.08" Sep11="4798.18" Sep10="3677.13" Sep09="4934.11" Sep08="2795.07" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Income" Sep12="4.94" Sep11="24.05" Sep10="18.62" Sep09="22.79" Sep08="12.16" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Income" Sep12="-87.32" Sep11="178.66" Sep10="-74.17" Sep09="-73.23" Sep08="127.51" />
  
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Expenditure" Sep12="4212.64" Sep11="3739.46" Sep10="2330.59" Sep09="3476.04" Sep08="2013.1" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Expenditure" Sep12="224.32" Sep11="184.18" Sep10="165.36" Sep09="246.53" Sep08="139.79" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Expenditure" Sep12="294.8" Sep11="271.8" Sep10="253.98" Sep09="294.99" Sep08="176.72" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Expenditure" Sep12="59.53" Sep11="86.33" Sep10="60.35" Sep09="84.7" Sep08="68.65" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Expenditure" Sep12="432.17" Sep11="360.76" Sep10="310.15" Sep09="376.93" Sep08="219.47" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Expenditure" Sep12="55.6" Sep11="0.08" Sep10="0.09" Sep09="0.12" Sep08="45.39" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Expenditure" Sep12="0" Sep11="0" Sep10="0" Sep09="0" Sep08="0" />

  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Income" Sep12="33542.65" Sep11="25974.05" Sep10="25706.93" Sep09="31092.55" Sep08="30788.34" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Income" Sep12="7908.1" Sep11="7108.91" Sep10="7292.43" Sep09="8117.76" Sep08="8878.85" />

  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="22808.5" Sep11="16957.15" Sep10="17592.57" Sep09="22725.93" Sep08="23484.24" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="3515.28" Sep11="2816.93" Sep10="1925.79" Sep09="1971.7" Sep08="2078.9" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="2888.22" Sep11="3785.13" Sep10="6056.48" Sep09="5977.72" Sep08="5834.95" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="524.53" Sep11="562.44" Sep10="619.5" Sep09="678.6" Sep08="578.35" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="5248.97" Sep11="3809.93" Sep10="2780.03" Sep09="4098.22" Sep08="3533.03" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="0" Sep11="0" Sep10="0" Sep09="0" Sep08="0" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="8843.63" Sep11="8594.16" Sep10="10221.99" Sep09="10795.14" Sep08="10855.18" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="3333.37" Sep11="2380.27" Sep10="1159.81" Sep09="1931.1" Sep08="1170.05" />

  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Income" Sep12="27286.35" Sep11="19928.21" Sep10="16172.9" Sep09="16332.26" Sep08="10115" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Income" Sep12="5333.41" Sep11="4433.51" Sep10="3810.62" Sep09="3470.63" Sep08="2205.38" />

  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="14989.58" Sep11="9385.08" Sep10="7786.3" Sep09="8911.1" Sep08="4887.12" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="3399.91" Sep11="2836.04" Sep10="2289.18" Sep09="2238.2" Sep08="1301.35" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="2647.25" Sep11="2510.82" Sep10="3395.83" Sep09="2851.26" Sep08="974.79" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="542.52" Sep11="497.41" Sep10="394.39" Sep09="359.91" Sep08="271.72" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="5873.42" Sep11="5205.97" Sep10="3169.12" Sep09="3197.49" Sep08="3295.22" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="0" Sep11="0" Sep10="0" Sep09="0" Sep08="0" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="9241.64" Sep11="8045.36" Sep10="7703.41" Sep09="7290.66" Sep08="3935.28" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="3221.46" Sep11="3004.88" Sep10="1545.11" Sep09="1356.2" Sep08="1907.8" />

</StockFinancials>
The ASPX script
<asp:GridView ID="grdViewProducts" runat="server" AutoGenerateColumns="False" TabIndex="1"
    Width="100%" DataSourceID="XmlDataSource1" CssClass="grdViewOrders"
    CellPadding="4" ForeColor="Black" GridLines="Vertical"
    OnRowDataBound="grdViewProducts_RowDataBound"
    onrowcreated="grdViewProducts_RowCreated" 
    OnDataBound="grdViewProducts_DataBound"
    BackColor="White" BorderColor="#999999" BorderStyle="Solid" BorderWidth="1px" >
    <Columns>
        <asp:BoundField DataField="" HeaderText="Sector">
            <ItemStyle HorizontalAlign="Left" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="" HeaderText="Name">
            <ItemStyle HorizontalAlign="Left" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="" HeaderText="Income / Expense">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="Sep12" HeaderText="Sep '12" DataFormatString="{0:N}">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="Sep11" HeaderText="Sep '11" DataFormatString="{0:N}">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="Sep10" HeaderText="Sep '10" DataFormatString="{0:N}">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="Sep09" HeaderText="Sep '09" DataFormatString="{0:N}">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="Sep08" HeaderText="Sep '08" DataFormatString="{0:N}">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
    </Columns>
             
    <RowStyle BackColor="#F7F7DE" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
    <FooterStyle BackColor="#CCCC99" />
    <PagerStyle BackColor="#F7F7DE" ForeColor="Black" HorizontalAlign="Right" />
    <SelectedRowStyle BackColor="#CE5D5A" ForeColor="White" Font-Bold="True" />
    <HeaderStyle BackColor="#6B696B" Font-Bold="True" ForeColor="White" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
    <AlternatingRowStyle BackColor="White" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
    <SortedAscendingCellStyle BackColor="#FBFBF2" />
    <SortedAscendingHeaderStyle BackColor="#848384" />
    <SortedDescendingCellStyle BackColor="#EAEAD3" />
    <SortedDescendingHeaderStyle BackColor="#575357" />
</asp:GridView>
<asp:XmlDataSource ID="XmlDataSource1" runat="server" DataFile="Data/StockFinancial.xml"></asp:XmlDataSource>
The C# Code behind
// To keep track of the previous row Group Identifier
string strPreviousSectorRowID = string.Empty;
string strPreviousNameRowID = string.Empty;
string strPreviousAccountSectionRowID = string.Empty;

int intSectorGroupStartRowIndex = 0;
int intNameGroupStartRowIndex = 0;
int intAccountSectionGroupStartRowIndex = 0;

// To keep track the Index of Group Total
int intSubTotalIndex = 1;

// To temporarily store Sub Total
ProfitLossTotal pfSectorGroupTotal;
ProfitLossTotal pfNameGroupTotal;
ProfitLossTotal pfAccountSectionGroupTotal;

IList<ProfitLossTotal> TotalList;

protected void Page_Load(object sender, EventArgs e)
{
    TotalList = new List<ProfitLossTotal>();
    pfSectorGroupTotal = new ProfitLossTotal();
    pfNameGroupTotal = new ProfitLossTotal();
    pfAccountSectionGroupTotal = new ProfitLossTotal();
}

/// <summary>
/// Event fires for every row creation
/// Used for creating SubTotal row when next group starts by adding Group Total at previous row manually
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void grdViewProducts_RowCreated(object sender, GridViewRowEventArgs e)
{
    bool IsSectorSubTotalRowNeedToAdd = false;
    bool IsNameSubTotalRowNeedToAdd = false;
    bool IsAccountSectionSubTotalRowNeedToAdd = false;

    // This is the first row
    if ((strPreviousSectorRowID == string.Empty) && (e.Row.RowType == DataControlRowType.DataRow))
    {
        IsSectorSubTotalRowNeedToAdd = true;
        IsNameSubTotalRowNeedToAdd = true;
        IsAccountSectionSubTotalRowNeedToAdd = true;
        intSubTotalIndex = 1;
    }

    // When a group completed fully, next group started
    if ((strPreviousSectorRowID != string.Empty) &&
        (e.Row.RowType == DataControlRowType.DataRow) &&
        (strPreviousSectorRowID != DataBinder.Eval(e.Row.DataItem, "Sector").ToString())
        )
    {
        IsSectorSubTotalRowNeedToAdd = true;
        IsNameSubTotalRowNeedToAdd = true;
        IsAccountSectionSubTotalRowNeedToAdd = true;
    }

    if ((strPreviousNameRowID != string.Empty) &&
        (e.Row.RowType == DataControlRowType.DataRow) &&
        (strPreviousNameRowID != DataBinder.Eval(e.Row.DataItem, "Name").ToString())
        )
        IsNameSubTotalRowNeedToAdd = true;

    if ((strPreviousAccountSectionRowID != string.Empty) &&
        (e.Row.RowType == DataControlRowType.DataRow) &&
        (strPreviousAccountSectionRowID != DataBinder.Eval(e.Row.DataItem, "AccountSection").ToString())
        )
        IsAccountSectionSubTotalRowNeedToAdd = true;

    if (e.Row.RowType == DataControlRowType.Footer)
    {
        IsSectorSubTotalRowNeedToAdd = false;
        IsNameSubTotalRowNeedToAdd = false;
        IsAccountSectionSubTotalRowNeedToAdd = false;
    }

    // To add the runing total into List
    if ((e.Row.RowType == DataControlRowType.Footer) ||
        ((e.Row.RowType == DataControlRowType.DataRow) && (IsSectorSubTotalRowNeedToAdd == true) && (strPreviousSectorRowID != string.Empty)
        )
        )
    {
        pfSectorGroupTotal.RowIndex = intSectorGroupStartRowIndex;
        TotalList.Add(pfSectorGroupTotal);
    }

    if ((e.Row.RowType == DataControlRowType.Footer) ||
        ((e.Row.RowType == DataControlRowType.DataRow) && (IsNameSubTotalRowNeedToAdd == true) && (strPreviousNameRowID != string.Empty)
        )
        )
    {
        pfNameGroupTotal.RowIndex = intNameGroupStartRowIndex;
        TotalList.Add(pfNameGroupTotal);
    }
    if ((e.Row.RowType == DataControlRowType.Footer) ||
        ((e.Row.RowType == DataControlRowType.DataRow) && (IsAccountSectionSubTotalRowNeedToAdd == true) && (strPreviousAccountSectionRowID != string.Empty)
        )
        )
    {
        pfAccountSectionGroupTotal.RowIndex = intAccountSectionGroupStartRowIndex;
        TotalList.Add(pfAccountSectionGroupTotal);
    }


    if (IsSectorSubTotalRowNeedToAdd)
    {
        #region Sector Sub Total
        GridView grdViewProducts = (GridView)sender;

        GridViewRow SubTotalRow = new GridViewRow(0, 0, DataControlRowType.DataRow, DataControlRowState.Insert);

        TableCell cell = new TableCell();

        System.Web.UI.HtmlControls.HtmlImage img = new System.Web.UI.HtmlControls.HtmlImage();
        img.Src = "images/minus.png";
        img.Attributes.Add("alt", DataBinder.Eval(e.Row.DataItem, "Sector").ToString() + ",1,Expanded");
        img.Attributes.Add("class", "ExpandCollapseStyle");
        cell.Controls.Add(img);

        System.Web.UI.HtmlControls.HtmlGenericControl title = new System.Web.UI.HtmlControls.HtmlGenericControl();
        title.InnerText = DataBinder.Eval(e.Row.DataItem, "Sector").ToString();
        cell.Controls.Add(title);
        cell.HorizontalAlign = HorizontalAlign.Left;
        cell.ColumnSpan = 3;
        cell.CssClass = "SectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfSectorGroupTotal.Sep12);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfSectorGroupTotal.Sep11);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfSectorGroupTotal.Sep10);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfSectorGroupTotal.Sep09);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfSectorGroupTotal.Sep08);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        //Adding the Row at the RowIndex position in the Grid
        grdViewProducts.Controls[0].Controls.AddAt(e.Row.RowIndex + intSubTotalIndex, SubTotalRow);
        intSectorGroupStartRowIndex = e.Row.RowIndex + intSubTotalIndex;
        intSubTotalIndex++;

        pfSectorGroupTotal = new ProfitLossTotal();
        pfNameGroupTotal = new ProfitLossTotal();
        pfAccountSectionGroupTotal = new ProfitLossTotal();
        #endregion
    }

    if (IsNameSubTotalRowNeedToAdd)
    {
        #region Name Sub Total
        GridView grdViewProducts = (GridView)sender;

        GridViewRow SubTotalRow = new GridViewRow(0, 0, DataControlRowType.DataRow, DataControlRowState.Insert);

        TableCell cell = new TableCell();

        cell = new TableCell();
        cell.Text = string.Empty;
        cell.CssClass = "DataCell";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();

        System.Web.UI.HtmlControls.HtmlImage img = new System.Web.UI.HtmlControls.HtmlImage();
        img.Src = "images/minus.png";
        img.Attributes.Add("alt", DataBinder.Eval(e.Row.DataItem, "Sector").ToString() + "_" + DataBinder.Eval(e.Row.DataItem, "Name").ToString() + ",2,Expanded");
        img.Attributes.Add("class", "ExpandCollapseStyle");
        cell.Controls.Add(img);

        System.Web.UI.HtmlControls.HtmlGenericControl title = new System.Web.UI.HtmlControls.HtmlGenericControl();
        title.InnerText = DataBinder.Eval(e.Row.DataItem, "Name").ToString();
        cell.Controls.Add(title);
        cell.HorizontalAlign = HorizontalAlign.Left;
        cell.ColumnSpan = 2;
        cell.CssClass = "NameTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfNameGroupTotal.Sep12);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "NameTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfNameGroupTotal.Sep11);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "NameTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfNameGroupTotal.Sep10);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "NameTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfNameGroupTotal.Sep09);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "NameTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfNameGroupTotal.Sep08);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "NameTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        //Adding the Row at the RowIndex position in the Grid
        grdViewProducts.Controls[0].Controls.AddAt(e.Row.RowIndex + intSubTotalIndex, SubTotalRow);
        intNameGroupStartRowIndex = e.Row.RowIndex + intSubTotalIndex;
        intSubTotalIndex++;

        pfNameGroupTotal = new ProfitLossTotal();
        pfAccountSectionGroupTotal = new ProfitLossTotal();
        #endregion
    }

    if (IsAccountSectionSubTotalRowNeedToAdd)
    {
        #region Account Section Sub Total
        GridView grdViewProducts = (GridView)sender;

        GridViewRow SubTotalRow = new GridViewRow(0, 0, DataControlRowType.DataRow, DataControlRowState.Insert);

        TableCell cell = new TableCell();

        cell = new TableCell();
        cell.Text = string.Empty;
        cell.CssClass = "DataCell";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Empty;
        cell.CssClass = "DataCell";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();

        System.Web.UI.HtmlControls.HtmlImage img = new System.Web.UI.HtmlControls.HtmlImage();
        img.Src = "images/minus.png";
        img.Attributes.Add("alt", DataBinder.Eval(e.Row.DataItem, "Sector").ToString() + "_" + DataBinder.Eval(e.Row.DataItem, "Name").ToString() + "_" + DataBinder.Eval(e.Row.DataItem, "AccountSection").ToString() + ",3,Expanded");
        img.Attributes.Add("class", "ExpandCollapseStyle");
        cell.Controls.Add(img);

        System.Web.UI.HtmlControls.HtmlGenericControl title = new System.Web.UI.HtmlControls.HtmlGenericControl();
        title.InnerText = DataBinder.Eval(e.Row.DataItem, "AccountSection").ToString();
        cell.Controls.Add(title);
        cell.HorizontalAlign = HorizontalAlign.Left;
        cell.CssClass = "AccountSectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfAccountSectionGroupTotal.Sep12);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "AccountSectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfAccountSectionGroupTotal.Sep11);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "AccountSectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfAccountSectionGroupTotal.Sep10);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "AccountSectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfAccountSectionGroupTotal.Sep09);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "AccountSectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfAccountSectionGroupTotal.Sep08);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "AccountSectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        //Adding the Row at the RowIndex position in the Grid
        grdViewProducts.Controls[0].Controls.AddAt(e.Row.RowIndex + intSubTotalIndex, SubTotalRow);
        intAccountSectionGroupStartRowIndex = e.Row.RowIndex + intSubTotalIndex;
        intSubTotalIndex++;

        pfAccountSectionGroupTotal = new ProfitLossTotal();
        #endregion
    }
}

/// <summary>
/// Event fires when data binds to each row
/// Used for calculating Group Total 
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void grdViewProducts_RowDataBound(object sender, GridViewRowEventArgs e)
{
    // This is for calculation of column (Total = Direct + Referral)
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        strPreviousSectorRowID = DataBinder.Eval(e.Row.DataItem, "Sector").ToString();
        strPreviousNameRowID = DataBinder.Eval(e.Row.DataItem, "Name").ToString();
        strPreviousAccountSectionRowID = DataBinder.Eval(e.Row.DataItem, "AccountSection").ToString();

        pfSectorGroupTotal.Sep12 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep12").ToString());
        pfSectorGroupTotal.Sep11 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep11").ToString());
        pfSectorGroupTotal.Sep10 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep10").ToString());
        pfSectorGroupTotal.Sep09 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep09").ToString());
        pfSectorGroupTotal.Sep08 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep08").ToString());

        pfNameGroupTotal.Sep12 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep12").ToString());
        pfNameGroupTotal.Sep11 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep11").ToString());
        pfNameGroupTotal.Sep10 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep10").ToString());
        pfNameGroupTotal.Sep09 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep09").ToString());
        pfNameGroupTotal.Sep08 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep08").ToString());

        pfAccountSectionGroupTotal.Sep12 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep12").ToString());
        pfAccountSectionGroupTotal.Sep11 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep11").ToString());
        pfAccountSectionGroupTotal.Sep10 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep10").ToString());
        pfAccountSectionGroupTotal.Sep09 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep09").ToString());
        pfAccountSectionGroupTotal.Sep08 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep08").ToString());

        e.Row.Cells[0].CssClass = "DataRowStyle";
        e.Row.Cells[0].Attributes.Add("alt", ",4");

    }
}

protected void grdViewProducts_DataBound(object sender, EventArgs e)
{
    foreach (ProfitLossTotal total in TotalList)
    {
        GridViewRow row = (GridViewRow)grdViewProducts.Controls[0].Controls[total.RowIndex];

        row.Cells[row.Cells.Count - 5].Text = string.Format("{0:0.00}", total.Sep12);
        row.Cells[row.Cells.Count - 4].Text = string.Format("{0:0.00}", total.Sep11);
        row.Cells[row.Cells.Count - 3].Text = string.Format("{0:0.00}", total.Sep10);
        row.Cells[row.Cells.Count - 2].Text = string.Format("{0:0.00}", total.Sep09);
        row.Cells[row.Cells.Count - 1].Text = string.Format("{0:0.00}", total.Sep08);
    }
}
The Style Sheet
.AccountSectionTotalRowStyle{
    border:solid 1px Black;
    background-color:#a8249d;
    font-weight:bold;
}
.NameTotalRowStyle {
    border:solid 1px Black;
    background-color:#e46144;
    font-weight:bold;
}
.SectionTotalRowStyle {
    border:solid 1px Black;
    background-color:#1c7647;
    font-weight:bold;
}
.GrandTotalRowStyle{
    border:solid 1px White;
    background-color:Gray;
    font-weight:bold;
}
.DataCell, .DataRowStyle
{
    border:solid 1px Black;
}
.ExpandCollapseStyle {
    border:0px;
    cursor:pointer;
    padding-left:3px;
    padding-right:5px;
    width:12px;
    height:12px;
}
The Javascript (When expanding a parent group, expand all the child groups)
$(document).ready(function () {
    $('.ExpandCollapseStyle').click(function () {
        var selectedTrackId = $(this).attr('alt');

        var isSelectedTrackerFound = false;
        var selectedTrackerGroupIndex = 0;

        var ExpandOrCollapse = $(this).attr('src');

        $($(".grdViewOrders tr").get()).each(function () {

            var currentTrackId = $(this).find(".ExpandCollapseStyle").attr('alt');

            if (currentTrackId == null)
                currentTrackId = $(this).find(".DataRowStyle").attr('alt');

            if (currentTrackId != null) {

                if (selectedTrackId.split(",")[0] == currentTrackId.split(",")[0]) {

                    isSelectedTrackerFound = true;
                    if (selectedTrackerGroupIndex == 0) {
                        selectedTrackerGroupIndex = currentTrackId.split(",")[1];

                        if (ExpandOrCollapse == 'images/plus.png') {
                            $(this).find(".ExpandCollapseStyle").attr('src', 'images/minus.png');
                        }
                        else {
                            $(this).find(".ExpandCollapseStyle").attr('src', 'images/plus.png');
                        }
                    }
                }
                else {
                    if (currentTrackId != null) {
                        if (parseInt(selectedTrackerGroupIndex) > 0) {
                            if (parseInt(selectedTrackerGroupIndex) >= parseInt(currentTrackId.split(",")[1]))
                                isSelectedTrackerFound = false;
                        }
                    }
                    if (isSelectedTrackerFound == true) {

                        if (ExpandOrCollapse == 'images/plus.png') {
                            $(this).css("display", "block");
                            $(this).find(".ExpandCollapseStyle").attr('src', 'images/minus.png');
                        }
                        else {
                            $(this).css("display", "none");
                            $(this).find(".ExpandCollapseStyle").attr('src', 'images/plus.png');
                        }
                    }
                }
            }
        });
    })
});
The Javascript (When expanding a parent group, expand only the next level child group)
$(document).ready(function () {
    $('.ExpandCollapseStyle').click(function () {
        var selectedTrackId = $(this).attr('alt');

        var isSelectedTrackerFound = false;
        var selectedTrackerGroupIndex = 0;

        var ExpandOrCollapse = $(this).attr('src');

        $($(".grdViewOrders tr").get()).each(function () {

            var currentTrackId = $(this).find(".ExpandCollapseStyle").attr('alt');

            if (currentTrackId == null)
                currentTrackId = $(this).find(".DataRowStyle").attr('alt');

            if (currentTrackId != null) {

                if (selectedTrackId.split(",")[0] == currentTrackId.split(",")[0]) {

                    isSelectedTrackerFound = true;
                    if (selectedTrackerGroupIndex == 0) {
                        selectedTrackerGroupIndex = currentTrackId.split(",")[1];

                        if (ExpandOrCollapse == 'images/plus.png') {
                            $(this).find(".ExpandCollapseStyle").attr('src', 'images/minus.png');
                        }
                        else {
                            $(this).find(".ExpandCollapseStyle").attr('src', 'images/plus.png');
                        }
                    }
                }
                else {
                    if (currentTrackId != null) {
                        if (parseInt(selectedTrackerGroupIndex) > 0) {
                            if (parseInt(selectedTrackerGroupIndex) >= parseInt(currentTrackId.split(",")[1]))
                                isSelectedTrackerFound = false;
                        }
                    }
                    if (isSelectedTrackerFound == true) {

                        if (ExpandOrCollapse == 'images/plus.png') {

                            $(this).find(".ExpandCollapseStyle").attr('src', 'images/plus.png');

                            if (parseInt(selectedTrackerGroupIndex) + 1 == parseInt(currentTrackId.split(",")[1]))
                                $(this).css("display", "block");
                            else
                                $(this).css("display", "none");
                        }
                        else {
                            $(this).css("display", "none");
                            $(this).find(".ExpandCollapseStyle").attr('src', 'images/plus.png');
                        }
                    }
                }
            }
        });
    })
});

Here is the output of this example




download the working example of the source code in C# here and in VB here.

Tuesday, 22 January 2013

Group Total and Grand Total in GridView – Part 9

We had seen many ways to show Group Total and Grand Total in GridView. In those posts, we had considered the Group Total will show at the end of the group and the Grand Total will show at the last row. But in many other requirements, we may need to show the Group Total when the group is starting (something like what it shown in Excel Pivot Table). So in this post, I am planning to post how to show the Group Total while starting the Group.

I have given two types of examples for providing more clarity on this implementation. For more understanding, here is the use case of the two examples –

First Example (One level Grouping)
  1. The records defined in the XML should bound to the Grid View in a normal way.
  2. The records should be grouped by Year and the Group Total should be shown at the beginning of each group.
  3. The Group Total must be displayed with different background color to differentiate the groups.
Second Example (Three level Grouping)

This example shows profit and loss sheet of market scripts. The data has taken from a public site.
  1. The records defined in the XML should bound to the Grid View in a normal way.
  2. The records should be grouped by Sector in first level, Name of the Company in second level and the Income/Expense details in third level. The Group Total should be shown at the beginning of each group.
  3. The Sector, Company Name, Income/Expense groups must be displayed with different background color to differentiate the groups.
This requirement talks about having three different groups, Sector is the first group and Company Name is the second group and Income/Expense is the third group. So the grid will have one or more Sector and each sector will have one or more Company Name. Each company name will have one or more Income/Export group.

Before going for actual implementation, please note the following points -
  1. To implement these examples, all the records must show in a single page of the Grid View (So, no pagination). Because for calculating the Group Total, the code required all the records must be in loop.
  2. The records must be sorted on the group wise. So all the records related to a particular group will show one after another. It will be useful for calculating cumulative values together. Keeping records in different group will be considered as separate group and cumulative values will be calculated as another separate group. As we have three groups in this example, we must sort by Sector at first and then Company Name and then Income/Export.
The First Example implementation goes as below –
The XML source which bond to the GridView
<?xml version="1.0" encoding="utf-8" ?>
<RevenueReport>
     
    <Data Year="2008" Period="Q1" AuditedBy="Maria Anders" DirectRevenue="12500.00" ReferralRevenue="2500.00" />
    <Data Year="2008" Period="Q2" AuditedBy="Ana Trujillo" DirectRevenue="21000.00" ReferralRevenue="8000.00" />
    <Data Year="2008" Period="Q3" AuditedBy="Antonio Moreno" DirectRevenue="20000.00" ReferralRevenue="5000.00" />
    <Data Year="2008" Period="Q4" AuditedBy="Thomas Hardy" DirectRevenue="25000.00" ReferralRevenue="1200.00" />
   
    <Data Year="2009" Period="Q1" AuditedBy="Christina Berglund" DirectRevenue="72500.00" ReferralRevenue="5000.00" />
    <Data Year="2009" Period="Q2" AuditedBy="Hanna Moos" DirectRevenue="15000.00" ReferralRevenue="6500.00" />
    <Data Year="2009" Period="Q3" AuditedBy="Thomas Hardy" DirectRevenue="25000.00" ReferralRevenue="1520.00" />
    <Data Year="2009" Period="Q4" AuditedBy="Martín Sommer" DirectRevenue="42000.00" ReferralRevenue="2580.00" />
   
    <Data Year="2010" Period="Q1" AuditedBy="Laurence Lebihan" DirectRevenue="12500.00" ReferralRevenue="1500.00" />
    <Data Year="2010" Period="Q2" AuditedBy="Elizabeth Lincoln" DirectRevenue="25000.00" ReferralRevenue="5500.00" />
    <Data Year="2010" Period="Q3" AuditedBy="Hanna Moos" DirectRevenue="12000.00" ReferralRevenue="1800.00" />
    <Data Year="2010" Period="Q4" AuditedBy="Antonio Moreno" DirectRevenue="10000.00" ReferralRevenue="1200.00" />

</RevenueReport>
The ASPX script
<asp:GridView ID="grdViewProducts" runat="server" AutoGenerateColumns="False" TabIndex="1"
    Width="100%" DataSourceID="XmlDataSource1" CssClass="grdViewOrders"
    CellPadding="4" ForeColor="Black" GridLines="Vertical"
    OnRowDataBound="grdViewProducts_RowDataBound"
    onrowcreated="grdViewProducts_RowCreated" 
    OnDataBound="grdViewProducts_DataBound"
    BackColor="White" BorderColor="#999999" BorderStyle="Solid" BorderWidth="1px" >
    <Columns>
        <asp:BoundField DataField="" HeaderText="Year">
            <ItemStyle HorizontalAlign="Left" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="Period" HeaderText="Period">
            <ItemStyle HorizontalAlign="Left" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="AuditedBy" HeaderText="Audited By">
            <ItemStyle HorizontalAlign="Left" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="DirectRevenue" HeaderText="Direct">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="ReferralRevenue" HeaderText="Referral">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:TemplateField HeaderText="Total">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell" />
            <HeaderStyle CssClass="DataCell" />
            <ItemTemplate>
                <asp:Label runat="server" ID="lblTotalRevenue" Text="0" />
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
    <RowStyle BackColor="#F7F7DE" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
    <FooterStyle BackColor="#CCCC99" />
    <PagerStyle BackColor="#F7F7DE" ForeColor="Black" HorizontalAlign="Right" />
    <SelectedRowStyle BackColor="#CE5D5A" ForeColor="White" Font-Bold="True" />
    <HeaderStyle BackColor="#6B696B" Font-Bold="True" ForeColor="White" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
    <AlternatingRowStyle BackColor="White" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
    <SortedAscendingCellStyle BackColor="#FBFBF2" />
    <SortedAscendingHeaderStyle BackColor="#848384" />
    <SortedDescendingCellStyle BackColor="#EAEAD3" />
    <SortedDescendingHeaderStyle BackColor="#575357" />
</asp:GridView>
<asp:XmlDataSource ID="XmlDataSource1" runat="server" DataFile="Data/RevenueReport.xml"></asp:XmlDataSource>
The C# Code behind
// To keep track of the previous row Group Identifier
string strPreviousRowID = string.Empty;
int intGroupStartRowIndex = 0;

// To keep track the Index of Group Total
int intSubTotalIndex = 1;

// To temporarily store Sub Total
double dblSubTotalDirectRevenue = 0;
double dblSubTotalReferralRevenue = 0;
double dblSubTotalTotalRevenue = 0;

IList<Total> TotalList;

protected void Page_Load(object sender, EventArgs e)
{
    TotalList = new List<Total>();
}

/// <summary>
/// Event fires for every row creation
/// Used for creating SubTotal row when next group starts by adding Group Total at previous row manually
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void grdViewProducts_RowCreated(object sender, GridViewRowEventArgs e)
{
    bool IsSubTotalRowNeedToAdd = false;

    if ((strPreviousRowID == string.Empty) && (e.Row.RowType == DataControlRowType.DataRow))
    {
        IsSubTotalRowNeedToAdd = true;
        intSubTotalIndex = 1;
    }

    if ((strPreviousRowID != string.Empty) &&
        (e.Row.RowType == DataControlRowType.DataRow) &&
        (strPreviousRowID != DataBinder.Eval(e.Row.DataItem, "Year").ToString())
        )
        IsSubTotalRowNeedToAdd = true;

    if (e.Row.RowType == DataControlRowType.Footer)
        IsSubTotalRowNeedToAdd = false;

    // To add the runing total into List
    if ((e.Row.RowType == DataControlRowType.Footer) ||
        ((e.Row.RowType == DataControlRowType.DataRow) && (IsSubTotalRowNeedToAdd == true) && (strPreviousRowID != string.Empty))
        )
    {
        Total total = new Total();
        total.RowIndex = intGroupStartRowIndex;
        total.DirectRevenue = dblSubTotalDirectRevenue;
        total.ReferralRevenue = dblSubTotalReferralRevenue;
        total.TotalRevenue = dblSubTotalTotalRevenue;
        TotalList.Add(total);
    }

    if (IsSubTotalRowNeedToAdd)
    {
        GridView grdViewProducts = (GridView)sender;

        GridViewRow SubTotalRow = new GridViewRow(0, 0, DataControlRowType.DataRow, DataControlRowState.Insert);

        TableCell cell = new TableCell();

        cell.Text = "Sub Total";
        cell.HorizontalAlign = HorizontalAlign.Left;
        cell.ColumnSpan = 3;
        cell.CssClass = "SubTotalRowStyle";
        cell.Text = DataBinder.Eval(e.Row.DataItem, "Year").ToString();
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", dblSubTotalDirectRevenue);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SubTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", dblSubTotalReferralRevenue);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SubTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", dblSubTotalTotalRevenue);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SubTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        //Adding the Row at the RowIndex position in the Grid
        grdViewProducts.Controls[0].Controls.AddAt(e.Row.RowIndex + intSubTotalIndex, SubTotalRow);
        intGroupStartRowIndex = e.Row.RowIndex + intSubTotalIndex;
        intSubTotalIndex++;

        dblSubTotalDirectRevenue = 0;
        dblSubTotalReferralRevenue = 0;
        dblSubTotalTotalRevenue = 0;
    }
}

/// <summary>
/// Event fires when data binds to each row
/// Used for calculating Group Total 
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void grdViewProducts_RowDataBound(object sender, GridViewRowEventArgs e)
{
    // This is for calculation of column (Total = Direct + Referral)
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        strPreviousRowID = DataBinder.Eval(e.Row.DataItem, "Year").ToString();

        double dblDirectRevenue = Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "DirectRevenue").ToString());
        double dblReferralRevenue = Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "ReferralRevenue").ToString());

        Label lblTotalRevenue = ((Label)e.Row.FindControl("lblTotalRevenue"));
        lblTotalRevenue.Text = string.Format("{0:0.00}", (dblDirectRevenue + dblReferralRevenue));

        dblSubTotalDirectRevenue += dblDirectRevenue;
        dblSubTotalReferralRevenue += dblReferralRevenue;
        dblSubTotalTotalRevenue += (dblDirectRevenue + dblReferralRevenue);
    }
}

protected void grdViewProducts_DataBound(object sender, EventArgs e)
{
    foreach (Total total in TotalList)
    {
        GridViewRow row = (GridViewRow)grdViewProducts.Controls[0].Controls[total.RowIndex];
        row.Cells[1].Text = string.Format("{0:0.00}", total.DirectRevenue);
        row.Cells[2].Text = string.Format("{0:0.00}", total.ReferralRevenue);
        row.Cells[3].Text = string.Format("{0:0.00}", total.TotalRevenue);
    }
}
The Style Sheet
.SubTotalRowStyle{
    border:solid 1px Black;
    background-color:#81BEF7;
    font-weight:bold;
}
.GrandTotalRowStyle{
    border:solid 1px Black;  
    background-color:Gray;
    font-weight:bold;
}   
.DataCell
{
    border:solid 1px Black;
}
Here is the output of this example




The Second Example implementation goes as below –
The XML source which bond to the GridView
<?xml version="1.0" encoding="utf-8" ?>
<StockFinancials>
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Income" Sep12="13061.75" Sep11="10644.86" Sep10="8104.31" Sep09="6164.06" Sep08="5731.63" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Income" Sep12="1211.07" Sep11="928.32" Sep10="641.57" Sep09="484.49" Sep08="670.82" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Income" Sep12="11850.68" Sep11="9716.54" Sep10="7462.74" Sep09="5679.57" Sep08="5060.81" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Income" Sep12="32.01" Sep11="16.33" Sep10="20.59" Sep09="11.29" Sep08="-2.22" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Income" Sep12="37.33" Sep11="331.74" Sep10="158.36" Sep09="-214.24" Sep08="89.23" />

  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Expenditure" Sep12="8590.59" Sep11="7615.2" Sep10="5315.14" Sep09="3613.2" Sep08="3645.42" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Expenditure" Sep12="618.51" Sep11="436.91" Sep10="405.08" Sep09="285.54" Sep08="294.88" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Expenditure" Sep12="513.69" Sep11="446.75" Sep10="378.17" Sep09="316.82" Sep08="275.71" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Expenditure" Sep12="50.47" Sep11="133.66" Sep10="110.92" Sep09="76.17" Sep08="87.87" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Expenditure" Sep12="0" Sep11="578.18" Sep10="535.89" Sep09="446.66" Sep08="387.64" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Expenditure" Sep12="853.75" Sep11="23.84" Sep10="39.63" Sep09="21.51" Sep08="20.44" />
  <Financial Sector="Tyres" Name="MRF" BSECode="500290" NSECode="MRF" ISIN="INE883A01011" AccountSection="Expenditure" Sep12="0" Sep11="0" Sep10="0" Sep09="0" Sep08="0" />

  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Income" Sep12="6148.59" Sep11="5247.57" Sep10="3956.29" Sep09="5490.32" Sep08="3195.71" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Income" Sep12="502.51" Sep11="449.39" Sep10="279.16" Sep09="556.21" Sep08="400.64" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Income" Sep12="5646.08" Sep11="4798.18" Sep10="3677.13" Sep09="4934.11" Sep08="2795.07" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Income" Sep12="4.94" Sep11="24.05" Sep10="18.62" Sep09="22.79" Sep08="12.16" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Income" Sep12="-87.32" Sep11="178.66" Sep10="-74.17" Sep09="-73.23" Sep08="127.51" />
  
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Expenditure" Sep12="4212.64" Sep11="3739.46" Sep10="2330.59" Sep09="3476.04" Sep08="2013.1" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Expenditure" Sep12="224.32" Sep11="184.18" Sep10="165.36" Sep09="246.53" Sep08="139.79" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Expenditure" Sep12="294.8" Sep11="271.8" Sep10="253.98" Sep09="294.99" Sep08="176.72" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Expenditure" Sep12="59.53" Sep11="86.33" Sep10="60.35" Sep09="84.7" Sep08="68.65" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Expenditure" Sep12="432.17" Sep11="360.76" Sep10="310.15" Sep09="376.93" Sep08="219.47" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Expenditure" Sep12="55.6" Sep11="0.08" Sep10="0.09" Sep09="0.12" Sep08="45.39" />
  <Financial Sector="Tyres" Name="JK Tyre and Industries" BSECode="530007" NSECode="JKTYRE" ISIN="INE573A01034" AccountSection="Expenditure" Sep12="0" Sep11="0" Sep10="0" Sep09="0" Sep08="0" />

  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Income" Sep12="33542.65" Sep11="25974.05" Sep10="25706.93" Sep09="31092.55" Sep08="30788.34" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Income" Sep12="7908.1" Sep11="7108.91" Sep10="7292.43" Sep09="8117.76" Sep08="8878.85" />

  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="22808.5" Sep11="16957.15" Sep10="17592.57" Sep09="22725.93" Sep08="23484.24" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="3515.28" Sep11="2816.93" Sep10="1925.79" Sep09="1971.7" Sep08="2078.9" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="2888.22" Sep11="3785.13" Sep10="6056.48" Sep09="5977.72" Sep08="5834.95" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="524.53" Sep11="562.44" Sep10="619.5" Sep09="678.6" Sep08="578.35" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="5248.97" Sep11="3809.93" Sep10="2780.03" Sep09="4098.22" Sep08="3533.03" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="0" Sep11="0" Sep10="0" Sep09="0" Sep08="0" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="8843.63" Sep11="8594.16" Sep10="10221.99" Sep09="10795.14" Sep08="10855.18" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="ICICI Banks" BSECode="532174" NSECode="ICICIBANK " ISIN="INE090A01013 " AccountSection="Expenditure" Sep12="3333.37" Sep11="2380.27" Sep10="1159.81" Sep09="1931.1" Sep08="1170.05" />

  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Income" Sep12="27286.35" Sep11="19928.21" Sep10="16172.9" Sep09="16332.26" Sep08="10115" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Income" Sep12="5333.41" Sep11="4433.51" Sep10="3810.62" Sep09="3470.63" Sep08="2205.38" />

  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="14989.58" Sep11="9385.08" Sep10="7786.3" Sep09="8911.1" Sep08="4887.12" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="3399.91" Sep11="2836.04" Sep10="2289.18" Sep09="2238.2" Sep08="1301.35" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="2647.25" Sep11="2510.82" Sep10="3395.83" Sep09="2851.26" Sep08="974.79" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="542.52" Sep11="497.41" Sep10="394.39" Sep09="359.91" Sep08="271.72" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="5873.42" Sep11="5205.97" Sep10="3169.12" Sep09="3197.49" Sep08="3295.22" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="0" Sep11="0" Sep10="0" Sep09="0" Sep08="0" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="9241.64" Sep11="8045.36" Sep10="7703.41" Sep09="7290.66" Sep08="3935.28" />
  <Financial Sector="BANKS - PRIVATE SECTOR" Name="HDFC Bank" BSECode="500180" NSECode="HDFCBANK " ISIN="INE040A01026" AccountSection="Expenditure" Sep12="3221.46" Sep11="3004.88" Sep10="1545.11" Sep09="1356.2" Sep08="1907.8" />

</StockFinancials>
The ASPX script
<asp:GridView ID="grdViewProducts" runat="server" AutoGenerateColumns="False" TabIndex="1"
    Width="100%" DataSourceID="XmlDataSource1" CssClass="grdViewOrders"
    CellPadding="4" ForeColor="Black" GridLines="Vertical"
    OnRowDataBound="grdViewProducts_RowDataBound"
    onrowcreated="grdViewProducts_RowCreated" 
    OnDataBound="grdViewProducts_DataBound"
    BackColor="White" BorderColor="#999999" BorderStyle="Solid" BorderWidth="1px" >
    <Columns>
        <asp:BoundField DataField="" HeaderText="Sector">
            <ItemStyle HorizontalAlign="Left" CssClass="DataCell" BorderStyle="Solid"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="" HeaderText="Name">
            <ItemStyle HorizontalAlign="Left" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="" HeaderText="Income / Expense">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="Sep12" HeaderText="Sep '12" DataFormatString="{0:N}">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="Sep11" HeaderText="Sep '11" DataFormatString="{0:N}">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="Sep10" HeaderText="Sep '10" DataFormatString="{0:N}">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="Sep09" HeaderText="Sep '09" DataFormatString="{0:N}">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="Sep08" HeaderText="Sep '08" DataFormatString="{0:N}">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
    </Columns>
             
    <RowStyle BackColor="#F7F7DE" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
    <FooterStyle BackColor="#CCCC99" />
    <PagerStyle BackColor="#F7F7DE" ForeColor="Black" HorizontalAlign="Right" />
    <SelectedRowStyle BackColor="#CE5D5A" ForeColor="White" Font-Bold="True" />
    <HeaderStyle BackColor="#6B696B" Font-Bold="True" ForeColor="White" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
    <AlternatingRowStyle BackColor="White" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
    <SortedAscendingCellStyle BackColor="#FBFBF2" />
    <SortedAscendingHeaderStyle BackColor="#848384" />
    <SortedDescendingCellStyle BackColor="#EAEAD3" />
    <SortedDescendingHeaderStyle BackColor="#575357" />
</asp:GridView>
<asp:XmlDataSource ID="XmlDataSource1" runat="server" DataFile="Data/StockFinancial.xml"></asp:XmlDataSource>
The C# Code behind
// To keep track of the previous row Group Identifier
string strPreviousSectorRowID = string.Empty;
string strPreviousNameRowID = string.Empty;
string strPreviousAccountSectionRowID = string.Empty;

int intSectorGroupStartRowIndex = 0;
int intNameGroupStartRowIndex = 0;
int intAccountSectionGroupStartRowIndex = 0;

// To keep track the Index of Group Total
int intSubTotalIndex = 1;

// To temporarily store Sub Total
ProfitLossTotal pfSectorGroupTotal;
ProfitLossTotal pfNameGroupTotal;
ProfitLossTotal pfAccountSectionGroupTotal;

IList<ProfitLossTotal> TotalList;

protected void Page_Load(object sender, EventArgs e)
{
    TotalList = new List<ProfitLossTotal>();
    pfSectorGroupTotal = new ProfitLossTotal();
    pfNameGroupTotal = new ProfitLossTotal();
    pfAccountSectionGroupTotal = new ProfitLossTotal();
}

/// <summary>
/// Event fires for every row creation
/// Used for creating SubTotal row when next group starts by adding Group Total at previous row manually
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void grdViewProducts_RowCreated(object sender, GridViewRowEventArgs e)
{
    bool IsSectorSubTotalRowNeedToAdd = false;
    bool IsNameSubTotalRowNeedToAdd = false;
    bool IsAccountSectionSubTotalRowNeedToAdd = false;

    // This is the first row
    if ((strPreviousSectorRowID == string.Empty) && (e.Row.RowType == DataControlRowType.DataRow))
    {
        IsSectorSubTotalRowNeedToAdd = true;
        IsNameSubTotalRowNeedToAdd = true;
        IsAccountSectionSubTotalRowNeedToAdd = true;
        intSubTotalIndex = 1;
    }

    // When a group completed fully, next group started
    if ((strPreviousSectorRowID != string.Empty) &&
        (e.Row.RowType == DataControlRowType.DataRow) &&
        (strPreviousSectorRowID != DataBinder.Eval(e.Row.DataItem, "Sector").ToString())
        )
    {
        IsSectorSubTotalRowNeedToAdd = true;
        IsNameSubTotalRowNeedToAdd = true;
        IsAccountSectionSubTotalRowNeedToAdd = true;
    }

    if ((strPreviousNameRowID != string.Empty) &&
        (e.Row.RowType == DataControlRowType.DataRow) &&
        (strPreviousNameRowID != DataBinder.Eval(e.Row.DataItem, "Name").ToString())
        )
        IsNameSubTotalRowNeedToAdd = true;

    if ((strPreviousAccountSectionRowID != string.Empty) &&
        (e.Row.RowType == DataControlRowType.DataRow) &&
        (strPreviousAccountSectionRowID != DataBinder.Eval(e.Row.DataItem, "AccountSection").ToString())
        )
        IsAccountSectionSubTotalRowNeedToAdd = true;

    if (e.Row.RowType == DataControlRowType.Footer)
    {
        IsSectorSubTotalRowNeedToAdd = false;
        IsNameSubTotalRowNeedToAdd = false;
        IsAccountSectionSubTotalRowNeedToAdd = false;
    }

    // To add the runing total into List
    if ((e.Row.RowType == DataControlRowType.Footer) ||
        ((e.Row.RowType == DataControlRowType.DataRow) && (IsSectorSubTotalRowNeedToAdd == true) && (strPreviousSectorRowID != string.Empty)
        )
        )
    {
        pfSectorGroupTotal.RowIndex = intSectorGroupStartRowIndex;
        TotalList.Add(pfSectorGroupTotal);
    }

    if ((e.Row.RowType == DataControlRowType.Footer) ||
        ((e.Row.RowType == DataControlRowType.DataRow) && (IsNameSubTotalRowNeedToAdd == true) && (strPreviousNameRowID != string.Empty)
        )
        )
    {
        pfNameGroupTotal.RowIndex = intNameGroupStartRowIndex;
        TotalList.Add(pfNameGroupTotal);
    }
    if ((e.Row.RowType == DataControlRowType.Footer) ||
        ((e.Row.RowType == DataControlRowType.DataRow) && (IsAccountSectionSubTotalRowNeedToAdd == true) && (strPreviousAccountSectionRowID != string.Empty)
        )
        )
    {
        pfAccountSectionGroupTotal.RowIndex = intAccountSectionGroupStartRowIndex;
        TotalList.Add(pfAccountSectionGroupTotal);
    }


    if (IsSectorSubTotalRowNeedToAdd)
    {
        #region Sector Sub Total
        GridView grdViewProducts = (GridView)sender;

        GridViewRow SubTotalRow = new GridViewRow(0, 0, DataControlRowType.DataRow, DataControlRowState.Insert);

        TableCell cell = new TableCell();
        cell.HorizontalAlign = HorizontalAlign.Left;
        cell.ColumnSpan = 3;
        cell.CssClass = "SectionTotalRowStyle";
        cell.Text = DataBinder.Eval(e.Row.DataItem, "Sector").ToString();
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfSectorGroupTotal.Sep12);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfSectorGroupTotal.Sep11);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfSectorGroupTotal.Sep10);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfSectorGroupTotal.Sep09);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfSectorGroupTotal.Sep08);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        //Adding the Row at the RowIndex position in the Grid
        grdViewProducts.Controls[0].Controls.AddAt(e.Row.RowIndex + intSubTotalIndex, SubTotalRow);
        intSectorGroupStartRowIndex = e.Row.RowIndex + intSubTotalIndex;
        intSubTotalIndex++;

        pfSectorGroupTotal = new ProfitLossTotal();
        pfNameGroupTotal = new ProfitLossTotal();
        pfAccountSectionGroupTotal = new ProfitLossTotal();
        #endregion
    }

    if (IsNameSubTotalRowNeedToAdd)
    {
        #region Name Sub Total
        GridView grdViewProducts = (GridView)sender;

        GridViewRow SubTotalRow = new GridViewRow(0, 0, DataControlRowType.DataRow, DataControlRowState.Insert);

        TableCell cell = new TableCell();

        cell = new TableCell();
        cell.Text = string.Empty;
        cell.CssClass = "DataCell";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.HorizontalAlign = HorizontalAlign.Left;
        cell.ColumnSpan = 2;
        cell.CssClass = "NameTotalRowStyle";
        //cell.Text = DataBinder.Eval(e.Row.DataItem, "Name").ToString() + " (BSE : " + DataBinder.Eval(e.Row.DataItem, "BSECode").ToString() + ", NSE : " + DataBinder.Eval(e.Row.DataItem, "NSECode").ToString() + ")";
        cell.Text = DataBinder.Eval(e.Row.DataItem, "Name").ToString();
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfNameGroupTotal.Sep12);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "NameTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfNameGroupTotal.Sep11);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "NameTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfNameGroupTotal.Sep10);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "NameTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfNameGroupTotal.Sep09);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "NameTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfNameGroupTotal.Sep08);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "NameTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        //Adding the Row at the RowIndex position in the Grid
        grdViewProducts.Controls[0].Controls.AddAt(e.Row.RowIndex + intSubTotalIndex, SubTotalRow);
        intNameGroupStartRowIndex = e.Row.RowIndex + intSubTotalIndex;
        intSubTotalIndex++;

        pfNameGroupTotal = new ProfitLossTotal();
        pfAccountSectionGroupTotal = new ProfitLossTotal();
        #endregion
    }

    if (IsAccountSectionSubTotalRowNeedToAdd)
    {
        #region Account Section Sub Total
        GridView grdViewProducts = (GridView)sender;

        GridViewRow SubTotalRow = new GridViewRow(0, 0, DataControlRowType.DataRow, DataControlRowState.Insert);

        TableCell cell = new TableCell();

        cell = new TableCell();
        cell.Text = string.Empty;
        cell.CssClass = "DataCell";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Empty;
        cell.CssClass = "DataCell";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.HorizontalAlign = HorizontalAlign.Left;
        cell.CssClass = "AccountSectionTotalRowStyle";
        cell.Text = DataBinder.Eval(e.Row.DataItem, "AccountSection").ToString();
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfAccountSectionGroupTotal.Sep12);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "AccountSectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfAccountSectionGroupTotal.Sep11);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "AccountSectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfAccountSectionGroupTotal.Sep10);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "AccountSectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfAccountSectionGroupTotal.Sep09);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "AccountSectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", pfAccountSectionGroupTotal.Sep08);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "AccountSectionTotalRowStyle";
        SubTotalRow.Cells.Add(cell);

        //Adding the Row at the RowIndex position in the Grid
        grdViewProducts.Controls[0].Controls.AddAt(e.Row.RowIndex + intSubTotalIndex, SubTotalRow);
        intAccountSectionGroupStartRowIndex = e.Row.RowIndex + intSubTotalIndex;
        intSubTotalIndex++;

        pfAccountSectionGroupTotal = new ProfitLossTotal();
        #endregion
    }
}

/// <summary>
/// Event fires when data binds to each row
/// Used for calculating Group Total 
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void grdViewProducts_RowDataBound(object sender, GridViewRowEventArgs e)
{
    // This is for calculation of column (Total = Direct + Referral)
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        strPreviousSectorRowID = DataBinder.Eval(e.Row.DataItem, "Sector").ToString();
        strPreviousNameRowID = DataBinder.Eval(e.Row.DataItem, "Name").ToString();
        strPreviousAccountSectionRowID = DataBinder.Eval(e.Row.DataItem, "AccountSection").ToString();

        pfSectorGroupTotal.Sep12 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep12").ToString());
        pfSectorGroupTotal.Sep11 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep11").ToString());
        pfSectorGroupTotal.Sep10 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep10").ToString());
        pfSectorGroupTotal.Sep09 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep09").ToString());
        pfSectorGroupTotal.Sep08 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep08").ToString());

        pfNameGroupTotal.Sep12 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep12").ToString());
        pfNameGroupTotal.Sep11 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep11").ToString());
        pfNameGroupTotal.Sep10 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep10").ToString());
        pfNameGroupTotal.Sep09 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep09").ToString());
        pfNameGroupTotal.Sep08 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep08").ToString());

        pfAccountSectionGroupTotal.Sep12 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep12").ToString());
        pfAccountSectionGroupTotal.Sep11 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep11").ToString());
        pfAccountSectionGroupTotal.Sep10 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep10").ToString());
        pfAccountSectionGroupTotal.Sep09 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep09").ToString());
        pfAccountSectionGroupTotal.Sep08 += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Sep08").ToString());

    }
}

protected void grdViewProducts_DataBound(object sender, EventArgs e)
{
    foreach (ProfitLossTotal total in TotalList)
    {
        GridViewRow row = (GridViewRow)grdViewProducts.Controls[0].Controls[total.RowIndex];

        row.Cells[row.Cells.Count - 5].Text = string.Format("{0:0.00}", total.Sep12);
        row.Cells[row.Cells.Count - 4].Text = string.Format("{0:0.00}", total.Sep11);
        row.Cells[row.Cells.Count - 3].Text = string.Format("{0:0.00}", total.Sep10);
        row.Cells[row.Cells.Count - 2].Text = string.Format("{0:0.00}", total.Sep09);
        row.Cells[row.Cells.Count - 1].Text = string.Format("{0:0.00}", total.Sep08);
    }
}
The Style Sheet
.AccountSectionTotalRowStyle{
    border:solid 1px Black;
    background-color:#a8249d;
    font-weight:bold;
}
.NameTotalRowStyle {
    border:solid 1px Black;
    background-color:#e46144;
    font-weight:bold;
}
.SectionTotalRowStyle {
    border:solid 1px Black;
    background-color:#1c7647;
    font-weight:bold;
}
.GrandTotalRowStyle{
    border:solid 1px White;
    background-color:Gray;
    font-weight:bold;
}
.DataCell
{
    border:solid 1px Black;
}
Here is the output of this example

download the working example of the source code in C# here and in VB here.

Sunday, 6 January 2013

Group Total and Grand Total in GridView – Part 8

In last post, I had posted a post on Group Total and Grand Total on GridView, which displays records in groups and provides the total of each group at the end of the group. In this post, I am planning to include +, - buttons on each group totals which helps to analyze the records easily.

For more understanding, here is the use case of the requirement –
  1. The records defined in the XML should bound to the Grid View in a normal way.
  2. The records should be grouped by Customer Name in first level and Order ID in second level and the Group Total for end of the each group should show.
  3. When Group Total shows at end of each group, the Customer Name and the Order ID information should show in addition to the group total.
  4. As there are two levels of grouping, the Order ID group should show little indent to the Customer Name and the actual value (data row) should show indent to the Order Id.
  5. The Customer Name, Order ID groups must be displayed with different background color to differentiate the groups.
  6. The Grand Total of all the records should be shown after all the records in the Grid.
  7. There must also be +, - buttons on each group total which helps to hide that particular group and make it visible.
  8. There should also be a +, - button at the grand total which used to hide and show all the rows and show only group total.
  9. There is also need to show +, - buttons on the header of each group column which can be used to show and hide the groups at that level for the whole rows.
This requirement talks about having two different groups, Customer Name is the first group and Order ID is the second group. So the grid will have one or more Customer Name as groups, each Customer Name group will have one or more Order ID as group. Actual data row will be displayed under each Order ID.

At the end of the Order ID group, the Order ID value will be displayed with some additional information such as Order Date, Delivery Date. At the end of the Customer Name group, the Customer Name value will be displayed with the Customer ID. Every group will have Group Total.

Before going for actual implementation, please note the following points -
  1. To implement these examples, all the records must show in a single page of the Grid View (So, no pagination). Because for calculating the Group Total and Grand Total, the code required all the records must be in loop.
  2. The records must be sorted on the group wise. So all the records related to a particular group will show one after another. It will be useful for calculating cumulative values together. Keeping records in different group will be considered as separate group and cumulative values will be calculated as another separate group. As we have two groups in this example, we must sort by Customer Name at first and then Order ID next.
In this page, I had provided two level of grouping as explained before. For more grouping, I provided in the downloadable source code at the end.

The implementation goes as below –

The XML source which bond to the GridView
<?xml version="1.0" encoding="utf-8" ?>
<Orders>
  
  <Order CustomerID="ALFKI" CompanyName="Alfreds Futterkiste"
         OrderID="10643" OrderDate="12-Apr-2012" DeliveryDate="15-Apr-2012" ContactTitle="Sales Representative" Address="Obere Str. 5711" City="Berlin" Country="Germany" Phone="030-0074321" Fax="030-0076545"
         ProductID="28" ProductName="Rössle Sauerkraut" UnitPrice="45.60" Quantity="15" Discount="0.25" Amount="683.75" />
  <Order CustomerID="ALFKI" CompanyName="Alfreds Futterkiste"
         OrderID="10643" OrderDate="12-Apr-2012" DeliveryDate="15-Apr-2012" ContactTitle="Sales Representative" Address="Obere Str. 5711" City="Berlin" Country="Germany" Phone="030-0074321" Fax="030-0076545" 
         ProductID="39" ProductName="Chartreuse verte" UnitPrice="18.00" Quantity="21" Discount="0.25" Amount="377.75"/>
  <Order CustomerID="ALFKI" CompanyName="Alfreds Futterkiste"
         OrderID="10643" OrderDate="12-Apr-2012" DeliveryDate="15-Apr-2012" ContactTitle="Sales Representative" Address="Obere Str. 5711" City="Berlin" Country="Germany" Phone="030-0074321" Fax="030-0076545" 
         ProductID="46" ProductName="Spegesild" UnitPrice="12.00" Quantity="2" Discount="0.25" Amount="23.75"/>
  <Order CustomerID="ALFKI" CompanyName="Alfreds Futterkiste"
         OrderID="10692" OrderDate="21-May-2012" DeliveryDate="23-May-2012" ContactTitle="Sales Representative" Address="Obere Str. 5711" City="Berlin" Country="Germany" Phone="030-0074321" Fax="030-0076545" 
         ProductID="63" ProductName="Vegie-spread" UnitPrice="43.90" Quantity="20" Discount="0.00" Amount="878.00"/>
  <Order CustomerID="ALFKI" CompanyName="Alfreds Futterkiste"
         OrderID="10702" OrderDate="01-Jun-2012" DeliveryDate="15-Jun-2012" ContactTitle="Sales Representative" Address="Obere Str. 5711" City="Berlin" Country="Germany" Phone="030-0074321" Fax="030-0076545" 
         ProductID="3" ProductName="Aniseed Syrup" UnitPrice="10.00" Quantity="6" Discount="0.00" Amount="60.00"/>
  
  <Order CustomerID="ANATR" CompanyName="Ana Trujillo Emparedados y helados"
         OrderID="10308" OrderDate="11-Aug-2012" DeliveryDate="21-Sep-2012" ContactTitle="Owner" Address="Avda. de la Constitución 2222" City="México D.F." Country="Mexico" Phone="(5) 555-4729" Fax="(5) 555-3745" 
         ProductID="69" ProductName="Gudbrandsdalsost" UnitPrice="28.80" Quantity="1" Discount="0.00" Amount="28.80"/>
  <Order CustomerID="ANATR" CompanyName="Ana Trujillo Emparedados y helados" 
         OrderID="10308" OrderDate="11-Aug-2012" DeliveryDate="21-Sep-2012" ContactTitle="Owner" Address="Avda. de la Constitución 2222" City="México D.F." Country="Mexico" Phone="(5) 555-4729" Fax="(5) 555-3745" 
         ProductID="70" ProductName="Outback Lager" UnitPrice="12.00" Quantity="5" Discount="0.00" Amount="60.00"/>
  <Order CustomerID="ANATR" CompanyName="Ana Trujillo Emparedados y helados"
         OrderID="10926" OrderDate="01-May-2012" DeliveryDate="12-Jun-2012" ContactTitle="Owner" Address="Avda. de la Constitución 2222" City="México D.F." Country="Mexico" Phone="(5) 555-4729" Fax="(5) 555-3745" 
         ProductID="72" ProductName="Mozzarella di Giovanni" UnitPrice="34.80" Quantity="10" Discount="0.00" Amount="348.00"/>
  
  <Order CustomerID="AROUT" CompanyName="Around the Horn"
         OrderID="10927" OrderDate="11-Aug-2012" DeliveryDate="21-Sep-2012" ContactTitle="Sales Representative" Address="120 Hanover Sq." City="London" Country="UK" Phone="(171) 555-7788" Fax="(171) 555-6750" 
         ProductID="24" ProductName="Guaraná Fantástica" UnitPrice="3.60" Quantity="25" Discount="0.00" Amount="90.00"/>
  <Order CustomerID="AROUT" CompanyName="Around the Horn"
         OrderID="10927" OrderDate="11-Aug-2012" DeliveryDate="21-Sep-2012" ContactTitle="Sales Representative" Address="120 Hanover Sq." City="London" Country="UK" Phone="(171) 555-7788" Fax="(171) 555-6750" 
         ProductID="31" ProductName="Gorgonzola Telino" UnitPrice="12.50" Quantity="50" Discount="0.05" Amount="624.95"/>
  <Order CustomerID="AROUT" CompanyName="Around the Horn"
         OrderID="11016" OrderDate="15-May-2012" DeliveryDate="15-Jun-2012" ContactTitle="Sales Representative" Address="120 Hanover Sq." City="London" Country="UK" Phone="(171) 555-7788" Fax="(171) 555-6750" 
         ProductID="31" ProductName="Gorgonzola Telino" UnitPrice="12.50" Quantity="15" Discount="0.00" Amount="187.50"/>
  <Order CustomerID="AROUT" CompanyName="Around the Horn"
         OrderID="11016" OrderDate="15-May-2012" DeliveryDate="15-Jun-2012" ContactTitle="Sales Representative" Address="120 Hanover Sq." City="London" Country="UK" Phone="(171) 555-7788" Fax="(171) 555-6750" 
         ProductID="36" ProductName="Inlagd Sill" UnitPrice="19.00" Quantity="16" Discount="0.00" Amount="304.00"/>
  
  <Order CustomerID="BERGS" CompanyName="Berglunds snabbköp"
         OrderID="10278" OrderDate="02-Nov-2012" DeliveryDate="15-Nov-2012" ContactTitle="Order Administrator" Address="Berguvsvägen  8" City="Luleå" Country="Sweden" Phone="0921-12 34 65" Fax="0921-12 34 67" 
         ProductID="44" ProductName="Gula Malacca" UnitPrice="15.50" Quantity="16" Discount="0.00" Amount="248.00"/>
  <Order CustomerID="BERGS" CompanyName="Berglunds snabbköp"
         OrderID="10278" OrderDate="02-Nov-2012" DeliveryDate="15-Nov-2012" ContactTitle="Order Administrator" Address="Berguvsvägen  8" City="Luleå" Country="Sweden" Phone="0921-12 34 65" Fax="0921-12 34 67" 
         ProductID="59" ProductName="Raclette Courdavault" UnitPrice="44.00" Quantity="15" Discount="0.00" Amount="660.00"/>
  <Order CustomerID="BERGS" CompanyName="Berglunds snabbköp"
         OrderID="10278" OrderDate="02-Nov-2012" DeliveryDate="15-Nov-2012" ContactTitle="Order Administrator" Address="Berguvsvägen  8" City="Luleå" Country="Sweden" Phone="0921-12 34 65" Fax="0921-12 34 67" 
         ProductID="63" ProductName="Vegie-spread" UnitPrice="35.10" Quantity="8" Discount="0.00" Amount="280.80"/>
  <Order CustomerID="BERGS" CompanyName="Berglunds snabbköp" 
         OrderID="10278" OrderDate="02-Nov-2012" DeliveryDate="15-Nov-2012" ContactTitle="Order Administrator" Address="Berguvsvägen  8" City="Luleå" Country="Sweden" Phone="0921-12 34 65" Fax="0921-12 34 67" 
         ProductID="73" ProductName="Röd Kaviar" UnitPrice="12.00" Quantity="25" Discount="0.00" Amount="300.00"/>
  <Order CustomerID="BERGS" CompanyName="Berglunds snabbköp" 
         OrderID="10280" OrderDate="15-Apr-2012" DeliveryDate="07-Jul-2012" ContactTitle="Order Administrator" Address="Berguvsvägen  8" City="Luleå" Country="Sweden" Phone="0921-12 34 65" Fax="0921-12 34 67" 
         ProductID="24" ProductName="Guaraná Fantástica" UnitPrice="3.60" Quantity="12" Discount="0.00" Amount="43.20"/>
</Orders>
The ASPX script
<asp:GridView ID="grdViewOrders" runat="server" AutoGenerateColumns="False" TabIndex="1"
    Width="100%" DataSourceID="XmlDataSource1" CssClass="grdViewOrders"
    CellPadding="4" ForeColor="Black" GridLines="Vertical" BackColor="White" 
    BorderColor="Black" BorderStyle="Solid" BorderWidth="1px"
    OnRowDataBound="grdViewOrders_RowDataBound"
    OnRowCreated="grdViewOrders_RowCreated" >            
    <Columns>
        <asp:TemplateField HeaderText="">
            <ItemStyle Width="10px" CssClass="DataCell" BorderStyle="Solid" BorderWidth="1" />
            <ItemTemplate></ItemTemplate>
            <HeaderStyle CssClass="DataCell" Width="10px" />
        </asp:TemplateField>
        <asp:TemplateField HeaderText="">
            <ItemStyle Width="10px" CssClass="DataCell" />
            <ItemTemplate></ItemTemplate>
            <HeaderStyle CssClass="DataCell" Width="10px" />
        </asp:TemplateField>
        <asp:TemplateField HeaderText="">
            <ItemStyle Width="10px" CssClass="DataCell" />
            <ItemTemplate></ItemTemplate>
            <HeaderStyle CssClass="DataCell" Width="10px" />
        </asp:TemplateField>
                
        <asp:BoundField DataField="ProductName" HeaderText="ProductName">
            <ItemStyle HorizontalAlign="Left" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" DataFormatString="{0:c}">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="Quantity" HeaderText="Quantity" DataFormatString="{0:c}">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="Discount" HeaderText="Discount" DataFormatString="{0:c}">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
        <asp:BoundField DataField="Amount" HeaderText="Amount" DataFormatString="{0:c}">
            <ItemStyle HorizontalAlign="Right" CssClass="DataCell"></ItemStyle>
            <HeaderStyle CssClass="DataCell" />
        </asp:BoundField>
    </Columns>
    <RowStyle BackColor="#F7F7DE" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
    <FooterStyle BackColor="#CCCC99" />
    <PagerStyle BackColor="#F7F7DE" ForeColor="Black" HorizontalAlign="Right" />
    <SelectedRowStyle BackColor="#CE5D5A" ForeColor="White" Font-Bold="True" />
    <HeaderStyle BackColor="#6B696B" Font-Bold="True" ForeColor="White" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
    <AlternatingRowStyle BackColor="White" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
    <SortedAscendingCellStyle BackColor="#FBFBF2" />
    <SortedAscendingHeaderStyle BackColor="#848384" />
    <SortedDescendingCellStyle BackColor="#EAEAD3" />
    <SortedDescendingHeaderStyle BackColor="#575357" />
</asp:GridView>
<asp:XmlDataSource ID="XmlDataSource1" runat="server" DataFile="Data/Orders.xml"></asp:XmlDataSource>
The C# Code behind
// To keep track of the previous row Group Identifier
string strPreviousRowCustomerID = string.Empty; // First Level Grouping Track Id (Used to identify the group is getting changed)
string strPreviousRowOrderID = string.Empty; // Second Level Grouping Track Id (Used to identify the group are getting changed)

// To keep track the Index of Group Total
int intSubTotalIndex = 0; // For increasing the row count (for inserting a row in the current row)

string strCustomerGroupHeaderText = string.Empty;
string strOrderGroupHeaderText = string.Empty;

// To customer temporarily store Sub Total - First Level Grouping (Declare variables for sum, count columns)
double dblCustomerGroupSubTotalUnitPrice = 0;
double dblCustomerGroupSubTotalQuantity = 0;
double dblCustomerGroupSubTotalDiscount = 0;
double dblCustomerGroupSubTotalAmount = 0;

// To order temporarily store Sub Total - Second Level Grouping
double dblOrderGroupSubTotalUnitPrice = 0;
double dblOrderGroupSubTotalQuantity = 0;
double dblOrderGroupSubTotalDiscount = 0;
double dblOrderGroupSubTotalAmount = 0;

// To temporarily store Grand Total
double dblGrandTotalUnitPrice = 0;
double dblGrandTotalQuantity = 0;
double dblGrandTotalDiscount = 0;
double dblGrandTotalAmount = 0;

protected void Page_Load(object sender, EventArgs e)
{
    TableCell cell = grdViewOrders.HeaderRow.Cells[0]; // First Cell in the Header - Grand Total
    System.Web.UI.HtmlControls.HtmlImage img = new System.Web.UI.HtmlControls.HtmlImage();
    img.Src = "images/minus.gif";
    img.Attributes.Add("class", "ExpandCollapseGrandStyle");
    img.Attributes.Add("alt", "0");
    cell.Controls.Add(img);
    cell.HorizontalAlign = HorizontalAlign.Left;
    //cell.CssClass = "HeaderCell";
    cell.Attributes.Add("alt", "HeaderCell" + ",0");

    cell = grdViewOrders.HeaderRow.Cells[1]; // Second Group
    img = new System.Web.UI.HtmlControls.HtmlImage();
    img.Src = "images/minus.gif";
    img.Attributes.Add("class", "ExpandCollapseHeaderStyle");
    img.Attributes.Add("alt", "1");
    cell.Controls.Add(img);
    cell.HorizontalAlign = HorizontalAlign.Left;
    //cell.CssClass = "HeaderCell" + "1";
    cell.Attributes.Add("alt", "HeaderCell" + ",1");

    cell = grdViewOrders.HeaderRow.Cells[2]; // First Group
    img = new System.Web.UI.HtmlControls.HtmlImage();
    img.Src = "images/minus.gif";
    img.Attributes.Add("class", "ExpandCollapseHeaderStyle");
    img.Attributes.Add("alt", "2");
    cell.Controls.Add(img);
    cell.HorizontalAlign = HorizontalAlign.Left;
    //cell.CssClass = "HeaderCell" + "2";
    cell.Attributes.Add("alt", "HeaderCell" + ",2");
}

/// <summary>
/// Event fires for every row creation
/// Used for creating SubTotal row when next group starts by adding Group Total at previous row manually
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void grdViewOrders_RowCreated(object sender, GridViewRowEventArgs e)
{
    bool IsCustomerSubTotalRowNeedToAdd = false; // First Level Grouping
    bool IsOrderSubTotalRowNeedToAdd = false; // Second Level Grouping
    bool IsGrandTotalRowNeedtoAdd = false;

    #region Reset firstlevel, secondlevel counters for Sub Total.
    // Not First row
    if ((strPreviousRowCustomerID != string.Empty) && (DataBinder.Eval(e.Row.DataItem, "CustomerID") != null))
    {
        // When customer is not changing, but order is changing - second level grouping changing
        if ((strPreviousRowCustomerID == DataBinder.Eval(e.Row.DataItem, "CustomerID").ToString()) &&
            (strPreviousRowOrderID != DataBinder.Eval(e.Row.DataItem, "OrderID").ToString())
        )
        {
            IsOrderSubTotalRowNeedToAdd = true;
        }
        // When customer changing - first level grouping changing
        if (strPreviousRowCustomerID != DataBinder.Eval(e.Row.DataItem, "CustomerID").ToString())
        {
            IsCustomerSubTotalRowNeedToAdd = true;
            IsOrderSubTotalRowNeedToAdd = true;
        }
    }
    #endregion

    #region When final row completed. firstlevel, secondlevel, and Grand Total needed
    if ((strPreviousRowCustomerID != string.Empty) &&
        (strPreviousRowOrderID != string.Empty) &&
        (DataBinder.Eval(e.Row.DataItem, "CustomerID") == null) &&
        (DataBinder.Eval(e.Row.DataItem, "OrderID") == null)
        )
    {
        IsCustomerSubTotalRowNeedToAdd = true;
        IsOrderSubTotalRowNeedToAdd = true;
        IsGrandTotalRowNeedtoAdd = true;
    }
    #endregion

    // Second Level Grouping
    if (IsOrderSubTotalRowNeedToAdd)
    {
        #region Inserting Order Group Total row - Second Level Grouping
        GridView grdViewOrders = (GridView)sender;

        // Creating a Row
        GridViewRow row = new GridViewRow(0, 0, DataControlRowType.DataRow, DataControlRowState.Insert);

        //Adding Group Expand Collapse Cell 
        TableCell cell = new TableCell();
        cell.CssClass = "DataCell";
        row.Cells.Add(cell);
        row.CssClass = "ExpandCollapse" + strPreviousRowOrderID;

        //Adding Expand Collapse Cell 
        cell = new TableCell();
        cell.CssClass = "DataCell";
        row.Cells.Add(cell);

        cell = new TableCell();
        System.Web.UI.HtmlControls.HtmlImage img = new System.Web.UI.HtmlControls.HtmlImage();
        img.Src = "images/minus.gif";
        img.Attributes.Add("alt", strPreviousRowOrderID + ",2"); // Second Level Grouping
        img.Attributes.Add("class", "ExpandCollapseStyle");
        cell.Controls.Add(img);
        cell.HorizontalAlign = HorizontalAlign.Left;
        cell.CssClass = "FirstGroupTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Header Cell 
        cell = new TableCell();
        cell.Text = strOrderGroupHeaderText;
        cell.HorizontalAlign = HorizontalAlign.Left;
        cell.ColumnSpan = 1;
        cell.CssClass = "FirstGroupTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Unit Price Column
        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", dblOrderGroupSubTotalUnitPrice);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "FirstGroupTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Quantity Column
        cell = new TableCell();
        cell.Text = dblOrderGroupSubTotalQuantity.ToString();
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "FirstGroupTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Discount Column
        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", dblOrderGroupSubTotalDiscount);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "FirstGroupTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Amount Column
        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", dblOrderGroupSubTotalAmount);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "FirstGroupTotalRowStyle";
        row.Cells.Add(cell);

        //Adding the Row at the RowIndex position in the Grid
        grdViewOrders.Controls[0].Controls.AddAt(intSubTotalIndex, row);
        intSubTotalIndex++;
        #endregion

        #region Reseting the Sub Total Variables
        dblOrderGroupSubTotalUnitPrice = 0;
        dblOrderGroupSubTotalQuantity = 0;
        dblOrderGroupSubTotalDiscount = 0;
        dblOrderGroupSubTotalAmount = 0;
        #endregion
    }

    // First Level Grouping
    if (IsCustomerSubTotalRowNeedToAdd)
    {
        #region Inserting Customer Group Total Row - First Level Grouping
        GridView grdViewOrders = (GridView)sender;

        // Creating a Row
        GridViewRow row = new GridViewRow(0, 0, DataControlRowType.DataRow, DataControlRowState.Insert);

        //Adding Group Expand Collapse Cell 
        TableCell cell = new TableCell(); 
        cell.CssClass = "DataCell";
        row.Cells.Add(cell);
        row.CssClass = "ExpandCollapse" + strPreviousRowCustomerID;

        //Adding Expand Collapse Cell 
        cell = new TableCell();
        System.Web.UI.HtmlControls.HtmlImage img = new System.Web.UI.HtmlControls.HtmlImage();
        img.Src = "images/minus.gif";
        img.Attributes.Add("alt", strPreviousRowCustomerID + ",1"); // First Level Grouping
        img.Attributes.Add("class", "ExpandCollapseStyle");
        cell.Controls.Add(img);
        cell.HorizontalAlign = HorizontalAlign.Left;
        cell.CssClass = "SecondGroupTotalRowStyle";
        row.Cells.Add(cell);

        cell = new TableCell();
        cell.CssClass = "SecondGroupTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Header Cell 
        cell = new TableCell();
        cell.Text = strCustomerGroupHeaderText;
        cell.HorizontalAlign = HorizontalAlign.Left;
        cell.ColumnSpan = 1;
        cell.CssClass = "SecondGroupTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Unit Price Column
        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", dblCustomerGroupSubTotalUnitPrice);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SecondGroupTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Quantity Column
        cell = new TableCell();
        cell.Text = dblCustomerGroupSubTotalQuantity.ToString();
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SecondGroupTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Discount Column
        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", dblCustomerGroupSubTotalDiscount);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SecondGroupTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Amount Column
        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", dblCustomerGroupSubTotalAmount);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "SecondGroupTotalRowStyle";
        row.Cells.Add(cell);

        //Adding the Row at the RowIndex position in the Grid
        grdViewOrders.Controls[0].Controls.AddAt(intSubTotalIndex, row);
        intSubTotalIndex++;
        #endregion

        #region Reseting the Sub Total Variables
        dblCustomerGroupSubTotalUnitPrice = 0;
        dblCustomerGroupSubTotalQuantity = 0;
        dblCustomerGroupSubTotalDiscount = 0;
        dblCustomerGroupSubTotalAmount = 0;
        #endregion
    }

    if (IsGrandTotalRowNeedtoAdd)
    {
        #region Grand Total Row - Third Level Grouping
        GridView grdViewOrders = (GridView)sender;

        // Creating a Row
        GridViewRow row = new GridViewRow(0, 0, DataControlRowType.DataRow, DataControlRowState.Insert);

        //Adding Group Expand Collapse Cell 
        TableCell cell = new TableCell();
        //System.Web.UI.HtmlControls.HtmlImage img = new System.Web.UI.HtmlControls.HtmlImage();
        //img.Src = "images/minus.gif";
        //img.Attributes.Add("class", "ExpandCollapseGrandStyle");
        //img.Attributes.Add("alt", "0");
        //cell.Controls.Add(img);
        //cell.HorizontalAlign = HorizontalAlign.Left;
        cell.CssClass = "GrandTotalRowStyle";
        row.Cells.Add(cell);

        cell = new TableCell();
        cell.CssClass = "GrandTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Expand Collapse Cell 
        cell = new TableCell();
        cell.CssClass = "GrandTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Header Cell 
        cell = new TableCell();
        cell.Text = "Grand Total";
        cell.HorizontalAlign = HorizontalAlign.Left;
        cell.ColumnSpan = 1;
        cell.CssClass = "GrandTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Unit Price Column
        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", dblGrandTotalUnitPrice);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "GrandTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Quantity Column
        cell = new TableCell();
        cell.Text = dblGrandTotalQuantity.ToString();
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "GrandTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Discount Column
        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", dblGrandTotalDiscount);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "GrandTotalRowStyle";
        row.Cells.Add(cell);

        //Adding Amount Column
        cell = new TableCell();
        cell.Text = string.Format("{0:0.00}", dblGrandTotalAmount);
        cell.HorizontalAlign = HorizontalAlign.Right;
        cell.CssClass = "GrandTotalRowStyle";
        row.Cells.Add(cell);

        //Adding the Row at the RowIndex position in the Grid
        grdViewOrders.Controls[0].Controls.AddAt(e.Row.RowIndex, row);
        #endregion
    }

    #region Getting the Group Header Text
    if (DataBinder.Eval(e.Row.DataItem, "CustomerID") != null)
        strCustomerGroupHeaderText = DataBinder.Eval(e.Row.DataItem, "CompanyName").ToString() + " (" + DataBinder.Eval(e.Row.DataItem, "CustomerID").ToString() + ")";

    if (DataBinder.Eval(e.Row.DataItem, "OrderID") != null)
        strOrderGroupHeaderText = DataBinder.Eval(e.Row.DataItem, "OrderID").ToString() + " (Order Date : " + DataBinder.Eval(e.Row.DataItem, "OrderDate").ToString() + ", Delivery Date : " + DataBinder.Eval(e.Row.DataItem, "DeliveryDate").ToString() + ")";
    #endregion
}

/// <summary>
/// Event fires when data binds to each row
/// Used for calculating Group Total 
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void grdViewOrders_RowDataBound(object sender, GridViewRowEventArgs e)
{
    // This is for cumulating the values
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        strPreviousRowCustomerID = DataBinder.Eval(e.Row.DataItem, "CustomerID").ToString();
        strPreviousRowOrderID = DataBinder.Eval(e.Row.DataItem, "OrderID").ToString();

        double dblUnitPrice = Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "UnitPrice").ToString());
        double dblQuantity = Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Quantity").ToString());
        double dblDiscount = Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Discount").ToString());
        double dblAmount = Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Amount").ToString());

        // Cumulating Sub Total
        dblCustomerGroupSubTotalUnitPrice += dblUnitPrice;
        dblCustomerGroupSubTotalQuantity += dblQuantity;
        dblCustomerGroupSubTotalDiscount += dblDiscount;
        dblCustomerGroupSubTotalAmount += dblAmount;

        dblOrderGroupSubTotalUnitPrice += dblUnitPrice;
        dblOrderGroupSubTotalQuantity += dblQuantity;
        dblOrderGroupSubTotalDiscount += dblDiscount;
        dblOrderGroupSubTotalAmount += dblAmount;

        // Cumulating Grand Total
        dblGrandTotalUnitPrice += dblUnitPrice;
        dblGrandTotalQuantity += dblQuantity;
        dblGrandTotalDiscount += dblDiscount;
        dblGrandTotalAmount += dblAmount;

        e.Row.Style.Add("display", "block");
        e.Row.Cells[0].CssClass = "DataRowStyle";
        e.Row.Cells[0].Attributes.Add("alt", ",3");
    }
    intSubTotalIndex++;
}
The Style sheet
.SecondGroupTotalRowStyle{
    border:solid 1px Black;
    background-color:chocolate;
    font-weight:bold;
}
.FirstGroupTotalRowStyle {
    border:solid 1px Black;
    background-color:#81BEF7;
}
.GrandTotalRowStyle{
    border:solid 1px Black;  
    background-color:Gray;
    font-weight:bold;
}
.GroupHeaderStyle{
    border:solid 1px Black;
    background-color:#81BEF7;
    font-weight:bold;
}
.ExpandCollapseStyle
{
    border:0px;
    cursor:pointer;
    width:10px;
}
.ExpandCollapseGrandStyle
{
    border:0px;
    cursor:pointer;
    width:10px;
}
.DataCell
{
    border:solid 1px Black;
}
.ExpandCollapseHeaderStyle {
    background-color:#6B696B;
    cursor:pointer;
}
The JavaScript
//ExpandCollapse
$(document).ready(function () {
    $('.ExpandCollapseStyle').click(function () {
        var selectedTrackId = $(this).attr('alt');

        var isSelectedTrackerFound = false;
        var selectedTrackerGroupIndex = 0;

        var ExpandOrCollapse = $(this).attr('src');


        $($(".grdViewOrders tr").get().reverse()).each(function () {

            var currentTrackId = $(this).find(".ExpandCollapseStyle").attr('alt');

            if (currentTrackId == null)
                currentTrackId = $(this).find(".DataRowStyle").attr('alt');

            if (currentTrackId != null) {

                if (selectedTrackId.split(",")[0] == currentTrackId.split(",")[0]) {

                    isSelectedTrackerFound = true;
                    if (selectedTrackerGroupIndex == 0) {
                        selectedTrackerGroupIndex = currentTrackId.split(",")[1];

                        if (ExpandOrCollapse == 'images/plus.gif') {
                            $(this).find(".ExpandCollapseStyle").attr('src', 'images/minus.gif');
                        }
                        else {
                            $(this).find(".ExpandCollapseStyle").attr('src', 'images/plus.gif');
                        }
                    }
                }
                else {
                    if (currentTrackId != null) {
                        if (parseInt(selectedTrackerGroupIndex) > 0) {
                            if (parseInt(selectedTrackerGroupIndex) >= parseInt(currentTrackId.split(",")[1]))
                                isSelectedTrackerFound = false;
                        }
                    }
                    if (isSelectedTrackerFound == true) {

                        if (ExpandOrCollapse == 'images/plus.gif') {
                            $(this).css("display", "block");
                            $(this).find(".ExpandCollapseStyle").attr('src', 'images/minus.gif');
                        }
                        else {
                            $(this).css("display", "none");
                            $(this).find(".ExpandCollapseStyle").attr('src', 'images/plus.gif');
                        }
                    }
                }
            }
        });
    })

    $('.ExpandCollapseGrandStyle').click(function () {

        var ExpandOrCollapse = $(this).attr('src');
        var selectedTrackId = $(this).attr('alt');

        var isSelectedTrackerFound = false;
        var selectedTrackerGroupIndex = 0;

        $($(".grdViewOrders tr").get().reverse()).each(function () {

            var currentTrackId = $(this).find(".ExpandCollapseStyle").attr('alt');


            if (currentTrackId != null) {

                if (ExpandOrCollapse == 'images/plus.gif') {

                    if (parseInt(selectedTrackId) + 1 == parseInt(currentTrackId.split(",")[1])) {
                        $(this).find(".ExpandCollapseStyle").attr('src', 'images/plus.gif');
                        $('.ExpandCollapse' + currentTrackId.split(",")[0]).css("display", "block");
                    }
                    if (parseInt(selectedTrackId) + 1 < parseInt(currentTrackId.split(",")[1])) {
                        $('.ExpandCollapse' + currentTrackId.split(",")[0]).css("display", "none");
                    }
                }
                else {

                    $(this).find(".ExpandCollapseStyle").attr('src', 'images/plus.gif');
                    $('.ExpandCollapse' + currentTrackId.split(",")[0]).css("display", "none");
                }
            }
            else { // Data Row
                currentTrackId = $(this).find(".DataRowStyle").attr('alt');
                if (currentTrackId != null) {
                    if (ExpandOrCollapse == 'images/plus.gif') {
                        //$(this).css("display", "block");
                        $(this).css("display", "none");
                    }
                    else {
                        $(this).css("display", "none");
                    }
                }
            }
        });

        $(".grdViewOrders tr").children("th").each(function (index) {

            var currentTrackId = $(this).attr('alt');
            if (currentTrackId != null) {
                if (parseInt(currentTrackId.split(",")[1]) > parseInt(selectedTrackId)) {
                    if (ExpandOrCollapse == 'images/minus.gif')
                        $(this).find('.ExpandCollapseHeaderStyle').attr('src', 'images/plus.gif');
                }
            }
        });

        if ($('.ExpandCollapseGrandStyle').attr('src') == 'images/minus.gif') {
            $('.ExpandCollapseGrandStyle').attr('src', 'images/plus.gif');
        }
        else {
            $('.ExpandCollapseGrandStyle').attr('src', 'images/minus.gif');
        }
    })

    $('.ExpandCollapseHeaderStyle').click(function () {

        var selectedTrackId = $(this).attr('alt');

        var ExpandOrCollapse = $(this).attr('src');

        $($(".grdViewOrders tr").get().reverse()).each(function () {

            var currentTrackId = $(this).find(".ExpandCollapseStyle").attr('alt');

            if (currentTrackId != null) {

                if (ExpandOrCollapse == 'images/plus.gif') {

                    if (parseInt(currentTrackId.split(",")[1]) <= parseInt(selectedTrackId)) {
                        $(this).find(".ExpandCollapseStyle").attr('src', 'images/minus.gif');
                        $('.ExpandCollapse' + currentTrackId.split(",")[0]).css("display", "block");
                    }

                    if (parseInt(currentTrackId.split(",")[1]) == (parseInt(selectedTrackId) + 1)) {
                        $(this).find(".ExpandCollapseStyle").attr('src', 'images/plus.gif');
                        $('.ExpandCollapse' + currentTrackId.split(",")[0]).css("display", "block");
                    }
                    if (parseInt(currentTrackId.split(",")[1]) > (parseInt(selectedTrackId) + 1))
                        $('.ExpandCollapse' + currentTrackId.split(",")[0]).css("display", "none");
                }
                else {

                    if (parseInt(currentTrackId.split(",")[1]) == parseInt(selectedTrackId))
                        $(this).find(".ExpandCollapseStyle").attr('src', 'images/plus.gif');

                    if (parseInt(currentTrackId.split(",")[1]) > parseInt(selectedTrackId)) {
                        $('.ExpandCollapse' + currentTrackId.split(",")[0]).css("display", "none");
                    }
                    if (parseInt(currentTrackId.split(",")[1]) < parseInt(selectedTrackId))
                        $(this).find(".ExpandCollapseStyle").attr('src', 'images/minus.gif');
                }
            }

            currentTrackId = $(this).find(".DataRowStyle").attr('alt');
            if (currentTrackId != null) {

                if (parseInt(currentTrackId.split(",")[1]) > parseInt(selectedTrackId)) {
                    if (ExpandOrCollapse == 'images/plus.gif') {
                        if (parseInt(currentTrackId.split(",")[1]) == (parseInt(selectedTrackId) + 1))
                            $(this).css("display", "block");
                        else
                            $(this).css("display", "none");
                    }
                    else {
                        $(this).css("display", "none");
                    }
                }
            }
        });

        $(".grdViewOrders tr").children("th").each(function (index) {

            var currentTrackId = $(this).attr('alt');
            if (currentTrackId != null) {
                if (parseInt(currentTrackId.split(",")[1]) > parseInt(selectedTrackId)) {
                    if (ExpandOrCollapse == 'images/minus.gif')
                        $(this).find('.ExpandCollapseHeaderStyle').attr('src', 'images/plus.gif');
                }
                if (parseInt(currentTrackId.split(",")[1]) < parseInt(selectedTrackId)) {
                    if (ExpandOrCollapse == 'images/plus.gif') {
                        $(this).find('.ExpandCollapseHeaderStyle').attr('src', 'images/minus.gif');
                        $('.ExpandCollapseGrandStyle').attr('src', 'images/minus.gif');
                    }
                }
            }
        });

        if ($(this).attr('src') == 'images/minus.gif') {
            $(this).attr('src', 'images/plus.gif');
        }
        else {
            $(this).attr('src', 'images/minus.gif');
        }
    })

    function isDisplayed(object) {
        // if the object is visible return true
        if ($(object).css('display') == 'block') {
            return true;
        }
        // if the object is not visible return false
        return false;
    };
});
The output of this example will be as below.





Below is the screenshots for five level of grouping.






download the working example of the source code here in C# here and in VB here.