Friday, 25 January 2013

Hierarchical GridView in ASP.NET with AJAX, JQuery implementation - Part 1

Previously I had blogged some post related to showing two grids in hierarchical way. For example - when I need to show an invoice reports on the screen, the user requesting to show the list of invoices at initial time and they can drill down the details of item details of each invoice by clicking on respective row.

The implementations can be in multiple ways, but I had blogged the implementation with AJAX and without AJAX using Java script on ASP.NET page. Below are the URLs of the same –

Hierarchical GridView in ASP.NET
Hierarchical GridView in ASP.NET with AJAX, Javascript implementation

In this post I am taking the same requirements done before and enhance with some additional functionality as per readers expectations.

The requirement on this implementation will be –
  1. The page should show a Grid View with list of Orders with Expand icon on the first column.
  2. On click of expand icon on each row, the Order Details of the Order must be fetched from the database and show in the Grid View just below to the row with little adjacent to the first column.
  3. The child Grid View must fetch from the server only on demand to avoid the loading time using AJAX concept.
  4. Once the expand icon clicked, there should be a loading message to inform to the user the details are fetching from the server.
  5. Once the Grid View fetched from the server, the Child Grid should show by hiding the loading message and the expand icon must be changed to collapse icon.
  6. On click of collapse icon, the child grid should hide from the screen.
  7. When the user clicks the Expand icon which was already done before, there should not be a call to the server again for fetching the same details. The system should show the hidden Grid View done in Step 6.
  8. When user moving one page to another page by expanding and collapsing some records and come back to the old pages, the expanded records must be expanded and others are collapsed (means old state must be maintained).
As stated, all requirements are same as in previous posts except the eighth point which is to maintain the state of the previous pages.

The implementation done in JQuery on ASP.NET page with AJAX concept.

Note: The implementation done here using AJAX concept, so it won’t provide the ability to update, delete records on child grid level. The next post which is continuation of this implementation explains the updation, deletion of record on child grid records.

The implementation follows –

I have two pages Default.aspx and ChildGridBuilder.aspx.
  1. The Default.aspx page shows the list of Orders on the page. The end user accessing this page only from the browser.
  2. When the user clicking the Expand icon on any of the order row, it will call the ChildGridBuilder.aspx page with Order Id in the query string as parameter using AJAX.
  3. The ChildGridBuilder.aspx page will bind the required records to the GridView and return the HTML script to the Default.aspx page as response to the AJAX call.
  4. The Default.aspx page will show the returned script just below to the Order row expanded inside a DIV control.
I am using JQuery script file version 1.7.2 (jquery-1.7.2.min.js) for this implementation.
The ASPX Script (Default.aspx)
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<asp:GridView id="GridViewHierachical" runat="server" AutoGenerateColumns="False"
    Width="100%" DataKeyNames="OrderId" AllowPaging="true" CssClass="GridViewStyle"
    BorderStyle="Solid" PageSize="10" CellPadding="0" CellSpacing="0"
    OnPageIndexChanging="GridViewHierachical_PageIndexChanging" EnableViewState="false">
    <Columns>
        <asp:TemplateField HeaderText="Order ID">
            <ItemStyle Width="10%" />
            <ItemTemplate>
                <input id='hid<%# Eval("OrderID") %>') value='0' type="hidden" />
                <img id="img<%# Eval("OrderID") %>" alt="<%# Eval("OrderID") %>" class="ExpandImage" width="9px"
                    border="0" src="Images/plus.png" style="cursor:pointer;padding-left:3px;width:12px;height:12px;" />
                <asp:Label ID="lblOrderID" Text='<%# Eval("OrderID") %>' Visible="true" runat="server"></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField DataField="CustomerID" HeaderText="Customer ID">
            <ItemStyle Width="10%" />
        </asp:BoundField>
        <asp:BoundField DataField="ShippedDate" HeaderText="Shipped Date"  DataFormatString="{0:MMMM d, yyyy}">
            <ItemStyle Width="20%" />
        </asp:BoundField>
        <asp:BoundField DataField="ShipName" HeaderText="Ship Name">
            <ItemStyle Width="20%" />
        </asp:BoundField>
        <asp:BoundField DataField="ShipCountry" HeaderText="Ship Country">
            <ItemStyle Width="15%" />
        </asp:BoundField>
        <asp:BoundField DataField="ShipCity" HeaderText="Ship City">
            <ItemStyle Width="15%" />
        </asp:BoundField>
        <asp:TemplateField>
            <HeaderStyle CssClass="InvisibleCol" />
            <ItemStyle CssClass="InvisibleCol" />
            <ItemTemplate>
            </td></tr>
                <tr class="ChildRow">
                    <td id="td<%# Eval("OrderID") %>" colspan="6">
                        <div id="div<%# Eval("OrderID") %>" style="width:100%;display:none;">
                            <div style='padding:4px;'>
                                <img src='Images/ajax-loader.gif' alt='' />
                                <span style='color:blue;font-size:17px;font-weight:bold;'>Loading... Please wait</span>
                            </div>
                        </div>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
    <FooterStyle BackColor="#CCCCCC" ForeColor="Black"></FooterStyle>
    <RowStyle CssClass="GridViewRow" Height="24px"></RowStyle>
    <HeaderStyle CssClass="GridViewHeader" Height="22px"></HeaderStyle>
    <SelectedRowStyle BackColor="#008A8C" Font-Bold="True" ForeColor="White"></SelectedRowStyle>
    <PagerStyle BackColor="#999999" ForeColor="Black" HorizontalAlign="Right"></PagerStyle>
    <AlternatingRowStyle CssClass="GridViewAlternativeRow"></AlternatingRowStyle>
</asp:GridView>
<asp:HiddenField ID="hndExpandedChild" runat="server" Value="" />
<asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />
The Code behind Script (Default.aspx.cs)
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        BindGrid();
    }
}
private void BindGrid()
{
    using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["SQLConnection"].ConnectionString))
    {
        string strSQL = "SELECT Orders.OrderID, Orders.CustomerID, Orders.ShippedDate, Orders.ShipName, Orders.ShipCountry, Orders.ShipCity FROM Orders";
        SqlCommand command = new SqlCommand(strSQL, connection);
        command.CommandType = CommandType.Text;
 
        connection.Open();
        SqlDataReader dr = command.ExecuteReader(CommandBehavior.CloseConnection);
 
        IList<Order> orderList = new List<Order>();
        while (dr.Read())
        {
            Order order = new Order();
            order.OrderID = Convert.ToInt32(dr["OrderID"].ToString());
            order.CustomerID = dr["CustomerID"].ToString();
            order.ShippedDate = dr["ShippedDate"].ToString();
            order.ShipName = dr["ShipName"].ToString();
            order.ShipCountry = dr["ShipCountry"].ToString();
            order.ShipCity = dr["ShipCity"].ToString();
 
            orderList.Add(order);
        }
        GridViewHierachical.DataSource = orderList;
        GridViewHierachical.DataBind();
    }
}
 
protected void GridViewHierachical_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
    GridViewHierachical.PageIndex = e.NewPageIndex;
    BindGrid();
}
 
protected void Button1_Click(object sender, EventArgs e)
{
 
}
?
1
2
3
4
5
6
7
8
9
public class Order
{
    public int OrderID { get; set; }
    public string CustomerID { get; set; }
    public string ShippedDate { get; set; }
    public string ShipName { get; set; }
    public string ShipCountry { get; set; }
    public string ShipCity { get; set; }
}
Included the Jquery file in the script folder and add the script in the Default.aspx file.
?
1
<script src="Scripts/jquery-1.7.2.min.js" type="text/javascript"></script>
The ASPX Script (ChildGridBuilder.aspx)
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<asp:GridView id="GridViewDetails" runat="server"
    AllowSorting="false" AutoGenerateColumns="False" Width="100%"
    CellSpacing="1" CellPadding="0" GridLines="Both" DataKeyNames="OrderId"
    BackColor="White" BorderWidth="2px" BorderStyle="Ridge"
    BorderColor="White" AllowPaging="false" ForeColor="#000066">
    <Columns>
        <asp:BoundField DataField="OrderId" HeaderText="Order ID">
            <ItemStyle Width="10%" />
        </asp:BoundField>
        <asp:BoundField DataField="ProductName" HeaderText="Product Name">
            <ItemStyle Width="30%" />
        </asp:BoundField>
        <asp:BoundField DataField="UnitPrice" HeaderText="Unit Price" DataFormatString="{0:N}" ItemStyle-HorizontalAlign="Right">
            <ItemStyle Width="15%" />
        </asp:BoundField>
        <asp:BoundField DataField="Quantity" HeaderText="Quantity" DataFormatString="{0:N}" ItemStyle-HorizontalAlign="Right">
            <ItemStyle Width="15%" />
        </asp:BoundField>
        <asp:BoundField DataField="Discount" HeaderText="Discount" DataFormatString="{0:N}" ItemStyle-HorizontalAlign="Right">
            <ItemStyle Width="15%" />
        </asp:BoundField>
        <asp:BoundField DataField="Amount" HeaderText="Amount" DataFormatString="{0:N}" ItemStyle-HorizontalAlign="Right">
            <ItemStyle Width="15%" />
        </asp:BoundField>
    </Columns>
    <RowStyle ForeColor="#000066" Height="20px"></RowStyle>
    <SelectedRowStyle BackColor="#669999" Font-Bold="True" ForeColor="White"></SelectedRowStyle>
    <PagerStyle BackColor="White" ForeColor="#000066" HorizontalAlign="Left"></PagerStyle>
    <HeaderStyle CssClass="GridViewHeader"></HeaderStyle>
    <AlternatingRowStyle BorderStyle="Solid" BorderWidth="0px"></AlternatingRowStyle>
</asp:GridView>
The Code Behind Code (ChildGridBuilder.aspx.cs)
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
protected void Page_Load(object sender, EventArgs e)
{
    Response.Clear();
    Response.ContentType = "text/xml";
    BindGrid();
 
    System.IO.StringWriter stringWrite = new System.IO.StringWriter();
    System.Web.UI.HtmlTextWriter htmlWrite = new HtmlTextWriter(stringWrite);
    GridViewDetails.RenderControl(htmlWrite);
    Response.Write(stringWrite.ToString());
             
    Response.End();
}
public override void VerifyRenderingInServerForm(Control control)
{
}
private void BindGrid()
{
    // I am delaying the response to see the Loading... message
    System.Threading.Thread.Sleep(1000);
 
    using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["SQLConnection"].ConnectionString))
    {
        string strSQL = "SELECT OrderDetails.OrderID, OrderDetails.ProductID, Products.ProductName," +
                                      "OrderDetails.UnitPrice, OrderDetails.Quantity, OrderDetails.Discount, " +
                                      "((OrderDetails.UnitPrice * OrderDetails.Quantity) - OrderDetails.Discount) Amount " +
                                            "FROM [Order Details] OrderDetails " +
                                            "JOIN Products ON Products.ProductID = OrderDetails.ProductID " +
                                            "WHERE OrderDetails.OrderID = '" + Request.Form["OrderID"].ToString() + "'";
        SqlCommand command = new SqlCommand(strSQL, connection);
        command.CommandType = CommandType.Text;
 
        connection.Open();
        SqlDataReader dr = command.ExecuteReader(CommandBehavior.CloseConnection);
 
        IList<OrderDetails> detailsList = new List<OrderDetails>();
        while (dr.Read())
        {
            OrderDetails details = new OrderDetails();
            details.OrderID = Convert.ToInt32(dr["OrderID"].ToString());
            details.ProductID = Convert.ToInt32(dr["ProductID"].ToString());
            details.ProductName = dr["ProductName"].ToString();
            details.UnitPrice = Convert.ToDouble(dr["UnitPrice"].ToString());
            details.Quantity = Convert.ToInt32(dr["Quantity"].ToString());
            details.Discount = Convert.ToDouble(dr["Discount"].ToString());
            details.Amount = Convert.ToDouble(dr["Amount"].ToString());
 
            detailsList.Add(details);
        }
 
        GridViewDetails.DataSource = detailsList;
        GridViewDetails.DataBind();
    }
}
?
1
2
3
4
5
6
7
8
9
10
public class OrderDetails
{
    public int OrderID { get; set; }
    public int ProductID { get; set; }
    public string ProductName { get; set; }
    public double UnitPrice { get; set; }
    public int Quantity { get; set; }
    public double Discount { get; set; }
    public double Amount { get; set; }
}
The Style Sheet Script
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
.GridViewStyle
{
    font-family:Calibri;
    font-size:15px;
}
.GridViewHeader
{
    background: url(../../Images/header.png) repeat-x 0px 0px;
}
.GridViewRow
{
}
.GridViewAlternativeRow
{
    background-color:#edf5ff;
}
.InvisibleCol
{
    display:none;
}
.ChildRow
{
    border-width:0px;
}
 
/* Style */
#divHidden
{
 width: 100%;
 height:100%;
 background-color:Gray;
 background: url(../Images/hidden.png) repeat;
 position: absolute;
 top: 0;
 left: 0;
 z-index: 100;
 display: none;
}
 
.EditInfo
{
    width: 310px;
 height: 220px;
 background-color: White;
 position: absolute;
 left: 50%;
 top: 50%;
 margin-top: -50px;
 margin-left: -168px;
 vertical-align:middle;
 z-index: 1001;
 display: none;
}
The JQuery Script
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
$(document).ready(function () {
 
    if ($('#<%= hndExpandedChild.ClientID %>').val().length > 0) {
 
        // Will run when some child grids are expanded
        $.each($('#<%= hndExpandedChild.ClientID %>').val().split(","), function (index, value) {
            ExpandCollapse(value);
        });
    }
 
    $('.ExpandImage').click(function () {
 
        ExpandCollapse($(this).attr('alt'));
 
    })
    function ExpandCollapse(orderId) {
        if ($('#div' + orderId)) {
 
            // Is already ChildGrid shown. If not shown
            if (!isDisplayed($('#div' + orderId))) {
 
                if ($('#hid' + orderId).val() == '0') {
 
                    $('#div' + orderId).css("display", "block");
 
                    jQuery.post('ChildGridBuilder.aspx',
                        { OrderID: orderId },
                        function (data) {
                            $('#div' + orderId).html(data.xml);
                            $('#div' + orderId).css("display", "block");
                            $('#img' + orderId).attr('src', 'images/minus.png');
                            $('#hid' + orderId).val("1");
                        }
                        )
                        .error(function () {
                            alert('Error Occured!');
                            $('#div' + orderId).css("display", "none");
                        })
                }
                else {
                    $('#div' + orderId).css("display", "block");
                    $('#img' + orderId).attr('src', 'images/minus.png');
                }
 
                var selectedIds = "," + $('#<%= hndExpandedChild.ClientID %>').val() + ",";
                if (selectedIds.indexOf("," + orderId + ",") == -1) {
                    selectedIds += "," + orderId;
 
                    selectedIds = selectedIds.replace(/,,/g, ","); // Replacing all ,, ,,, ,,,, etc., to ,
                    while (selectedIds.substring(0, 1) == ",") selectedIds = selectedIds.substring(1);
 
                    $('#<%= hndExpandedChild.ClientID %>').val(selectedIds);
 
                    //alert(orderId + "\n" + $('#<%= hndExpandedChild.ClientID %>').val());
                }
            }
            else { // Already Child Grid Shown
                $('#div' + orderId).css("display", "none");
                $('#img' + orderId).attr('src', 'images/plus.png');
 
                //alert($('#<%= hndExpandedChild.ClientID %>').val());
 
                var selectedIds = "," + $('#<%= hndExpandedChild.ClientID %>').val() + ",";
                //alert(selectedIds.indexOf("," + orderId + ","));
 
                if (selectedIds.indexOf("," + orderId + ",") >= 0) {
 
                    selectedIds = selectedIds.replace("," + orderId + ",", ","); // Removing the Id
 
                    selectedIds = selectedIds.replace(/,,/g, ","); // Replacing all ,, ,,, ,,,, etc., to ,
                    selectedIds = selectedIds.substr(1, selectedIds.length - 2); // Removing , both the end and assigning
 
                    //alert(selectedIds);
                    $('#<%= hndExpandedChild.ClientID %>').val(selectedIds);
 
                    //alert(orderId + "\n" + $('#<%= hndExpandedChild.ClientID %>').val());
                }
            }
        }
    }
    function isDisplayed(object) {
        // if the object is visible return true
        if ($(object).css('display') == 'block') {
            return true;
        }
        // if the object is not visible return false
        return false;
    };
});
In this implementation, I am using an hidden control for storing the list of Order Ids (in comma separated) which are expanded in all pages. So when expanding a row, it will store the Order Id to the hidden control value and when collapse - it will delete from the same. When use navigating to the old page in the GridView, the Javascript search list of orders present on the particular page and search on the hidden control value and expand accordingly.

The output of this code would be as below -


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

This implementation is not helpful for editing a particular row on the Child GridView. This is because the child GridView build in another page (ChildGridBuilder.aspx) and shown in Default.aspx using AJAX concept. So it wont have code behind code to execute.

The next post will explain how to achieve this requirement.

No comments:

Post a Comment

Note: only a member of this blog may post a comment.