Tuesday, 26 February 2008

How to collapse and expand rows of a datagrid using C#, ASP.NET, and Javascript. (Version 2)

Article Author: Rajib A.

What is HGrid?

HGrid stands for Hierarchy Grid.  This grid is meant to hold data using a datagrid/table, and present it to users in a fashionable manner. 

 

What is this article about?

The following article will describe the source code, which will allow programmers to create a HGrid.  It will allow master/slave or master/child data to be shown.  It also includes the features to collapse and expand data on the client side using JavaScript.  The original code was done in Visual Studio 2003 using framework 1.1.  This is the second version of this grid. 

 

Why the second version?

The original had a flaw where I had to bind the data both on the "!Page.IsPostBack", and "Page.IsPostBack" of the page load to ensure that the state of expansion of the rows of the grid were maintained.  I did not like the fact that I had to connect to the database again for a query.  It did not seem efficient.  I also wanted to add basic sorting features, and give more detail to beginner programmers on using the code.  I also had to modify the JavaScript methods to handle a few bugs I had missed out on.

 

What are the controls needed?

In order to create a HGrid on your own, only a datagrid, and a hidden textbox are needed.  In the example I have included the following are the components:

1.     DataGrid          (ID: DataGrid1)

The datagrid will hold the data for the master/slave or master/child data.  We will also include the feature to expand/collapse (show/hide) on the grid itself.  In the example I use, I will be connecting to the NorthWind database of a SQL Database.

2.      TextBox           (ID: txtExpandedDivs, Width = "0px" or in a div which has style.display: 'none') 

The width is set to 0px so that the users cannot see it, but it exists on the page as a hidden text field.  I chose to do it this way, so users can change the width and view the contents of the textbox

3.   Button              (ID: ButtonSample)

This button is outside of the grid.  I created this button, but most likely when users create a page, the page will consist of many controls which will call the page to reload.  We have to make sure the contents of the grid are the same, once the page is refreshed.

4.   Labels              (ID: LabelPostBack)

This label will show the contents of the hidden textbox (txtExpandedDivs) once the (ButtonSample) is clicked.

5.   Labels              (ID: LabelTitle)

This label is just show the database we are connecting to.  e.g.. NorthWind

6.   Labels              (ID: LabelWhatHappens)

This label is a static label which always shows the message:  "What are we storing in the hidden textbox field (txtExpandedDivs Textbox Control)?"

7.   Table                (ID: Table1)

This table has all the above controls in it.

Details of the DataGrid

Here is the html of the datagrid below.  It has paging (10 per page), sorting, and autogeneratedcolumns = false.  The columns we have are:

  1. HyperLinkColumn: "+"
  2. BoundColumn: "OrderID", "CustomerID", "EmployeeID", "ShipName", "ShipAddress", "ShipCity", "ShipRegion", "ShipPostCode", "ShipCountry".


<asp:datagrid id="DataGrid1" runat="server" ForeColor="Black" AllowSorting="True" AllowPaging="True" GridLines="Vertical" CellPadding="3" BackColor="White" BorderWidth="1px" BorderStyle="Solid" BorderColor="#999999"  Width="100%" AutoGenerateColumns="
False">

<SelectedItemStyle Font-Bold="True" HorizontalAlign="Left" ForeColor="White"

                VerticalAlign="Top" BackColor="#000099">

</SelectedItemStyle>

                <EditItemStyle HorizontalAlign="Left" VerticalAlign="Top"></EditItemStyle>

                <AlternatingItemStyle HorizontalAlign="Left" ackColor="#CCCCCC"></AlternatingItemStyle>

                <ItemStyle Font-Size="8pt" Font-Names="Tahoma" HorizontalAlign="Left"

 VerticalAlign="Top">

</ItemStyle>

                <HeaderStyle Font-Bold="True" ForeColor="White" BackColor="Black"></HeaderStyle>

                <FooterStyle BackColor="#CCCCCC"></FooterStyle>                                                                               

<Columns>

                                <asp:HyperLinkColumn Text="+"></asp:HyperLinkColumn>

<asp:BoundColumn Visible="False" DataField="OrderID" ReadOnly="True"

                HeaderText="OrderID">

</asp:BoundColumn>

                                <asp:BoundColumn Visible="False" DataField="CustomerID"                                 

                                                HeaderText="CustomerID">

</asp:BoundColumn>

                                <asp:BoundColumn Visible="False" DataField="EmployeeID"

HeaderText="EmployeeID">

</asp:BoundColumn>

                                <asp:BoundColumn DataField="ShipName" SortExpression="ShipName"

HeaderText="Name">

</asp:BoundColumn>

                                <asp:BoundColumn DataField="ShipAddress" SortExpression="ShipAddress"

                                                HeaderText="Address">

</asp:BoundColumn>

                                <asp:BoundColumn DataField="ShipCity" SortExpression="ShipCity"                   

HeaderText="City">

</asp:BoundColumn>

                                <asp:BoundColumn DataField="ShipRegion" SortExpression="ShipRegion"

                                                HeaderText="Region">

</asp:BoundColumn>

                                <asp:BoundColumn DataField="ShipPostalCode" SortExpression="ShipPostalCode"

HeaderText="Postal">

</asp:BoundColumn>

                                <asp:BoundColumn DataField="ShipCountry" SortExpression="ShipCountry"

                                                HeaderText="Country">

</asp:BoundColumn>

                </Columns>

                <PagerStyle HorizontalAlign="Center" ForeColor="Black" BackColor="#999999"

Mode="NumericPages">

</PagerStyle>

</asp:datagrid>


Screen Shots

Loading Data:

Expanding Rows:

Expanding More Rows:

Clicking on a button outside of grid on page:


Expanding Rows in a different page, and then sorting:

Clicking a button outside of datagrid where your on a a different page where sorting occurred.


The Source Code

Language used for this code is: ASP.NET, C#.  There are many tools to convert to VB.NET if needed.  The source code is very descriptive and can easily be traced.

 

Code:

 

private string connectionstring = "server=SERVERNAME;database=Northwind;uid=USERID;password=PASSWORD;";

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

      //Clear the contents of the Label
      this.LabelPostBack.Text = "";

      if ( !Page.IsPostBack )
      {

            BindData();  //Bind Master Details
      }
      else
      {

            for ( int i = 0; i < this.DataGrid1.Items.Count; i++ )
            {
                  //After Postback ID's get lost.  Javascript will not //work without it, so we must set them back.
                  
this.DataGrid1.Items[i].Cells[0].ID = "CellInfo" + i.ToString();
            
}

            //If it is a postback that is not from the grid, we have to 
            //expand the rows the user had expanded before.  We have to 
            
//check first who called this postback by checking the 
            
//Event Target.  The reason we check this, is because we 
            
//don't need to expand if it is changing the page of the 
            
//datagrid, or sorting, etc...

            if ( Request["__EVENTTARGET"] != null)
            
{
                  
string strEventTarget = Request["__EVENTTARGET"].ToString().ToLower();

                  //datagrid1 is the name of the grid.  If you modify 
                  //the grid name, make sure to modify this if 
                  //statement.

                  if ( strEventTarget.IndexOf("datagrid1") == -1)
                  
{

                     if (!Page.IsStartupScriptRegistered("ShowDataJS"))
                     
{
               
         Page.RegisterStartupScript( "ShowDataJS", "<script>ShowExpandedDivInfo('" + this.txtExpandedDivs.ClientID + "','" +
                                  this.DataGrid1.ClientID + "');</script>");
                     
}
                  
}
            }
      }
} 

private void ButtonSample_Click(object sender, System.EventArgs e)
{

      LabelPostBack.ForeColor = System.Drawing.Color.DarkRed;
      if ( txtExpandedDivs.Text.Length == 0 )
      {
         
LabelPostBack.Text = "A Postback has occurred. txtExpandedDivs has no content!";
      
}
      
else
      
{
   
      LabelPostBack.Text = "A Postback has occurred. txtExpandedDivs has contents: <BR/><B>" + 
            
this.txtExpandedDivs.Text + "</B>";
      
}
}
#region Database Methods

private void BindData()
{

      //======Query For Master Rows=======
      string QueryString = "SELECT OrderID, CustomerID, EmployeeID, 
                           
ShipName, ShipAddress, ShipCity, ShipRegion, 
                           
ShipPostalCode, ShipCountry FROM Orders";

      if ( ViewState["sortby"] != null )
      {
            QueryString = QueryString + " order by " + ViewState["sortby"].ToString();
      
}

      //==============================

      System.Data.SqlClient.SqlConnection conn = new 
            System.Data.SqlClient.SqlConnection();

try
      {
            conn.ConnectionString = connectionstring;
            if ( conn.State == System.Data.ConnectionState.Closed )
            {
                  conn.Open();
            }                       

            System.Data.SqlClient.SqlDataAdapter adapter = new 
                  
System.Data.SqlClient.SqlDataAdapter( QueryString, conn);

            DataSet ds = new DataSet();
            adapter.Fill( ds );
            DataGrid1.DataSource = ds;
            DataGrid1.DataBind();
      }
      catch( Exception ex1 )
      {
            Response.Write( "An error has occurred: " );
            Response.Write( ex1.Message.ToString() );
            Response.End();
      }
      finally
      {
            if ( conn.State == System.Data.ConnectionState.Open )
            {
                  conn.Close();
            }
      } 
} 

private DataSet RunQuery(string QueryString)
{
      System.Data.SqlClient.SqlConnection conn = new 
   
                  System.Data.SqlClient.SqlConnection();

      try
      {
            conn.ConnectionString = connectionstring;
            if ( conn.State == System.Data.ConnectionState.Closed )
            {
                  conn.Open();
            }                       

            System.Data.SqlClient.SqlDataAdapter adapter = new 
              System.Data.SqlClient.SqlDataAdapter( QueryString, conn);           

            DataSet ds = new DataSet();
            adapter.Fill( ds );
            return ds;
      }
      catch(Exception ex1)
      {
            Response.Write("An Error has occurred.<BR />");
            Response.Write(ex1.Message.ToString());
            Response.End();

            //This line below will never execute.
            return null;
      }
      finally
      {
            if ( conn.State == System.Data.ConnectionState.Open )
            {
                  conn.Close();
            }
      }
} 

No comments: