Listview With Group Headers in ASP.Net

ListView with group headers

 
Let's say we want to implement a ListView with group headers, where each group is defined by a certain data column having the same value. In other words, each time the data column has a new value (proceeding from top to bottom), we want to have a special view that highlights that fact.
 

Introduction

 
The ListView control renders its ItemTemplate once for every item in its DataSource. As discussed in Grouping Data with the ListView Control, it is possible to insert a grouping template for every N records. This flexibility allows for scenarios like displaying data in a multi-column table.
 
While the built-in grouping feature is certainly useful when it comes to displaying data most people think of grouping to mean that records with similar attributes are lumped together. For instance, the Ticket Booking database contains information about an assortment of products, and each product has attributes like PNR_NUMBER, NAME, MOBILE_NO, BOOKED_BY, BOARDING_POINT, FARE, and so forth. While each NAME name is unique, many PNR_NO share the same Boarding point and  BoardingPointTime. When someone says, "I want to group the PNR_NUMBER data," usually they mean they want to group it by one of these common attributes. The following screenshot shows the user interface people most people associate with the term grouping.
 
Capture.JPG
 
tripsheetUnfortunately, the ListView's grouping feature does not allow for this style of grouping. The good news is that with a few lines of code and markup we can construct such an interface. This article shows how to build a flexible grouping interface that allows the user to choose what data field to group the data by. Read on to learn more!  
  1. string LastBoardingPoint = string.Empty;  
  2. public string AddGroupingRowIfBoardingHasChanged() {  
  3.     string values = string.Empty;  
  4.     string CurrentBoardingPoint = Eval("BUS_BOARDING_POINT").ToString();  
  5.     string boardingPointTime = Eval("START_TIME").ToString();  
  6.     string Landmark = Eval("LAND_MARK").ToString();  
  7.     if (CurrentBoardingPoint.Length == 0) {  
  8.         CurrentBoardingPoint = "Unknown";  
  9.     } else  
  10.     if (!LastBoardingPoint.Equals(CurrentBoardingPoint)) {  
  11.         LastBoardingPoint = CurrentBoardingPoint;  
  12.         values = String.Format("<tr class='groupheader'><td colspan='13'>Boarding Point : {0} - {1} ,{2}  </td></tr>", CurrentBoardingPoint, boardingPointTime, Landmark);  
  13.     } else {  
  14.         values = string.Empty;  
  15.     }  
  16.     return values;  
By adding the preceding databinding syntax we are instructing the ListView to call the AddGroupingRowIfBoardingHasChanged() each time it renders the ItemTemplate, that it will do once per record being bound to the ListView. The AddGroupingRowIfBoardingHasChanged() method, which we will create momentarily, will return either an empty string (in the case that the supplier name hasn't changed since the last product) or the HTML for a table row that announces the new BoardingPoint.
 
Next, create the AddGroupingRowIfBoardingHasChanged() method in the page's code-behind class. This method must return a string value and canont be marked Private (that is, it must be marked as Public or Protected). Keep in mind that this method is called once for every record being bound to the ListView. Add the following code:
  1. <asp:ListView ID="lvDetails" runat="server" OnItemDataBound="lvDetails_ItemDataBound" OnDataBound="lvDetails_DataBound">  
  2.     <LayoutTemplate>  
  3.         <table width="100%" border="1px" cellpadding="0" cellspacing="0" id="tripsheet">  
  4.             <thead>  
  5.                 <tr>  
  6.                     <th>  
  7.                         SNo  
  8.                     </th>  
  9.                     <th>  
  10.                         Ticket No  
  11.                     </th>  
  12.                     <th>  
  13.                         Seat No  
  14.                     </th>  
  15.                     <th>  
  16.                         Passenger Name  
  17.                     </th>  
  18.                     <th>  
  19.                         Contact  
  20.                     </th>  
  21.                     <th>  
  22.                         Droping Point  
  23.                     </th>  
  24.                     <th>  
  25.                         Fare(Rs.)  
  26.                     </th>  
  27.                     <th>  
  28.                         Com.(Rs.)  
  29.                     </th>  
  30.                     <th>  
  31.                         Balance(Rs.)  
  32.                     </th>  
  33.                     <th>  
  34.                         Booked By  
  35.                     </th>  
  36.                     <th>  
  37.                         Booking Type  
  38.                     </th>  
  39.                 </tr>  
  40.             </thead>  
  41.             <tbody>  
  42.                 <tr id="itemPlaceHolder" runat="server">  
  43.                 </tr>  
  44.             </tbody>  
  45.             <tfoot>  
  46.                 <tr>  
  47.                     <td colspan="5">  
  48.                            
  49.                     </td>  
  50.                     <td>  
  51.                         Totals  
  52.                     </td>  
  53.                     <td>  
  54.                         Rs.<asp:Label ID="lblTotalSum" Font-Bold="true" runat="server"></asp:Label>  
  55.                     </td>  
  56.                     <td>  
  57.                         Rs.<asp:Label ID="lblTotalComm" Font-Bold="true" runat="server"></asp:Label>  
  58.                     </td>  
  59.                     <td>  
  60.                         Rs.<asp:Label ID="lblTotalBalance" Font-Bold="true" runat="server"></asp:Label>  
  61.                     </td>  
  62.                 </tr>  
  63.             </tfoot>  
  64.         </table>  
  65.     </LayoutTemplate>  
  66.     <ItemTemplate>  
  67.         <%# AddGroupingRowIfBoardingHasChanged() %>  
  68.         <tr>  
  69.             <td>  
  70.                 <%# Container.DataItemIndex+1 %>  
  71.             </td>  
  72.             <td>  
  73.                 <%# Eval("PNR_NUMBER")%>  
  74.             </td>  
  75.             <td>  
  76.                 <%# Eval("SEAT_NUMBER")%>  
  77.             </td>  
  78.             <td>  
  79.                 <%# Eval("CUSTOMER_NAME")%>  
  80.             </td>  
  81.             <td>  
  82.                 <%# Eval("CUSTOMER_CONTACT")%>  
  83.             </td>  
  84.             <td>  
  85.                 <%# Eval("DROPING_POINT")%>  
  86.             </td>  
  87.             <td>  
  88.                 <%# Eval("FARE")%>  
  89.             </td>  
  90.             <td>  
  91.                 <%# Eval("COMMISSION")%>  
  92.             </td>  
  93.             <td>  
  94.                 <%# Convert.ToInt32(Eval("FARE")) - Convert.ToInt32(Eval("COMMISSION"))%>  
  95.             </td>  
  96.             <td>  
  97.                 <%# Eval("BOOKED_AT")%>  
  98.             </td>  
  99.             <td>  
  100.                 <%# Eval("BOOKING_TYPE")%>  
  101.             </td>  
  102.         </tr>  
  103.     </ItemTemplate>  
  104.     <EmptyItemTemplate>  
  105.         No Bookings are made for this Service  
  106.     </EmptyItemTemplate>  
  107. </asp:ListView> 
ListView Items DataBound
 
The following is for the DataBound ListView Items:
  1. int totalfare, totalCommisionAmount, balanceAmount;  
  2. // Calculating Totals  
  3. protected void lvDetails_ItemDataBound(object sender, ListViewItemEventArgs e) {  
  4.   
  5.     ListViewDataItem dataItem = (ListViewDataItem) e.Item;  
  6.     if (e.Item.ItemType == ListViewItemType.DataItem) {  
  7.         DataRowView rowView = (DataRowView) dataItem.DataItem;  
  8.         totalfare = totalfare + Convert.ToInt32(rowView["FARE"]);  
  9.         totalCommisionAmount = totalCommisionAmount + Convert.ToInt32(rowView["COMMISSION"]);  
  10.         balanceAmount = balanceAmount + (Convert.ToInt32(rowView["FARE"]) - Convert.ToInt32(rowView["COMMISSION"]));  
  11.     }  
  12. }  
  13. // Binding Totals to Labels  
  14. protected void lvDetails_DataBound(object sender, EventArgs e) {  
  15.     Label lblTotal = (Label) lvDetails.FindControl("lblTotalSum");  
  16.     lblTotal.Text = totalfare.ToString();  
  17.     Label lblTotalCommisionAmount = (Label) lvDetails.FindControl("lblTotalComm");  
  18.     lblTotalCommisionAmount.Text = totalCommisionAmount.ToString();  
  19.     Label lblTotalBalanceAmount = (Label) lvDetails.FindControl("lblTotalBalance");  
  20.     lblTotalBalanceAmount.Text = balanceAmount.ToString();  
Grouping Method Based On PNR_NUMBER
 
The following is the grouping method based On PNR_NUMBER:
  1. private DataTable GenerateData(DataTable dtDetails) {  
  2.     string currentpnr = string.Empty;  
  3.     //Geting Data Table structure  
  4.     // DtDetails Have the actual data  
  5.     DataTable newData = dtDetails.Clone();  
  6.     // Getting Distinct datatable PNR_NUMBERS  
  7.     DataTable dtDistinctData = dtDetails.DefaultView.ToTable("NeWData"true"PNR_NUMBER");  
  8.     StringBuilder sbSeats = new StringBuilder();  
  9.     foreach(DataRow drow in dtDistinctData.Rows) {  
  10.         int fareSum = 0, commitionSum = 0;  
  11.         currentpnr = Convert.ToString(drow["PNR_NUMBER"]);  
  12.         //Selecting the rows  
  13.         DataRow[] rows = dtDetails.Select("PNR_NUMBER='" + currentpnr + "'");  
  14.         //Seats join  
  15.         var result = rows.Select(row => row[3].ToString()).ToArray();  
  16.         string joinseats = string.Join(",", result);  
  17.         //fares sum  
  18.         string[] resultfare = rows.Select(row => row[11].ToString()).ToArray();  
  19.         for (int i = 0; i < resultfare.Length; i++) {  
  20.             fareSum = fareSum + Convert.ToInt32(resultfare[i]);  
  21.         }  
  22.         //commition sum  
  23.         string[] resultcomm = rows.Select(row => row[16].ToString()).ToArray();  
  24.         for (int i = 0; i < resultcomm.Length; i++) {  
  25.             commitionSum = commitionSum + Convert.ToInt32(resultcomm[i]);  
  26.         }  
  27.         //gender join  
  28.         var resultgender = rows.Select(row => row[4].ToString()).ToArray();  
  29.         string joingenders = string.Join(",", resultgender);  
  30.         // copy data to new datatable  
  31.         DataRow drrow = newData.NewRow();  
  32.         drrow["PNR_NUMBER"] = currentpnr;  
  33.         drrow["SEAT_NUMBER"] = joinseats + "(" + joingenders + ")";  
  34.         drrow["FARE"] = fareSum;  
  35.         drrow["COMMISSION"] = commitionSum;  
  36.         drrow["CUSTOMER_GENDER"] = joingenders;  
  37.         drrow["CUSTOMER_NAME"] = rows[0]["CUSTOMER_NAME"];  
  38.         drrow["CUSTOMER_CONTACT"] = rows[0]["CUSTOMER_CONTACT"];  
  39.         drrow["BUS_BOARDING_POINT"] = rows[0]["BUS_BOARDING_POINT"];  
  40.         drrow["BOOKED_AT"] = rows[0]["BOOKED_AT"];  
  41.         drrow["DROPING_POINT"] = rows[0]["DROPING_POINT"];  
  42.         drrow["START_TIME"] = rows[0]["START_TIME"];  
  43.         drrow["BOOKING_TYPE"] = rows[0]["BOOKING_TYPE"];  
  44.         drrow["LAND_MARK"] = rows[0]["LAND_MARK"];  
  45.         //Adding Rows to table  
  46.         newData.Rows.Add(drrow);  
  47.     }  
  48.   
  49.     return newData;  
  50. }  
  51.   
  52. protected void GetPassengersDetails() {  
  53.     // getting individual total booked seats from service  
  54.     DataTable dtPassengers = objBusiness.GetData(EBEnum.Enum.Functionality.Bookings.ToString(), 0, ref strProcedure);  
  55.     DataTable newdata = this.GenerateData(dtPassengers);  
  56.     lvDetails.DataSource = newdata;  
  57.     lvDetails.DataBind();  
That's all there is to it! With this code in place, we now get the desired grouping interface.