Sunday 3 January 2016

Scheduling to Stop & Start the Azure VM using Azure Automation


This video explains to Shut down the Azure Virtual Machine using Azure Automation.




Powershell scripts used in this video can be downloaded here.


Monday 22 July 2013

Generating Shared Access Signature using Storage API and REST API

When we create a blob container as private, we need to have shared access signature to access a particular blob. The shared access signature can be created either using Storage API or REST API.

When we use Storage client, the storage client assembly needs to be deployed with the application. But when using the RESTful service, it can be used any application support HTTP protocol.

Below is the code for creating SAS token –

First let us create an UI for preparing SAS token
<table width="100%">
    <tr>
        <td>
            <asp:Label ID="lblAccountName" runat="server" Text="Account Name : "></asp:Label>
        </td>
        <td>
            <asp:TextBox ID="txtAccountName" runat="server" Width="400px" Text=""></asp:TextBox>
        </td>
    </tr>
    <tr>
        <td>
            <asp:Label ID="lblContainer" runat="server" Text="Container : "></asp:Label>
        </td>
        <td>
            <asp:TextBox ID="txtContainer" runat="server" Width="400px" Text=""></asp:TextBox>
        </td>
    </tr>
    <tr>
        <td>
            <asp:Label ID="lblSharedKey" runat="server" Text="SharedKey : "></asp:Label>
        </td>
        <td>
            <asp:TextBox ID="txtSharedKey" runat="server" Width="600px" Text=""></asp:TextBox>
        </td>
    </tr>
    <tr>
        <td>
            <asp:Label ID="lblBlob" runat="server" Text="Blob : "></asp:Label>
        </td>
        <td>
            <asp:TextBox ID="txtBlob" runat="server" Width="600px" Text=""></asp:TextBox>
        </td>
    </tr>
    <tr>
        <td colspan="2">
            <asp:TextBox ID="txtSASToken" runat="server" style="width:100%;" TextMode="MultiLine" ReadOnly="true"></asp:TextBox>
        </td>
    </tr>
    <tr>
        <td colspan="2">
            <asp:Button ID="btnUsingClient" runat="server" Text="Using Storage Client" Width="200px" OnClick="btnUsingClient_Click" />
            <asp:Button ID="btnUsingREST" runat="server" Text="Using REST" Width="200px" OnClick="btnUsingREST_Click" />
        </td>
    </tr>
</table>

The code for creating SAS token using Storage API
protected void btnUsingClient_Click(object sender, EventArgs e)
{
 var storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
 CloudBlobClient client = storageAccount.CreateCloudBlobClient();
 CloudBlobContainer container = client.GetContainerReference(txtContainer.Text);

 string path = container.GetBlobReferenceFromServer(txtBlob.Text).Uri.ToString();

 SharedAccessBlobPermissions permissions = new SharedAccessBlobPermissions();
 permissions = SharedAccessBlobPermissions.Read;

 SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy()
 {
  Permissions = permissions,
  SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5), // To make adjustment on UTC time difference
  SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(60)
 };

 string queryString = container.GetSharedAccessSignature(policy);

 txtSASToken.Text = path + queryString;
}

The code for creating SAS token using Storage API
protected void btnUsingREST_Click(object sender, EventArgs e)
{
 string permissions = "r";
 DateTime startTime = DateTime.UtcNow.AddMinutes(-5); // To make adjustment on UTC time difference
 DateTime expiryTime = startTime.AddMinutes(60);

 string signature = permissions + "\n" +
  startTime.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") + "\n" +
  expiryTime.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") + "\n" +
  string.Format("/{0}/{1}", txtAccountName.Text, txtContainer.Text) + "\n" +
  String.Empty;

 using (var hmac = new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(txtSharedKey.Text)))
  signature = Convert.ToBase64String(hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(signature)));

 string strSASToken = string.Format("st={0}&se={1}&sr=c&sp=r&sig={2}",
   Uri.EscapeDataString(startTime.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")),
   Uri.EscapeDataString(expiryTime.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")),
   Uri.EscapeDataString(signature));

 txtSASToken.Text = txtBlob.Text + "?" + strSASToken;
}

The output of the code would looks as below

Thursday 11 July 2013

Installing assemblies to GAC on Azure Instances

After a four months of interval I again restarting to blog in this blog. Sorry for those who asked some queries which I could not answer on time, was busy with work.

Normally, when we refer some assemblies in Web (or Worker) role project, we will be changing the Copy Local property of the assembly reference to True, which make sure the assembly to get copied with the package and deployed to Azure instances. This helps deploying assemblies that private and will be referred from any of a directory from the application path.

But in some cases, we required to deploy some assemblies to the GAC on the Azure instances and refer from the project. For example – Custom HTTP handler assemblies, HTTP Modules, etc.

To install assemblies into GAC on development servers, we will be using various methods including gacutil.exe command utility. But GACUtil.exe command will not exist in the Azure instances. When I tried coping gacutil.exe and related config file with the package and deploy to Azure and used startup script to deploy my package. But failed. I thought it should be possible by installing .NET runtime as part of Azure startup just to get the gacutil.exe command to work, which is a wrong sign to me to go with.

Finally, I decided to install the assembly using PowerShell script. Below are the steps I used to install my custom assembly into GAC on Azure VM. (Just to say, I did not invent anything here, get the pieces from net and used together).

Getting Ready:

Step 1: Created a sample Azure Web Role project (InstallGACdllEx) and verified the application works perfectly.

Step 2: Added a Class Library in to the project and named it as MyGACdll. This is going to be my custom assembly, which I need to install into GAC on Azure instance.

Step 3: Added a custom method on the project, to verify.
public class Verify
{
    public string SayHello(string myName)
    {
        return "Hello " + myName;
    }
}
Step 4: To install the assembly install into GAC, the assembly require signing with Strong Name Key. So, open the Properties of the project, and select the Signing tab.

Select the Sign the assembly checkbox and choose the <New…> from the dropdown box.

There will be a Create Strong Name Key popup.

Step 5: Provide Key file name and password. Press OK.


Finally my MyGACdll project and the solution look like this.


Step 6: Added the MyGACdll project as reference to the WebRole1 project. Make the Copy Local property to Local.


Step 7: Added below code in the page load event to show list of GAC assemblies in the default page.

Step 8: Deployed to Azure and browsed the deployed url. The page shows, the list of GAC assemblies into the default page.


Installing assembly to GAC:

To install assembly into the GAC on Azure instances, we can use Startup Script. Verify the below links for more information on startup script and to run PowerShell script on startup.

https://thirumalaipm.blogspot.com/2011/11/windows-azure-startup-tasks.html
https://thirumalaipm.blogspot.com/2011/11/executing-powershell-script-with.html

Step 1: Added Add-AssemblyToGlobalAssemblyCache.ps1 PowerShell script file and changed Copy to Output Directory property to Copy Always.

The Add-AssemblyToGlobalAssemblyCache.ps1 file can find from the link here.

Step 2: Added Startup script (StartupTask.cmd) and changed Copy to Output Directory property to Copy Always.

Step 3: Added below script in StartupTask.cmd file.
if "%EMULATED%"=="true" goto EOF

powershell -command "Set-ExecutionPolicy Unrestricted" 2>> error.out
powershell .\Add-AssemblyToGlobalAssemblyCache.ps1 -AssemblyName E:\sitesroot\0\bin\MyGACdll.dll 2>> error.out

exit /b 0
As mentioned in the script, first set the execution policy and call the Add-AssemblyToGlobalAssemblyCache.ps1 with the assembly name to install the assembly to GAC.

Step 4: Save the StartupTask.cmd file to Unicode by selecting File -> Advanced Save Options…


Step 5: Added StartupTask.cmd file as startup script into the service definition file.


Step 6: Now deployed the cloud app to Azure, and verified the url again. Now the MyGACdll.dll will be listed in the page.


So, now the MyGACdll.dll assembly is deployed to GAC.

Tuesday 2 April 2013

Exporting to Excel from GridView when grid changed by code behind

This is a small post, which explains how to export to excel from GridView where the GridView has been changed from the code behind by adding/deleting some rows and updated some data. When GridView changed from code behind, the export might not include the updated version done from the code behind.

Understanding a sample scenario:

Before going for implementation, let us understand the scenario where this implementation is applicable. I have a GridView, which binds some sample data from an XML file. The GridView on screen will be as below –

Now I want to change same GridView by adding group total and grand total as per the post Group Total and Grand Total in GridView - Part 2. Now my GridView shows as below –

Here, the group total, grand total rows are added from code behind by doing some logic. I want to export the same GridView to Excel. I followed normal way of export to excel as per the post Exporting to Excel from GridView (All columns and rows - Normal Method). But I am not getting all the rows exported to the Excel, and the total values are not getting exported. The excel shows as below –

I am not sure why this happened. But seems it exported only the number of rows as per the data it binds. Also RowDataBound event also not fired while exporting, so excel not updated with the total value.

Solution –

The implementation is very simple. The actual requirement while exporting to excel is, we required the html script which needs to be exported to excel. In a normal way, we render the GridView at the time of export (again) and get the html script. The html script is then used for exporting to excel. The code is below –
PrepareGridViewForExport(grdViewOrders);

Context.Response.ClearContent();
Context.Response.ContentType = "application/ms-excel";
Context.Response.AddHeader("content-disposition", string.Format("attachment;filename={0}.xls", "ExcelFileName"));
Context.Response.Charset = "";
System.IO.StringWriter stringwriter = new System.IO.StringWriter();
HtmlTextWriter htmlwriter = new HtmlTextWriter(stringwriter);
grdViewOrders.RenderControl(htmlwriter);
Context.Response.Write(stringwriter.ToString());
Context.Response.End();
But instead of doing this, we can use the actual html script which already been rendered on the screen. So to export the actual html on screen to excel, I added a div as parent to GridView. I also added a hidden control for storing the html script before going to code behind. In the export button, I called a javascript function which gets the html script of GridView and assign to the hidden control (before going to code behind).

function AssignExportHTML() {
    document.getElementById("<%= hidGridView.ClientID %>").value = htmlEscape(forExport.innerHTML);
}
function htmlEscape(str) {
    return String(str)
    .replace(/&/g, '&amp;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;');
}
In the code behind, I used the hidden control value for exporting to the grid.
Context.Response.ClearContent();
Context.Response.ContentType = "application/ms-excel";
Context.Response.AddHeader("content-disposition", string.Format("attachment;filename={0}.xls", "ExcelFileName"));
Context.Response.Charset = "";
System.IO.StringWriter stringwriter = new System.IO.StringWriter();
//HtmlTextWriter htmlwriter = new HtmlTextWriter(stringwriter);
//grdViewOrders.RenderControl(htmlwriter);
stringwriter.Write(System.Web.HttpUtility.HtmlDecode(hidGridView.Value));
Context.Response.Write(stringwriter.ToString());
Context.Response.End();
Now my export will be as below –

To get the same style and colors used in the GridView, we can add the css style sheet inside the div control. So while exporting, the style sheets also goes to the excel sheet.

Now the excel looks as below –

Note: This implementation can be used for exporting any kind grid, tables etc to excel as shown in the screen.

Sunday 3 February 2013

Group Total and Grand Total in GridView – Part 11

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 last post, I am posted how to extend the same examples additionally one functionality - expanding and collapsing the groups with corresponding images. In this post, I am planning to post Expand All / Collapse All functionality.
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.
  5. The groups must be expended all / collapsed all by having +/- image in the header at the Year column.
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.
  7. The groups must also have Expand All/ Collapse All functionalities with +/- images at the header of first three columns (Sector, Company Name, Income/Expense)

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>();

    TableCell cell = grdViewProducts.HeaderRow.Cells[0]; // First Cell in the Header - Grand Total

    System.Web.UI.HtmlControls.HtmlGenericControl title = new System.Web.UI.HtmlControls.HtmlGenericControl();
    title.InnerText = "Year  ";
    cell.Controls.Add(title);

    System.Web.UI.HtmlControls.HtmlImage img = new System.Web.UI.HtmlControls.HtmlImage();
    img.Src = "images/minus.gif";
    img.Attributes.Add("class", "ExpandCollapseHeaderStyle");
    img.Attributes.Add("alt", "0");
    cell.Controls.Add(img);

}

/// <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.gif";
        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;
    width:300px;
}
.ExpandCollapseStyle {
    border:0px;
    cursor:pointer;
    padding-left:3px;
    padding-right:5px;
    width:12px;
    height:12px;
}
.ExpandCollapseHeaderStyle {
    background-color:#6B696B;
    cursor:pointer;
}
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.gif');
        }
        else {
            $('.Row' + selectedTrackId).css("display", "block"); // Expand the rows
            $(this).attr('alt', selectedTrackId.split(",")[0] + ",Expanded");
            $(this).attr('src', 'images/minus.gif');
        }
    });
    $('.ExpandCollapseHeaderStyle').click(function () {
        var ExpandOrCollapse = $(this).attr('src');

        $($(".grdViewOrders tr").get()).each(function () {
            var currentTrackId = $(this).find(".ExpandCollapseStyle").attr('alt');
            if (currentTrackId != null) {
                if (ExpandOrCollapse == 'images/minus.gif') {
                    $(this).find(".ExpandCollapseStyle").attr('src', 'images/plus.gif');
                    $(this).find(".ExpandCollapseStyle").attr('alt', currentTrackId.split(",")[0] + ",Collapsed");
                    $('.Row' + currentTrackId.split(",")[0]).css('display', 'none');
                }
                else {
                    $(this).find(".ExpandCollapseStyle").attr('src', 'images/minus.gif');
                    $(this).find(".ExpandCollapseStyle").attr('alt', currentTrackId.split(",")[0] + ",Expanded");
                    $('.Row' + currentTrackId.split(",")[0]).css('display', 'block');
                }
            }
        })
        if (ExpandOrCollapse == 'images/minus.gif')
            $(this).attr('src', 'images/plus.gif');
        else
            $(this).attr('src', 'images/minus.gif');
    });
});
Here is the output of this example


The Second Example implementation goes as below –
In this example, I had provided three different implementation which are little different from others.
  1. The GridView will show with group and their data rows initially. When a parent group is collapsed and expanding again - the child groups and their data rows will be expanded at one shot. It means - when expanding a parent groups, all child groups and data rows will be expanded immediately
  2. In the next example - when a parent group expanding, the immediate child group only will be expanded and other groups will be collapsed/hidden status. When last group expanded, the data row will show.
  3. In third example - Initially when screen shows, it shows only the groups and not data rows. So if lots of records are available, the screen will show only small number of records. Other functionalities are as per previous example.
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="grdViewProducts"
    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();

    TableCell cell = grdViewProducts.HeaderRow.Cells[0];

    System.Web.UI.HtmlControls.HtmlGenericControl title = new System.Web.UI.HtmlControls.HtmlGenericControl();
    title.InnerText = "Sector  ";
    cell.Controls.Add(title);
    System.Web.UI.HtmlControls.HtmlImage 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.Attributes.Add("alt", "HeaderCell" + ",1");

    cell = grdViewProducts.HeaderRow.Cells[1];
    title = new System.Web.UI.HtmlControls.HtmlGenericControl();
    title.InnerText = "Name  ";
    cell.Controls.Add(title);
    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.Attributes.Add("alt", "HeaderCell" + ",2");

    cell = grdViewProducts.HeaderRow.Cells[2];
    title = new System.Web.UI.HtmlControls.HtmlGenericControl();
    title.InnerText = "Income / Expense  ";
    cell.Controls.Add(title);
    img = new System.Web.UI.HtmlControls.HtmlImage();
    img.Src = "images/minus.gif";
    img.Attributes.Add("class", "ExpandCollapseHeaderStyle");
    img.Attributes.Add("alt", "3");
    cell.Controls.Add(img);
    cell.Attributes.Add("alt", "HeaderCell" + ",3");
}

/// <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);
        SubTotalRow.CssClass = "ExpandCollapse" + DataBinder.Eval(e.Row.DataItem, "Sector").ToString().Replace(" ", "");

        TableCell cell = new TableCell();

        System.Web.UI.HtmlControls.HtmlImage img = new System.Web.UI.HtmlControls.HtmlImage();
        img.Src = "images/minus.gif";
        img.Attributes.Add("alt", DataBinder.Eval(e.Row.DataItem, "Sector").ToString().Replace(" ", "") + ",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);
        SubTotalRow.CssClass = "ExpandCollapse" + DataBinder.Eval(e.Row.DataItem, "Sector").ToString().Replace(" ", "") + "_" + DataBinder.Eval(e.Row.DataItem, "Name").ToString().Replace(" ", "");
        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.gif";
        img.Attributes.Add("alt", DataBinder.Eval(e.Row.DataItem, "Sector").ToString().Replace(" ", "") + "_" + DataBinder.Eval(e.Row.DataItem, "Name").ToString().Replace(" ", "") + ",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);
        SubTotalRow.CssClass = "ExpandCollapse" + DataBinder.Eval(e.Row.DataItem, "Sector").ToString().Replace(" ", "") + "_" + DataBinder.Eval(e.Row.DataItem, "Name").ToString().Replace(" ", "") + "_" + DataBinder.Eval(e.Row.DataItem, "AccountSection").ToString().Replace(" ", "");
        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.gif";
        img.Attributes.Add("alt", DataBinder.Eval(e.Row.DataItem, "Sector").ToString().Replace(" ", "") + "_" + DataBinder.Eval(e.Row.DataItem, "Name").ToString().Replace(" ", "") + "_" + DataBinder.Eval(e.Row.DataItem, "AccountSection").ToString().Replace(" ", "") + ",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.Style.Add("display", "block");
        e.Row.Cells[0].CssClass = "DataRowStyle";
        e.Row.Cells[0].Attributes.Add("alt", ",4");
        //e.Row.CssClass = "Row" + strPreviousSectorRowID.Replace(" ", "") + "_" + strPreviousNameRowID.Replace(" ", "") + "_" + strPreviousAccountSectionRowID.Replace(" ", "");

    }
}

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;
}
.ExpandCollapseHeaderStyle {
    background-color:#6B696B;
    cursor:pointer;
}
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');

        $($(".grdViewProducts 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.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');
                        }
                    }
                }
            }
        });
    });

    $('.ExpandCollapseHeaderStyle').click(function () {
        var ExpandOrCollapse = $(this).attr('src');

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

        $($(".grdViewProducts tr").get()).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");
                    }
                }
            }

        });

        $(".grdViewProducts 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');
                }
            }
        });

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

    });
});
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');

        $($(".grdViewProducts 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.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).find(".ExpandCollapseStyle").attr('src', 'images/plus.gif');

                            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.gif');
                        }
                    }
                }
            }
        });
    });

    $('.ExpandCollapseHeaderStyle').click(function () {
        var ExpandOrCollapse = $(this).attr('src');

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

        $($(".grdViewProducts tr").get()).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");
                    }
                }
            }

        });

        $(".grdViewProducts 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');
                }
            }
        });

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

    });
});
The Javascript (Collapse all data row while showing at the first time)
$(document).ready(function () {
    $('.ExpandCollapseStyle').click(function () {
        var selectedTrackId = $(this).attr('alt');

        var isSelectedTrackerFound = false;
        var selectedTrackerGroupIndex = 0;

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

        $($(".grdViewProducts 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.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).find(".ExpandCollapseStyle").attr('src', 'images/plus.gif');

                            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.gif');
                        }
                    }
                }
            }
        });
    });

    $('.ExpandCollapseHeaderStyle').click(function () {
        var ExpandOrCollapse = $(this).attr('src');

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

        $($(".grdViewProducts tr").get()).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");
                    }
                }
            }

        });

        $(".grdViewProducts 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');
                }
            }
        });

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

    });
});
Here is the output of this example




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