The following example displays master-detail data similar to MS-Access. It also shows the sum total of Price Column at the end of each Products section. Attached screenshots shows various views, when the application is run.
I used the Categories & products table from sample Northwind DB. A given category can have multiple products which is the basis of this master-detail.The 2 tables are joined by CategoryID. I used ListView for the Outer Category table & the light-weight Repeater as the nested one which shows the product details for a given Category.JQuery helps in hide/Show of each detail section. Rest is all self-explainatory. Here’s the complete code.
//aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ListViewGrouping2.aspx.cs" Inherits="ListViewGrouping2" %> <%@ Import Namespace="System.Data" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.5.js" type="text/javascript"></script> <script type="text/javascript"> $(document).ready(function () { $('.expandcolapse').click(function () { var image = $(this)[0]; if ($(image).attr("src") == "./images/plus.png") { $(image).attr("src", "./images/minus.png"); $(this).closest('table').next('table').find('tbody').find('tr').removeClass('hideDetails'); } else { $(image).attr("src", "./images/plus.png"); $(this).closest('table').next('table').find('tbody').find('tr').addClass('hideDetails'); } }); }); </script> <style type="text/css"> .hideDetails { display:none; } .RowOdd { color:Green; } .RowEven { color:Olive; } .RowHeader { color:Red; } .RepeaterBkdg { color:Orange; } </style> </head> <body> <form id="form1" runat="server"> <div> <asp:ListView runat="server" ID="lv1" onitemdatabound="lv1_ItemDataBound"> <LayoutTemplate> <b><u><span class="RowHeader">Categories</span></u></b> <table > <tr class="RowHeader"> <td style="width:40px;"></td> <td style="width:40px;">ID</td> <td style="width:150px;">Category Name</td> <td style="width:450px;">Description</td> </tr> </table> <tr id="itemPlaceholder" runat="server"></tr> </LayoutTemplate> <ItemTemplate> <table> <tr class='<%# Convert.ToBoolean(Container.DataItemIndex % 2) ? "RowOdd" : "RowEven" %>' > <td style="width:40px;"><IMG id="<%#Eval("CategoryID")%>img1" class="expandcolapse" src="./images/plus.png"></td> <td style="width:40px;"><%#Eval("CategoryID", "{0:d}")%></td> <td style="width:150px;" ><%# Eval("CategoryName")%></td> <td style="width:450px;"><%# Eval("Description")%></td> </tr> </table> <asp:Repeater ID="rptProducts" OnItemDataBound="rptProducts_ItemDataBound" OnItemCreated="rptProducts_ItemCreated" runat="server" DataSource='<%# ((DataRowView)Container.DataItem).Row.GetChildRows("myrelation") %>'> <HeaderTemplate> <table class="RepeaterBkdg" style="padding-left:50px;"> <tr class="hideDetails"><td colspan="5"><b><u>Related Products</u></b></td></tr> <tr class="hideDetails" > <td> </td> <td><b>ID</b></td> <td><b>Name</b></td> <td><b>Qty</b></td> <td><b>Price</b></td> </tr> </HeaderTemplate> <ItemTemplate> <tr class="hideDetails" > <td> </td> <td><%# DataBinder.Eval(Container.DataItem, "[\"ProductID\"]")%></td> <td><%# DataBinder.Eval(Container.DataItem, "[\"ProductName\"]")%></td> <td><%# DataBinder.Eval(Container.DataItem, "[\"QuantityPerUnit\"]")%></td> <td><%# DataBinder.Eval(Container.DataItem, "[\"UnitPrice\"]")%></td> </tr> </ItemTemplate> <FooterTemplate> <tr class="hideDetails" ><td colspan="5"><hr /></td></tr> <tr class="hideDetails" > <td colspan="5"> <asp:Label ID="Label1" style="float:right;padding-right:2px;" runat="server" Text="<%#TotalPrice%>"></asp:Label> </td></tr> </table> </FooterTemplate> </asp:Repeater> </ItemTemplate> </asp:ListView> </div> </form> </body> </html>
//codebehind
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Configuration; using System.Data; using System.Data.SqlClient; public partial class ListViewGrouping2 : System.Web.UI.Page { private string _DBConnection1 = ConfigurationManager.AppSettings["conStr3"]; private float _TPrice = 0.0f; public string TotalPrice { get { return "<b>Total Price:</b> $" + String.Format("{0:0.##}", _TPrice.ToString()); } } protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { lv1.DataSource = GetData2().Tables["Categories"]; lv1.DataBind(); } } private DataSet GetData2() { SqlConnection cnn = new SqlConnection(_DBConnection1); SqlDataAdapter cmd1 = new SqlDataAdapter("select CategoryID,CategoryName,Description from Categories WHERE CategoryID is NOT NULL order by CategoryID", cnn); DataSet ds = new DataSet(); cmd1.Fill(ds, "Categories"); SqlDataAdapter cmd2 = new SqlDataAdapter("select ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock from Products", cnn); cmd2.Fill(ds, "Products"); //Create the relation bewtween the Category and Products tables. ds.Relations.Add("myrelation", ds.Tables["Categories"].Columns["CategoryID"], ds.Tables["Products"].Columns["CategoryID"]); cnn.Close(); return ds; } protected void lv1_ItemDataBound(object sender, ListViewItemEventArgs e) { _TPrice = 0.0f; } protected void rptProducts_ItemDataBound(object sender, RepeaterItemEventArgs e) { if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) { _TPrice += float.Parse(((System.Data.DataRow)(e.Item.DataItem)).ItemArray[5].ToString()); } } protected void rptProducts_ItemCreated(object sender, RepeaterItemEventArgs e) { } }
Thanks for reading.