1

I have a setup using three levels of nesting: A Repeater, the items of which utilize CollapsiblePanelExtenders (which work), and each contain a GridView. Each of these then contain another GridView which is controlled by another CollapsiblePanelExtender. These inner CollapsiblePanels will effectively show either an expanded or collapsed state only if I set clientState to True. However, they do not effectively expand or collapse as expected. Everything is bound dynamically.

Here is the markup...

<asp:Repeater ID="cat_repeater" runat="server">
  <ItemTemplate>
    <asp:CollapsiblePanelExtender id="cat_cpExt" runat="server" TargetControlID="cat_pnl" CollapsedSize="0" Collapsed="false" CollapsedImage="collapsed.png" ExpandedImage="expanded.png" ExpandControlID="cpControl_pnl" CollapseControlID="cpControl_pnl" TextLabelID="cpControl_lbl" ImageControlID="cpControl_img" CollapsedText='<%#configCPText(eval("Title"), False)%>' ExpandedText='<%#configCPText(eval("Title"), True) %>' />
    <asp:Panel ID="cpControl_pnl" runat="server" Visible='<%#itemVisible(eval("ID"), "Recipients", "CategoryID") %>' CssClass="CPanelStyle">
      <asp:Image ID="cpControl_img" runat="server" ImageUrl="expanded.png" />
      <asp:Label ID="cpControl_lbl" runat="server" Text='<%#configCPText(eval("Title"), True) %>' CssClass="CPanelText" />
    </asp:Panel>
    <asp:Panel ID="cat_pnl" runat="server">
      <asp:GridView ID="recipients_gv" runat="server" CssClass="GVStyle" HeaderStyle-CssClass="GVHeaderStyle" RowStyle-CssClass="GVItemStyle" AutoGenerateColumns="false" GridLines="none" AllowPaging="false">
        <Columns>
          <asp:TemplateField HeaderText="Name" SortExpression="Last Name" ItemStyle-CssClass="GVNameStyle">
            <ItemTemplate>
              <asp:Literal id="name_lit" runat="server" text='<%#formatNameText(eval("FirstName"), eval("LastName")) %>' />
            </ItemTemplate>
          </asp:TemplateField>
          <asp:TemplateField HeaderText="Gifts" ItemStyle-Width="500px">
            <ItemTemplate>
              <asp:CollapsiblePanelExtender id="gifts_cpExt" runat="server" TargetControlID="gifts_pnl" CollapsedSize="0" Collapsed="true" CollapsedImage="collapsed.png" ExpandedImage="expanded.png" ExpandControlID="cpControl_pnl2" CollapseControlID="cpControl_pnl2" TextLabelID="cpControl_lbl2" ImageControlID="cpControl_img2" CollapsedText='<%#configGiftsCPText(eval("ID"), True)%>' ExpandedText='<%#configGiftsCPText(eval("ID"), False) %>' />
              <asp:Panel ID="cpControl_pnl2" runat="server" Visible='<%#itemVisible(eval("ID"), "Gifts", "RecipientID") %>'>
                <asp:Image ID="cpControl_img2" runat="server" ImageUrl="collapsed.png" />
                <asp:Label ID="cpControl_lbl2" runat="server" Text='<%#configGiftsCPText(eval("ID"), False) %>' />
              </asp:Panel>
              <asp:Panel ID="gifts_pnl" runat="server">
                <asp:GridView ID="gifts_gv" runat="server" DataKeyNames="ID" RowStyle-CssClass="GVInnerItemStyle" HeaderStyle-CssClass="GVInnerHeaderStyle" Gridlines="None" AutoGenerateColumns="false" AllowPaging="false" Width="475px">
                  <Columns>
                    <asp:TemplateField ItemStyle-CssClass="GVInnerButtonItemStyle" HeaderText="Description">
                      <ItemTemplate>
                        <asp:LinkButton ID="gift_lBtn" runat="server" Text='<%#eval("Description") %>' CommandName="Select" />
                      </ItemTemplate>
                    </asp:TemplateField>
                    <asp:TemplateField HeaderText="Complete" ItemStyle-Width="50px">
                      <ItemTemplate>
                        <asp:CheckBox ID="giftComplete_cbx" runat="server" Checked='<%#eval("Complete") %>' />
                      </ItemTemplate>
                    </asp:TemplateField>
                  </Columns>
                </asp:GridView>
              </asp:Panel>
            </ItemTemplate>
          </asp:TemplateField>
        </Columns>
      </asp:GridView>
    </asp:Panel>
    <br />
  </ItemTemplate>
</asp:Repeater>

...And here is the code to bind everything:

Protected Sub bindCategories()

    Try
        oCmd.Connection = oConn
        oCmd.CommandType = CommandType.Text

        strSQL = "SELECT * FROM Categories"

        oCmd.CommandText = strSQL
        oDA.SelectCommand = oCmd

        oDA.Fill(oDTbl)

        cat_repeater.DataSource = oDTbl
        cat_repeater.DataBind()

        For i As Integer = 0 To oDTbl.Rows.Count - 1


            oCmd.Parameters.Clear()
            inner_dTbl.Clear()

            strSQL = "SELECT *, Title FROM Recipients INNER JOIN Categories on Recipients.CategoryID = Categories.ID WHERE CategoryID = @CategoryID ORDER BY LastName"

            oParam = New SqlParameter
            oParam.ParameterName = "CategoryID"
            oParam.SqlDbType = SqlDbType.Int
            oParam.Value = oDTbl.Rows(i)("ID")
            oCmd.Parameters.Add(oParam)

            oCmd.CommandText = strSQL
            oDA.SelectCommand = oCmd

            oDA.Fill(inner_dTbl)

            Dim gv As GridView = CType(cat_repeater.Items(i).FindControl("recipients_gv"), GridView)
            gv.DataSource = inner_dTbl
            gv.DataBind()

            For j As Integer = 0 To inner_dTbl.Rows.Count - 1

                oCmd.Parameters.Clear()
                gifts_dTbl.Clear()

                strSQL = "SELECT * FROM Gifts WHERE RecipientID = @RecipientID ORDER BY Description"

                oParam = New SqlParameter
                oParam.ParameterName = "RecipientID"
                oParam.SqlDbType = SqlDbType.Int
                oParam.Value = inner_dTbl.Rows(j)("ID")
                oCmd.Parameters.Add(oParam)

                oCmd.CommandText = strSQL
                oDA.SelectCommand = oCmd

                oDA.Fill(gifts_dTbl)

                Dim inner_gv As GridView = CType(gv.Rows(j).FindControl("gifts_gv"), GridView)
                inner_gv.DataSource = gifts_dTbl
                inner_gv.DataBind()

                Dim cpExt As CollapsiblePanelExtender = CType(gv.Rows(j).FindControl("gifts_cpExt"), CollapsiblePanelExtender)
                cpExt.Collapsed = True
                cpExt.ClientState = True

            Next

        Next
    Catch ex As Exception
        Throw ex
    End Try

End Sub

I've successfully used CollapsiblePanelExtenders in a 2-level nested GridView setup that without a problem before, and without having to set clientState. However, I've had to specify clientState when using CollapsiblePanelExtenders with Repeaters before.

Does anyone have any input regarding why these extenders will not function in this 3-level nested setup?

jcolebrand
  • 15,889
  • 12
  • 75
  • 121
Zogglet
  • 39
  • 2
  • 10

1 Answers1

0

It looks like the reason it's required is because it needs a postback to make all that logic work:

if (this.SupportsClientState) {
  ScriptManager.RegisterHiddenField(this, this.ClientStateFieldID, this.SaveClientState());
  this.Page.RegisterRequiresPostBack(this);
}

This is from public class ScriptControlBase in the AjaxControlToolkit space.

Are you wrapping this in an UpdatePanel? Does it cause a postback to use the CPE in the repeater? It's been awhile since I've tried to do a CPE in a repeater, and I'm not setup at this point to check the code out and build one.

Is there a reason you can't just use something completely client-side and just expand/collapse these using javascript? Are you loading data dynamically server side when these things get expanded?

jcolebrand
  • 15,889
  • 12
  • 75
  • 121
  • Do you know if it's currently doing a postback on the repeater CPE? – jcolebrand Nov 26 '11 at 04:38
  • I haven't put any of it in an UpdatePanel at this point (I was planning to wrap at least the inner GridViews in one for further functionality I plan on adding.) Using the CPE in the repeater doesn't cause a postback, yet the CPE is postback-aware. All of my data--as I've shown in my code--is loaded dynamically into the controls utilizing the CPE's. I could attempt a JavaScript approach using the CPE's client-side methods. I will give it a try tomorrow. – Zogglet Nov 26 '11 at 04:49
  • If you don't do that, then just look and see about using some jQuery and toggle on those id's, but that may be trickier. I should be on and off tomorrow during the day, if you want to ping me here and I can see if I can meet you online (say in the [so] chatrooms?). I'm US Central time (so GMT - 6 yeah?). Lemme know, glad to help debug where I can. – jcolebrand Nov 26 '11 at 04:56
  • I don't believe I could, in fact, utilize the CPE's client-side methods or jQuery since the CPEs are generated dynamically upon the binding of the outer Gridviews; any number of them could be generated. I'd be happy to debug with you; I'm Eastern Standard time, however. – Zogglet Nov 26 '11 at 05:17
  • I did intend you to attempt replacing the CPE functionality with something else, in point of fact. Let it lie until tomorrow ;-) – jcolebrand Nov 26 '11 at 06:17
  • Well, since a CPE is just two primary parts: a header, and a body; I would suggest just using two divs stacked inside another div, and let clicking on the header div would toggle the display: none of the body div. Since the HTML is easy enough to generate, I would just advise using jQuery to achieve the rest of what you want. – jcolebrand Nov 26 '11 at 23:02
  • The problem with that is that this content is dependent on data bound server side - specifically the outer CPE. In any other case, I'd agree. – Zogglet Nov 26 '11 at 23:10
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/5381/discussion-between-jcolebrand-and-zogglet) – jcolebrand Nov 26 '11 at 23:17