Like many before me I have been trying to implement a tidy expandable/collapsible gridview where, for example, the grid initially shows a list of writers and on clicking on a suitable 'expand' button in a row the writer's works are dynamically retrieved from the database and displayed. Actually I have managed to get a reasonably tidy version to work using the Ajax Toolkit CollapsiblePanelExtender and DynamicPopulateExtender in combination with minimal additional Javascript. In case it is of any use to anyone, the key elements are as follows:
<asp:GridView runat="server" ID="Writers" DataSourceID="WritersDS" DataKeyNames="PersonID" PageSize='<%$ AppSettings: GridPageSize %>' ><Columns><asp:TemplateField ShowHeader="False" ItemStyle-VerticalAlign="Top"><ItemTemplate><asp:Image ID="ShowWorksButton" runat="server" /><ajaxToolkit:DynamicPopulateExtender ID="DPE" runat="server" PopulateTriggerControlID="ShowWorksButton" CacheDynamicResults="false" ClearContentsDuringUpdate="true" EnableViewState="False" ServiceMethod="GetWorks" ContextKey='<%# Eval("PersonID") %>' TargetControlID="WorksPanel" /></ItemTemplate></asp:TemplateField><asp:TemplateField HeaderText="writer" SortExpression="Surname" ItemStyle-Width="200" ItemStyle-VerticalAlign="Top"><ItemTemplate><asp:Label ID="hName" runat="server" Text='<%# Eval("Name")%>' /></ItemTemplate></asp:TemplateField><asp:TemplateField ItemStyle-Width="750"><ItemTemplate><asp:Panel ID="WorksPanel" runat="server" EnableViewState="false" style="height:auto;min-height:36px;max-height:124px;overflow:auto"/><ajaxToolkit:CollapsiblePanelExtender ID="CPE" ClientIDMode="Static" runat="server" TargetControlID="WorksPanel" ExpandControlID="ShowWorksButton" CollapseControlID="ShowWorksButton" ImageControlID="ShowWorksButton" CollapsedImage="~/images/expand.png" ExpandedImage="~/images/collapse.png" Collapsed="True" CollapsedSize="0" ExpandedSize="124" ScrollContents="True" AutoCollapse="False" AutoExpand="False" /></ItemTemplate></asp:TemplateField></Columns>
And in code behind:
<WebMethod()> _<ScriptMethod()> _ Public Shared Function GetWorks(ByVal contextKey As String) As String Dim dal As New DAL Dim personid As Integer = CInt(contextKey) Dim html As StringBuilder = New StringBuilder html.Append("<table><col width='480px'</col><col width='70px'</col><col width='110px'</col><col width='35px'</col><col width='50px'</col>") html.Append("<tr><th style='text-align:left;'>title</th><th style='text-align:left;'>form</th><th style='text-align:left;'>genre</th><th>written</th><th>owned</th></tr>") Dim works As List(Of LiteraryWorkInfo) = dal.GetSelectedWorksByWriter(personid) Dim owned As String = "" For Each work As LiteraryWorkInfo In works html.Append("<tr><td><a href='workform.aspx?id=" & work.WorkID.ToString() & "'>" & work.Title & "</a></td><td>" & work.Form & "</td><td>" & work.Genre & "</td><td style='text-align:center'>" & work.Written & "</td><td style='text-align:center'>" & work.Owned & "</td></tr>") Next html.Append("</table>") Return html.ToString() End Function
It is also necessary to make the CPE IDs unique (it is probably possible to do this declaratively):
Protected Sub WireUpWorks(ByVal s As Object, ByVal e As GridViewRowEventArgs) Handles Writers.RowCreated If e.Row.RowType = DataControlRowType.DataRow Then Dim cpe As CollapsiblePanelExtender = CType(e.Row.FindControl("CPE"), CollapsiblePanelExtender) Dim id As String = String.Concat("CPE", e.Row.RowIndex) cpe.BehaviorID = id End If End Sub
And like others I have found it necessary to add some Javascript to smooth the animation (why this could not have been the default or at least easily changeable I do not know) and to provide an accordion-like effect:
function pageLoad(sender, args) { for (num = 0; num < 20; num++) { var cpe = $find("CPE" + num); if (cpe) { cpe.add_expanding(accordion); cpe._animation._fps = 45; cpe._animation._duration = 0.4; } } } function accordion(sender, arg) { for (num = 0; num < 20; num++) { var cpe = $find("CPE" + num); if (cpe) { if (sender._expandControlID != cpe._expandControlID) cpe.collapsePanel(cpe._expandControlID); } } }
All of this works surprisingly well, but there are some irritating aspects which I have not been able to fix. I have limited the maximum height of the works panel in order to prevent the overall height of the grid causing vertical scrolling of the page. But what I would really like is for the CPE ExpandedSize to reflect the actual height of the panel (i.e. equivalent to 'auto'). But this does not appear possible and I have had to set ExpandedSize to the same value as the panel's maximum height (not setting ExpandSize at all has the surprising effect of causing the panel to pop out to the full size of the actual content with no animation, completely ignoring the CSS on the panel itself). As a result, the panel always expands to this height even if there is less actual content. And what is even more irritating is that there is a 'disabled' vertical scrollbar in the panel even if no scrolling is required (and if I remove ScrollContents="True" from the extender then, perhaps less surprisingly, there is no scrolling, despite the panel CSS). What I do not know is whether this is a CSS issue or something to do with the extender. Either way, I would be grateful for any suggestions how to fix this.
Jon