AJAX Grid Groupuing

I admit, Matt Berseth is really inspiring me! Few days ago I pass through 2 of his great posts:

The first one is pure design tips one which I used for making good presentation of my demo here. The 2nd post is actually the idea.

Here I'll do the same idea Matt presented in his post, but I'll user ASP.NET 2.0 simple stuff plus ASP.NET AJAX Toolkit CollapsiblePanel control.It is required that you have ASP.NET AJAX Extensions installed to be able to run the sample code.

I'll not go through the presentation tips mentioned in Matt's post, I'll just show my technique to implement grouping idea with GridViews and CollapsiblePanel along with SqlDataSource which of course can be replaced with ObjectDataSource. I should mention that I'm using same style sheets provided in Matt's sample code along with images [View Demo].

Bellow is a screenshot of a running sample:
Collapsible Panel

The whole group is wrapped inside a ComponentArt like design:

   1: <div id="dlg" class="dialog" style="width: 700px">
   2:     <div class="header" style="cursor: default">
   3:         <div class="outer">
   4:             <div class="inner">
   5:                 <div class="content">
   6:                     <h2>
   7:                         Northwind: Orders By Customer</h2>
   8:                 </div>
   9:             </div>
  10:         </div>
  11:     </div>
  12:     <div class="body">
  13:         <div class="outer">
  14:             <div class="inner">
  15:                 <div class="content">
  16:                     <!-- My Code Goes Here -->
  17:                 </div>
  18:             </div>
  19:         </div>
  20:     </div>
  21:     <div class="footer">
  22:         <div class="outer">
  23:             <div class="inner">
  24:                 <div class="content">
  25:                 </div>
  26:             </div>
  27:         </div>
  28:     </div>
  29: </div>

The grouping will display orders by customer from Northwind database. Bellow is the SqlDataSource code for selecting Customers "sqlDsCustomers":

   1: <asp:SqlDataSource ID="sqlDsCustomers" runat="server" ConnectionString="<%$ ConnectionStrings:Northwind %>"
   2:     SelectCommand="SELECT [Customers].[CustomerID], [Customers].[CompanyName], COUNT([OrderID]) TotalOrders
   3:                     FROM [Customers] INNER JOIN [Orders] ON [Customers].[CustomerID]=[Orders].[CustomerID]
   4:                     Group By [Customers].[CustomerID], [Customers].[CompanyName]">
   5: </asp:SqlDataSource>

Now simply I can display my master data (grouping) which will display CustomerID with total number of orders he made:

   1: <asp:Panel CssClass="grid" ID="pnlCust" runat="server">
   2:     <asp:UpdatePanel ID="pnlUpdate" runat="server">
   3:         <ContentTemplate>
   4:             <asp:GridView Width="100%" AllowPaging="True" ID="gvCustomers" AutoGenerateColumns="False"
   5:                 DataSourceID="sqlDsCustomers" runat="server" ShowHeader="False" OnRowCreated="gvCustomers_RowCreated">
   6:                 <Columns>
   7:                     <asp:TemplateField>
   8:                         <ItemTemplate>
   9:                             <asp:Panel CssClass="group" ID="pnlCustomer" runat="server">
  10:                                 <asp:Image ID="imgCollapsible" CssClass="first" ImageUrl="~/Assets/img/plus.png"
  11:                                     Style="margin-right: 5px;" runat="server" />
  12:                                 <span class="header"> <%#Eval("CustomerID")%>: <%#Eval("CompanyName")%>(<%#Eval("TotalOrders")%>Orders)</span>
  13:                             </asp:Panel> <!-- Nested GridView And CollapsibalePanel will placed bellow this line -->
  14:                         </ItemTemplate>
  15:                     </asp:TemplateField>
  16:                 </Columns>
  17:             </asp:GridView>
  18:         </ContentTemplate>
  19:     </asp:UpdatePanel>
  20: </asp:Panel>

This will simply display something like this:

Note that the GridView is wrapped inside an ASP.NET Panel control. This is actually important when we start to work with CollapsiblePanel extender.

As we want to implement the grouped data of orders. We will add a nested GridView with its own SqlDataSource. Bellow is a the SqlDataSource for the nested GridView that will display customer orders "sqlDsOrders":

   1: <asp:SqlDataSource ID="sqlDsOrders" runat="server" ConnectionString="<%$ ConnectionStrings:Northwind %>"
   2:     SelectCommand="SELECT [OrderID], [OrderDate], [RequiredDate], [Freight], [ShippedDate] FROM [Orders] WHERE ([CustomerID] = @CustomerID)">
   3:     <SelectParameters>
   4:         <asp:Parameter Name="CustomerID" Type="String" DefaultValue="" />
   5:     </SelectParameters>
   6: </asp:SqlDataSource>
This SqlDataSource is placed inside the ItemTemplate of the master GridView. It is important to note that this SqlDataSource accepts a CustomerID as parameter. To provide this parameter value, we should handle RowCreated Event of the master GridView:
   1: protected void gvCustomers_RowCreated(object sender, GridViewRowEventArgs e)
   2: {
   3:     if (e.Row.RowType == DataControlRowType.DataRow)
   4:     {
   5:         SqlDataSource ctrl = e.Row.FindControl("sqlDsOrders") as SqlDataSource;
   6:         if (ctrl != null && e.Row.DataItem != null)
   7:         {
   8:             ctrl.SelectParameters["CustomerID"].DefaultValue = 
   9:                                ((DataRowView)e.Row.DataItem)["CustomerID"].ToString();
  10:         }
  11:     }
  12: }
Now time to place child nested GridView which will be attached to the "sqlDsOrders". Of course as a nested GridView it will be place just inside the ItemTemplate of the master GridView TemplateField:
   1: <asp:Panel Style="margin-left: 20px; margin-right: 20px" ID="pnlOrders" runat="server">
   2:     <asp:GridView AutoGenerateColumns="false" CssClass="grid" ID="gvOrders" DataSourceID="sqlDsOrders"
   3:         runat="server" ShowHeader="true" EnableViewState="false">
   4:         <RowStyle CssClass="row" />
   5:         <AlternatingRowStyle CssClass="altrow" />
   6:         <Columns>
   7:             <asp:TemplateField ItemStyle-CssClass="rownum">
   8:                 <ItemTemplate><%Container.DataItemIndex + 1%></ItemTemplate>
   9:             </asp:TemplateField>
  10:             <asp:BoundField HeaderText="Order ID" DataField="OrderID" ItemStyle-Width="80px" />
  11:             <asp:BoundField HeaderText="Date Ordered" DataField="OrderDate" DataFormatString="{0:MM/dd/yyyy}"
  12:                 ItemStyle-Width="100px" />
  13:             <asp:BoundField HeaderText="Date Required" DataField="RequiredDate" DataFormatString="{0:MM/dd/yyyy}"
  14:                 ItemStyle-Width="110px" />
  15:             <asp:BoundField HeaderText="Freight" DataField="Freight" DataFormatString="{0:c}"
  16:                 ItemStyle-Width="50px" ItemStyle-HorizontalAlign="Right" />
  17:             <asp:BoundField HeaderText="Date Shipped" DataField="ShippedDate" DataFormatString="{0:MM/dd/yyyy}"
  18:                 ItemStyle-Width="100px" />
  19:         </Columns>
  20:     </asp:GridView>
  21: </asp:Panel>
As you might noticed, this GridView is also wrapped in an ASP.NET Panel control. Now it is time to work with CollapsiblePanel.Just beneath the orders panel I'll place the CollapsiblePanel:
   1: <ajaxToolkit:CollapsiblePanelExtender ID="cpe" runat="Server" 
   2: TargetControlID="pnlOrders"
   3: ExpandControlID="pnlCustomer" CollapseControlID="pnlCustomer"
   4: CollapsedSize="0" Collapsed="True"
   5: AutoCollapse="False" AutoExpand="False" ScrollContents="false" 
   6: ImageControlID="imgCollapsible"
   7: ExpandedImage="~/Assets/img/minus.png" CollapsedImage="~/Assets/img/plus.png"
   8: ExpandDirection="Vertical" />

Now I'll explain the most important properties of the CollapsiblePanel that is used:

  • TargetControlID: Would be the ID of the ASP.NET Panel that contains the contents to be shown or hide. In our case would be the Orders list Panel "pnlOrders"
  • ExpandControlID and CollapseControlID: The ID of the ASP.NET Panel control that will control the expand (show) and collapse (hide) of the contents available in the TargetControlID. In our case would be the customer panel "pnlCust"
  • ImageControlID: The ID of an Image control where an icon indicating the collapsed status of the panel will be placed. The extender will replace the source of this Image with the CollapsedImage and ExpandedImage urls as appropriate. If the ExpandedText or CollapsedText properties are set, they are used as the alternate text for the image
  • ExpandedImage:The path to an image used by ImageControlID when the panel is collapsed
  • CollapsedImage:The path to an image used by ImageControlID when the panel is expanded.

Now when this run, it will display a collapsed list of customers. when you click on the customer it will expand and show list of orders made by that customer.

You can refer to the posted mentioned above for more design and presentation tips. Also the code of this sample is available to download.

Download sample code (291.25 kb) -AjaxControlToolkit v1.0.10920.0 included-