Grouped Gridview With Expandable Groups

asp.net, c#-3.0, gridview, jquery

Solution

Since you don't provide your actual code, I put together an example on how to accomplish what you need based off of this other question.

That other question simply takes the files on the server's drive C and groups them by creation time in descending order in a grid. So here's the repeater markup:

<asp:HiddenField ID="dataGroups" runat="server" />
<asp:Repeater ID="rpt" runat="server" OnItemDataBound="rpt_RowDataBound" >
    <ItemTemplate>          
        <!-- Bind to your specific properties i.e. Invoice #, file type, etc. -->
        <table id="tableItem"  runat="server">
            <tr>
                <td style="width: 200px;">
                    <asp:Label ID="lblName" runat="server" Text='<%#Eval("Name") %>'></asp:Label>
                </td>
                <td style="width: 200px;">
                    <asp:Label ID="lblDirName" runat="server" Text='<%#Eval("DirectoryName") %>'></asp:Label>
                </td>
                <td style="width: 200px;">
                    <asp:Label ID="lblCreationTime" runat="server" Text='<%#Eval("CreationTime") %>'></asp:Label>
                </td>
                <td style="50px">
                <asp:Button ID="btnAction" runat="server"  Text="Hit me" OnClick="btnAction_Click"/>
                </td>
            </tr>
        </table>
    </ItemTemplate>       
</asp:Repeater>

Here's the code behind for the `OnRowDataBound` event; written in C# since that's what you use:

private int month = -1;
private int year = -1;
protected void rpt_RowDataBound(object sender, RepeaterItemEventArgs e)
{
    if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
    {
        //Binding to FileInfo objects. 
        //Since we are grouping by CreationTime we need to check if it's time to create a new "group"
        //Is current month and year different from the value previously stored on the month and year variables?
        if (month != (e.Item.DataItem as FileInfo).CreationTime.Month || year != (e.Item.DataItem as FileInfo).CreationTime.Year)
        {   
            month = (e.Item.DataItem as FileInfo).CreationTime.Month;
            year = (e.Item.DataItem as FileInfo).CreationTime.Year;

            //append the current group to the hidden variable "dataGroups" which will tell us quickly how many groups we have in total
            dataGroups.Value += (e.Item.DataItem as FileInfo).CreationTime.ToString("yyyMM") + ",";
        }
        //for every row; "stamp it" with this attribute since we'll use it on the client side with jQuery
        (e.Item.FindControl("tableItem") as HtmlTable).Attributes.Add("data-group", (e.Item.DataItem as FileInfo).CreationTime.ToString("yyyMM"));
    }
}

Now on the client-side; we need to do some jQuery magic in order to build the collapsible panels.

<link href="css/flick/jquery-ui-1.8.22.custom.css" rel="stylesheet" type="text/css" />
<script src="js/jquery-1.7.2.min.js" type="text/javascript"></script>
<script src="js/jquery-ui-1.8.22.custom.min.js" type="text/javascript"></script>

 <script type="text/javascript">
     $(document).ready(function () {
         //asp hidden element containing the list of groups separated by commas.
         //Check code behind of RowDataBound to see where is this populated
         var dataGroups = $('#<%=dataGroups.ClientID%>').val().split(',');
         for (var i = 0; i < dataGroups.length; i++) {
             //split() doesn't have an option to ignore empty strings so we'll just ignore it
             if (dataGroups[i] != '') {
                 //select all table elements with the data-group value matching the 
                 //current group we are iterating over and enclose them all 
                 //inside a div; effectively creating a "group"
                 $('table').filter(function (inputs) {
                     return $(this).data('group') == dataGroups[i];
                 }).wrapAll("<div class='accordion'>");
             }
         }
         var accordions = $('.accordion');
         //now, for every div enclosing the groups, create a Handle that will work as the element that
         //collapses or expands the group
         $(accordions).wrapInner("<div>").prepend('<h3><a href="#">Handle</a></h3>');

         //Now replace the word "Handle" above for the actual group number/name or what have you
         for (var i = 0; i < accordions.length; i++) {
             $(accordions[i]).find('h3 a').text("Group " + $(accordions[i]).find('table:first').data('group'));
         }

         //finally call jQuery.accordion to create the accordions on every group
         $('.accordion').accordion({ collapsible: true, autoHeight: false });
     });
 </script>

Now, those lines of code produce this:

Problem

There are a number of questions around the topic of nesting gridviews or having sub-gridviews. I have considered this approach but it is too much for my purposes. The closest existing question I could find was this one: Grouped Gridview Unfortunately although this has some advice for how to create grouping rows it doesn't go into making them collapsible. My requirement is that I wish the user to see the gridview with separating rows e.g. - GROUP 1 Data 1 | Data 2 | Data 3 Data 1 | Data 2 | Data 3 Data 1 | Data 2 | Data 3 - GROUP 2 Data 1 | Data 2 | Data 3 Data 1 | Data 2 | Data 3 - GROUP 3 Data 1 | Data 2 | Data 3 Data 1 | Data 2 | Data 3 Data 1 | Data 2 | Data 3 Data 1 | Data 2 | Data 3 So that a user could, if they wished view like this: + GROUP 1 - GROUP 2 Data 1 | Data 2 | Data 3 Data 1 | Data 2 | Data 3 - GROUP 3 Data 1 | Data 2 | Data 3 Data 1 | Data 2 | Data 3 Data 1 | Data 2 | Data 3 Data 1 | Data 2 | Data 3 Or this: + GROUP 1 + GROUP 2 + GROUP 3 Essentially all the grouping rows have in them is the title of the group. They're not even really proper Gridview rows. The actual rows are the gridview proper and don't neeed any further drill down capability. I would like my solution to be workable clientside, I have constraints which are that I can use javascript or jQuery (including jQuery-ui 1.8.8) but cannot arbitrarily expand the number of AJAX toolkits I'm using. I would prefer not to have to manage the state of the page constantly through multiple group expanding postbacks. Is this something that can be achieved? Could anyone point me in the direction of a resource which might give me a nudge? EDIT: Oh, yes, and I forgot to mention. The rows of the base gridview occasionally have controls in them including, but potentially not limited to: buttons, textboxes, checkboxes and dropdowns.

Original source