Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Hierarchical GridView Control Using AJAX

2.69/5 (14 votes)
4 Mar 2010CPOL3 min read 68.4K   3.5K  
Explains how to create a hierarchical GridView control in ASP.NET using AJAX

Introduction

HierachicalGridViewImage_small.gif

This article explains about creating a hierarchical grid view control. I implemented this grid two years ago in a project. I thought of publishing it at that time itself, but couldn't as I didn't have enough time. I am publishing it now, amidst tight timelines. You can use this grid to display the data in a parent-child model. In this example, I have used only one child grid. Using this code, you could convert a child grid to a parent by creating more child grids under it.

Background

Two GridViews and two ObjectDataSources have been used in this project. The IDs of the GridViews are grdOrders and grdOrderDetails. SQL Express has been used as a back-end server and NorthWind is the database in this module. In this document, grdOrders is referred as the parent grid, and grdOrderDetails is referred as the child grid which is placed inside grdOrders. By default, the Visible property of the child grid would be false. The parent grid would display the Orders. When the user clicks on a particular row on the parent grid, the child grid would be visible with the Order Details data.

Using the Code

Here is the declaration of the ObjectDataSources that are used to pull the data from the business logic layer:

ASP.NET
<asp:ObjectDataSource ID="objDataSourceID" 
                        runat="server" 
                        SelectMethod="GetOrders" 
                        EnablePaging="true" 
                        TypeName="clsGeneralFunctions" 
                        StartRowIndexParameterName="StartRowIndex" 
                        MaximumRowsParameterName="MaximumRows" 
                        SelectCountMethod="GetOrdersCount">
</asp:ObjectDataSource>

The above ObjectDataSource is used to pull the data from the business logic layer for the parent DataGrid. The StartRowIndexParameterName and MaximumRowsParameterName properties are used to pull only the records that are needed to be displayed on the current page. The SelectCountMethod property is used to pull the total number of records that the actual query is going to return, which is used to display the total number of pages on the grid depending on the PageSize property. When the user selects a particular page number on the grid, the ObjectDataSource would execute the GetOrders method which is assigned to the SelectMethod property. If the EnablePaging property set to False, then there is no use of the StartRowIndexParameterName and MaximumRowsParameterName properties, i.e., we cannot pull just the records that are needed to be displayed on the current page.

XML
<asp:ObjectDataSource   ID="objOrderDetails" 
                           runat="server" 
                           SelectMethod="GetOrdersDetailsList" 
                           EnablePaging="true" 
                           TypeName="clsGeneralFunctions" 
                           SelectCountMethod="GetOrdersDetailsCount">
   <SelectParameters>
   <asp:Parameter Direction="Input" 
        Name="StartRowIndex" type="Int32" />
   <asp:Parameter Direction="Input" 
        Name="MaximumRows" type="Int32" />
   <asp:Parameter Direction="Input" 
        Name="OrderId" 
        DefaultValue="0" type="Int32" />
   </SelectParameters>
</asp:ObjectDataSource>

The above ObjectDataSource is used to pull data from the business logic layer for the child DataGrid based on the Order ID, which will come from the parent grid. When the user clicks on a particular order on the parent grid, the child grid would display the order details for the selected order. The above ObjectDataSource has an extra input parameter called OrderId which is used to pass the Order ID to the methods that are assigned to the SelectCountMethod and SelectMethod properties.

C#
protected void grdOrders_RowDataBound(object sender, GridViewRowEventArgs e)
{{
    ImageButton imgBtn;

    if (e.Row.RowType == DataControlRowType.DataRow)
    {

        imgBtn = (ImageButton)(e.Row.FindControl("ImgBtn"));
        imgBtn.CommandArgument = e.Row.RowIndex.ToString();

        if (e.Row.Cells[0].Text == Session["OrderId"])
        {
            PlaceHolder objPH;
            objPH = (PlaceHolder)(e.Row.FindControl("objPHOrderDetails"));
            if (objPH != null)
                objPH.Visible = true;

            if (imgBtn.ImageUrl == "Include/images/gridplus.gif")
                imgBtn.ImageUrl = @"Include/images/gridminus.gif";
            else
                imgBtn.ImageUrl = @"Include/images/gridplus.gif";
        }
    }
}

When the rows are created on the GridView, the above RowDataBound event gets fired. The above event is used to set the GridView row number to the CommandArgument property of the ImageButton. This property would be used in the RowCommand event of parent grid which gets fired when the user clicks on the ImageButton on the parent grid.

C#
protected void grdOrders_RowCommand(object sender, GridViewCommandEventArgs e)
{
    if (e.CommandName == "Expand")
    {
        ImageButton imgbtn;
        GridView gv = (GridView)(sender);
        Int32 rowIndex = Convert.ToInt32(e.CommandArgument.ToString());
        PlaceHolder objPH = 
          (PlaceHolder)(gv.Rows[rowIndex].FindControl("objPHOrderDetails"));
        ObjectDataSource objDS = 
          (ObjectDataSource)(gv.Rows[rowIndex].FindControl("objOrderDetails"));
        GridView objChildGrid = 
          (GridView)(gv.Rows[rowIndex].FindControl("grdOrderDetails"));
        imgbtn = (ImageButton)(gv.Rows[rowIndex].FindControl("ImgBtn"));

        objDS.SelectParameters["OrderId"].DefaultValue = 
                    gv.DataKeys[rowIndex][0].ToString();
        Session["OrderId"] = gv.DataKeys[rowIndex][0].ToString();

        if (imgbtn.ImageUrl == "Include/images/gridplus.gif")
        {
            imgbtn.ImageUrl = @"Include/images/gridminus.gif";
            if (objChildGrid != null)
            {
                if (objPH != null)
                    objPH.Visible = true;
                objChildGrid.Visible = true;
            }
        }
        else
        {
            if (objChildGrid != null)
            {
                imgbtn.ImageUrl = @"Include/images/gridplus.gif";
                if (objPH != null)
                    objPH.Visible = false;
                objChildGrid.Visible = false;
            }
        }
    }
}

The if condition (e.CommandName == "Expand") is used to verify whether the user clicked on the ImageButton or not. The value "Expand" has been set to the CommandName property of the ImageButton. When the user clicks on the ImageButton, we create objects for all the controls that are inside the parent grid control, passing the selected row, i.e., the Order ID, to the child data source which would pull all the order details records for the selected order. Once the data gets populated in the child data grid, we should change the image button plus(+) symbol to minus(-) and should set the Visible property of the child grid and the PlaceHolder to true. Next time, when the user clicks on the same row, the minus symbol should be changed to a plus symbol and set the Visible property of the child grid and PlaceHolder to false, which the else statement does.

Points of Interest

We learnt about a lot of new controls during this project as we were new to ASP.NET.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)