Introduction
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 GridView
s and two ObjectDataSource
s have been used in this project. The IDs of the GridView
s 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 ObjectDataSource
s that are used to pull the data from the business logic layer:
<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.
<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.
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.
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.