Asp.Net Nested Grid View Example And Ajax PopUp & Calendar Extender
Asp.Net Nested Grid View Example And Ajax PopUp & Calendar Extender
Nested Grid View is useful to show parent child relationship table. This blog post contains example to build Nested Grid View with Asp.Net.
The Parent Grid View content can be Edited and its Child Grid View loads on the fly if user clicks on parent grid view row.
I use Northwind Sample Database to produce nested Grid View. Also I use DropDownList, TextBox with Ajax PopUp and TextBox with Ajax Calendar to filter database query.
I already have example about Ajax Popup & Calendar in this Url Making Flexible User Input With Asp.Net Ajax PopUp & Calendar Extender Toolkit Controls .
I already have example about Ajax Popup & Calendar in this Url Making Flexible User Input With Asp.Net Ajax PopUp & Calendar Extender Toolkit Controls .
So this blog contains miscelanous example. This might be useful for web internal application that contains complex user input & data layout.
DROPDOWNLIST, AJAX POPUP & CALENDAR CONTROLS
Asp.Net has DropDownList or Combo Box that easily created using a data source. Ajax PopUp & Calendar Extender creates rich user input.
I want to show Order data based on those three control values.
<ajaxToolkit:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server"> </ajaxToolkit:ToolkitScriptManager> <table> <tr> <td>Category:</td><td>Product:</td><td>Order Date</td><td> </td> </tr> <tr> <td><asp:DropDownList ID="category" runat="server" DataSourceID="sqlcategory" DataTextField="categoryname" DataValueField="categoryid" AutoPostBack="true" ></asp:DropDownList></td> <td><asp:TextBox ID="txproduct" runat="server"></asp:TextBox> </td> <td><asp:TextBox ID="txorderdate" runat="server"></asp:TextBox></td> <td><asp:Button ID="btnfind" runat="server" Text=" Find " /></td> </tr> </table> <asp:Panel ID="panel_product" runat="server" CssClass="popupControl"> <asp:UpdatePanel ID="UpdatePanel2" runat="server"> <ContentTemplate> <asp:CheckBoxList id="chkproduct" runat="server" DataSourceID="sqlproduct" DataTextField="productname" DataValueField="productid" AutoPostBack="true"> </asp:CheckBoxList> <center> <asp:LinkButton ID="lnkprod" runat="server">OK</asp:LinkButton> </center> </ContentTemplate> </asp:UpdatePanel> </asp:Panel> <ajaxToolkit:PopupControlExtender ID="popup_product" runat="server" TargetControlID="txproduct" PopupControlID="panel_product" Position="Bottom"> </ajaxToolkit:PopupControlExtender> <ajaxToolkit:CalendarExtender ID="calext_orderdate" runat="server" TargetControlID="txorderdate" Format="yyyy-MM-dd" PopupPosition="Left"></ajaxToolkit:CalendarExtender> <asp:SqlDataSource ID="sqlcategory" runat="server" ConnectionString="<%$ connectionStrings:northwind_connstr %>" SelectCommand="select categoryid, categoryname from categories"></asp:SqlDataSource> <asp:SqlDataSource ID="sqlproduct" runat="server" ConnectionString="<%$ connectionStrings:northwind_connstr %>" SelectCommand="select productid, productname from products where categoryid=@categoryid"> <SelectParameters> <asp:ControlParameter ControlID="category" Type="Int32" Name="categoryid" PropertyName="SelectedValue" /> </SelectParameters> </asp:SqlDataSource>
ToolkitScriptManager must be included in order to use Ajax PopUp & Calendar extender.
‘category’ DropDownList data is populated using ‘sqlcategory’ sql data source to fill in with list of product categories.
The actual PopUp Control is a panel that contains UpdatePanel, ContentTemplate tags. We can write any Asp.net control Inside ContentTemplate. Here I have checkboxlist and linkbutton to confirm user choice.
PopupControlExtender is a controller to decide which one is a Popup Control, its Target and position relative to target by setting PopupControlID, TargetControlID, and Position relatively.
Likewise with CalendarExtender, it has TargetControlID, PopupPosition and also Date Format.
The actual PopUp Control is a panel that contains UpdatePanel, ContentTemplate tags. We can write any Asp.net control Inside ContentTemplate. Here I have checkboxlist and linkbutton to confirm user choice.
PopupControlExtender is a controller to decide which one is a Popup Control, its Target and position relative to target by setting PopupControlID, TargetControlID, and Position relatively.
Likewise with CalendarExtender, it has TargetControlID, PopupPosition and also Date Format.
CheckBoxList inside panel Popup Control populated with ‘sqlproduct’. This data source retrieve list of products based on categoryid that chosen in ‘category’ DropDownList.
I have ‘Find’ button to find specific Order from Northwind Database based on above user inputs.
NESTED GRID VIEW
After user clicks ‘Find’ button then query is running and resulting a Grid View.
Source of GridView:
<asp:GridView ID="gdvResult" runat="server" AutoGenerateColumns="false" DataKeyNames="orderid" AllowSorting="true"> <Columns> <asp:TemplateField> <ItemTemplate> <a href="javascript:collapseExpand('oid-<%# Eval("orderid") %>');">+</a> <input type='hidden' id='hid_orderid' name='hidorderid' Value='<%# Eval("orderid") %>' /> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Order id" SortExpression="orderid"> <ItemTemplate> <%# Eval("orderid")%> <asp:HiddenField ID="orderid" runat="server" Value='<%# Eval("orderid") %>' /> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Order Date" SortExpression="orderdate" > <ItemTemplate> <%# Eval("orderdate")%> </ItemTemplate> <EditItemTemplate> <div style="width:100px"><asp:TextBox ID="orderdate" runat="server" text='<%#Bind("orderdate") %>' ClientIDMode="Inherit" style="width:60px"></asp:TextBox> <asp:ImageButton ID="imgbtnorddt" runat="server" ImageUrl="~/Calendar_scheduleHS.png" /> </div> </EditItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Required Date" SortExpression="requireddate"> <ItemTemplate> <%# Eval("requireddate")%> </ItemTemplate> <EditItemTemplate> <div style="width:100px"><asp:TextBox ID="requireddate" runat="server" text='<%#Bind("requireddate") %>' ClientIDMode="Inherit" style="width:60px"></asp:TextBox> <asp:ImageButton ID="imgbtnreqdt" runat="server" ImageUrl="~/Calendar_scheduleHS.png" /> </div> </EditItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Freight" SortExpression="freight"> <ItemTemplate> <%# Eval("freight")%> </ItemTemplate> <EditItemTemplate> <asp:TextBox ID="freight" runat="server" text='<%#Bind("freight") %>'></asp:TextBox> </EditItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Ship Name" SortExpression="shipname"> <ItemTemplate> <%# Eval("shipname")%> </ItemTemplate> <EditItemTemplate> <asp:TextBox ID="shipname" runat="server" text='<%#Bind("shipname") %>'></asp:TextBox> </EditItemTemplate> </asp:TemplateField> <asp:TemplateField> <ItemTemplate> <asp:Button ID="editbutton" Text="Edit" runat="server" CommandName="Edit" /> </ItemTemplate> <EditItemTemplate> <asp:Button ID="updatebutton" Text="Update" runat="server" CommandName="Update" /> <asp:Button ID="cancelbutton" Text="Cancel" runat="server" CommandName="Cancel" /> </EditItemTemplate> </asp:TemplateField> <asp:TemplateField> <ItemTemplate> <tr><td colspan="100%"> <div id='oid-<%# Eval("orderid") %>' style="display:none;position:relative; left:25px"> <asp:GridView ID="gdvnested" runat="server" AutoGenerateColumns="false" > <Columns> <asp:TemplateField HeaderText="Product id"> <ItemTemplate> <%# Eval("ProductID") %> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Product Name"> <ItemTemplate> <%#Eval("ProductName") %> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="UnitPrice"> <ItemTemplate> <%# Eval("UnitPrice")%> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Quantity"> <ItemTemplate> <%# Eval("Quantity")%> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Discount"> <ItemTemplate> <%# Eval("Discount")%> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView> </div> </td></tr> </ItemTemplate> </asp:TemplateField> </Columns> <EmptyDataTemplate> No Records Found </EmptyDataTemplate> </asp:GridView> <asp:SqlDataSource ID="sqlresult" runat="server" ConnectionString="<%$ connectionStrings:northwind_connstr %>"></asp:SqlDataSource> <asp:SqlDataSource ID="sqlnested" runat="server" ConnectionString="<%$ connectionStrings:northwind_connstr %>"></asp:SqlDataSource>
In last column section I have templatefield contains child grid view. This child grid view contained in tables row (<TR />) and a div element to implement left padding.
‘sqlresult’ and ‘sqlnested’ sqldatasources SelectCommand is created programmatically due to I want to display the child grid view on demand. It is better at performance and web server do not load heavily.
In first column section I have this
Since showing and hiding a child grid view is a client process then javascript must be used.
Full Js source:
<a href="javascript:collapseExpand('oid-');">+
to show/hide child grid view using javascript.Since showing and hiding a child grid view is a client process then javascript must be used.
Full Js source:
<script type="text/javascript"> function trim(str) { return str.replace(/^\s+|\s+$/g, ''); } function collapseExpand(obj) { var gvObject = document.getElementById(obj); if (gvObject.style.display == "none") { if (trim(gvObject.innerHTML) == "<DIV></DIV>") { __doPostBack("hid_orderid", "nested_gv"); } else { gvObject.style.display = "inline"; } } else { gvObject.style.display = "none"; } } function expandonload(obj) { var gvObject = document.getElementById(obj); gvObject.style.display = "inline"; } function open_calendar(openerid, selecteddate) { window.open("DatePicker.aspx?openerid=" + openerid + "&selecteddate=" + selecteddate,"","width=220,height=210"); return false; } </script>
In
‘hid_orderid’ is Event Target Control ID that contains field ‘OrderID’ value and ‘nested_gv’ is a Event Argument. These value is used in code behind file.
Otherwise, if child grid view is loaded then show it with css style display value ‘inline’ ->
collapseExpand(..)
function I check whether a respective child gridview is already loaded or not. If it is not loaded then create it by submiting a spesific postback using __doPostBack("hid_orderid", "nested_gv");
function.‘hid_orderid’ is Event Target Control ID that contains field ‘OrderID’ value and ‘nested_gv’ is a Event Argument. These value is used in code behind file.
Otherwise, if child grid view is loaded then show it with css style display value ‘inline’ ->
gvObject.style.display = "inline"
.
At every date field like orderdate, requireddate Imagebutton applied to show calendar popup. This is also accomplished in code behind file.
Full code behind file source code:
Partial Class SearchNGridView Inherits System.Web.UI.Page Dim nestedOrderID As String Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If Page.IsPostBack Then If Request.Form("__EVENTARGUMENT") = "nested_gv" Then nestedOrderID = Request.Form("hidorderid") gdvResultBind() ScriptManager.RegisterStartupScript(Me, Me.GetType(), "expand_onload", "expandonload('oid-" & nestedOrderID & "');", True) End If End If End Sub Protected Sub lnkprod_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles lnkprod.Click Dim strprod As New StringBuilder() Dim selected_no As Integer = 0 For i As Integer = 0 To chkproduct.Items.Count - 1 If chkproduct.Items(i).Selected Then If selected_no = 0 Then strprod.Append(chkproduct.Items(i).Value) Else strprod.Append("," & chkproduct.Items(i).Value) End If selected_no = selected_no + 1 End If Next popup_product.Commit(strprod.ToString()) End Sub Protected Sub btnfind_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnfind.Click Session("sqlsearch") = "select distinct orders.orderid, Convert(varchar(10),orders.orderdate,101) as orderdate, convert(varchar(10),requireddate,101) as requireddate, freight, shipname from orders, [order details] od, products " & _ " where orders.orderid=od.orderid and od.productid=products.productid and od.productid in (" & txproduct.Text & ") and orders.orderdate='" & txorderdate.Text & "'" gdvResultBind() End Sub Protected Sub gdvResult_RowCreated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles gdvResult.RowCreated Dim selectedorderdate As String Dim selectedrequireddate As String Dim orderdate As TextBox = DirectCast(e.Row.FindControl("orderdate"), TextBox) Dim requireddate As TextBox = DirectCast(e.Row.FindControl("requireddate"), TextBox) Dim btnorddt As ImageButton = DirectCast(e.Row.FindControl("imgbtnorddt"), ImageButton) Dim btnreqdt As ImageButton = DirectCast(e.Row.FindControl("imgbtnreqdt"), ImageButton) If DataBinder.Eval(e.Row.DataItem, "orderdate") IsNot Nothing Then selectedorderdate = DataBinder.Eval(e.Row.DataItem, "orderdate").ToString() End If If DataBinder.Eval(e.Row.DataItem, "orderdate") IsNot Nothing Then selectedrequireddate = DataBinder.Eval(e.Row.DataItem, "requireddate").ToString() If btnorddt IsNot Nothing Then btnorddt.Attributes.Add("onclick", "javascript:return open_calendar('gdvResult_" & orderdate.ClientID & "','" & selectedorderdate & "')") End If If btnreqdt IsNot Nothing Then btnreqdt.Attributes.Add("onclick", "javascript:return open_calendar('gdvResult_" & requireddate.ClientID & "','" & selectedrequireddate & "')") End If End Sub Protected Sub gdvResult_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles gdvResult.RowDataBound If e.Row.RowType = DataControlRowType.DataRow Then Dim orderid As String = Convert.ToString(DataBinder.Eval(e.Row.DataItem, "orderid")) If orderid = nestedOrderID And nestedOrderID <> "" Then Dim gvchild As GridView = DirectCast(e.Row.FindControl("gdvnested"), GridView) Dim sql As String = "select od.orderid, od.productid, productname, od.unitprice, od.quantity, od.discount from [order details] od inner join products on od.productid=products.productid where orderid=" & orderid sqlnested.SelectCommand = sql gvchild.DataSourceID = "sqlnested" gvchild.DataBind() End If End If End Sub Protected Sub gdvResult_Sorting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewSortEventArgs) Handles gdvResult.Sorting Dim sortDirection As String If e.SortDirection = WebControls.SortDirection.Ascending Then sortDirection = "DESC" Else sortDirection = "ASC" End If Session("sqlsearch") = Session("sqlsearch") & " order by " & e.SortExpression & " " & sortDirection gdvResultBind() End Sub Protected Sub gdvResult_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles gdvResult.RowCommand If e.CommandName = "Edit" Then ElseIf e.CommandName = "Update" Then Dim gvr As GridViewRow = CType(CType(e.CommandSource, Button).NamingContainer, GridViewRow) Dim orderid As HiddenField = CType(gvr.FindControl("orderid"), HiddenField) Dim freight As TextBox = CType(gvr.FindControl("freight"), TextBox) Dim orderdate As TextBox = CType(gvr.FindControl("orderdate"), TextBox) Dim requireddate As TextBox = CType(gvr.FindControl("requireddate"), TextBox) Dim shipname As TextBox = CType(gvr.FindControl("shipname"), TextBox) Dim sqlupdate = "update orders set orderdate='" & orderdate.Text & "', " & _ " requireddate='" & requireddate.Text & "', freight=" & freight.Text & ", shipname='" & shipname.Text & "' where orderid=" & orderid.Value sqlresult.UpdateCommand = sqlupdate sqlresult.Update() End If gdvResultBind() End Sub Private Sub gdvResultBind() sqlresult.SelectCommand = Session("sqlsearch") gdvResult.DataSourceID = "sqlresult" gdvResult.DataBind() End Sub End Class
Several things I want to point are
- Parent Gridview data is populated in
btnfind_Click(..)
- In
Page_Load(..)
procedure, I catch __EVENTARGUMENT element with value ‘nested_gv’ to load child grid view on demand. The creation itself is during Binding the parent gridview atgdvResult_RowDataBound(..)
After child grid view is created then show it usingScriptManager.RegisterStartupScript(Me, Me.GetType(), "expand_onload", "expandonload('oid-" & nestedOrderID & "');", True)
. This code makes expanonload Js function running on load event. - In
gdvResult_RowDataBound(..)
procedure, I check whether parent order id is same with child. if it is same then SelectCommand in sqlnested sqldatasource is written and then child grid view populates. - In
gdvResult_RowCreated(..)
procedure, I add ‘onclick’ attribute to ImageButton to show calendar or datapicker as popup. Basically, it opens an Url using javascript open_calendar function. Tihs Url contains Calendar control only.
DatePicker.aspx<html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> <script type="text/javascript" language="javascript"> function setdate(selecteddate, openerid) { window.opener.document.getElementById(openerid).value = selecteddate; window.close(); } </script> </head> <body> <form id="form1" runat="server"> <div> <asp:Calendar ID="Calendar1" runat="server" BackColor="White" BorderColor="#999999" CellPadding="4" DayNameFormat="Shortest" Font-Names="Verdana" Font-Size="8pt" ForeColor="Black" Height="180px" Width="200px"> <DayHeaderStyle BackColor="#CCCCCC" Font-Bold="True" Font-Size="7pt" /> <NextPrevStyle VerticalAlign="Bottom" /> <OtherMonthDayStyle ForeColor="#808080" /> <SelectedDayStyle BackColor="#666666" Font-Bold="True" ForeColor="White" /> <SelectorStyle BackColor="#CCCCCC" /> <TitleStyle BackColor="#999999" BorderColor="Black" Font-Bold="True" /> <TodayDayStyle BackColor="#CCCCCC" ForeColor="Black" /> <WeekendDayStyle BackColor="#FFFFCC" /> </asp:Calendar> </div> </form> </body> </html>
DatePicker.aspx.vbPartial Class DatePicker Inherits System.Web.UI.Page Dim openerid, selecteddate As String Protected Sub Calendar1_SelectionChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Calendar1.SelectionChanged ScriptManager.RegisterStartupScript(Me, Me.GetType(), "set_date", "setdate('" & Calendar1.SelectedDate & "','" & openerid & "');", True) End Sub Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load openerid = Request.QueryString("openerid") selecteddate = Request.QueryString("selecteddate") Calendar1.SelectedDate = Date.Parse(selecteddate) Calendar1.VisibleDate = Calendar1.SelectedDate End Sub End Class
Theopen_calendar(..)
Js function contains two querystring which are opener element id and current respective date value. So at Page_load, I retrieved them to set initial date on Calendate control.
When user change a date then opener element value set to chosen date and the DatePicker.aspx page will be closed.
Screen shots:
C# create dates in GridView
ReplyDelete