Databinding Nested Repeaters

Many of you might already know about this, but I’ll post it for those of you like me who didn’t. Occasionally I’ll nest a repeater inside of another repeater, and when I do I always attach an ItemDataBound event handler to the parent repeater so I can set the DataSource of the child repeater.

<asp:Repeater ID="rptManufacturers" runat="server" OnItemDataBound="rptManufacturers_ItemDataBound">
    <ItemTemplate>
        <%# Eval("Name") %>
        <asp:Repeater runat="server" ID="rptModels">
            <HeaderTemplate>
                <ul>
            </HeaderTemplate>
            <ItemTemplate>
                <li><%# Eval("ModelName") %></li>
            </ItemTemplate>
            <FooterTemplate>
                </ul>
            </FooterTemplate>
        </asp:Repeater>
    </ItemTemplate>
</asp:Repeater>
protected void rptManufacturers_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
        Manufacturer man = (Manufacturer)e.Item.DataItem;
        Repeater rptModels = (Repeater)e.Item.FindControl("rptModels");
 
        rptModels.DataSource = man.Models;
        rptModels.DataBind();
}

I always found it annoying that I had to create a ItemDataBound function when the only thing I needed to do was bind the child repeater. However, recently I found out you don’t need to bother with all the above code. You can just do this:

<asp:Repeater ID="rptManufacturers" runat="server">
    <ItemTemplate>
        <%# Eval("Name") %>
        <asp:Repeater runat="server" ID="rptModels" DataSource='<%# Eval("Models") %>'>
            <HeaderTemplate>
                <ul>
            </HeaderTemplate>
            <ItemTemplate>
                <li><%# Eval("ModelName") %></li>
            </ItemTemplate>
            <FooterTemplate>
                </ul>
            </FooterTemplate>
        </asp:Repeater>
    </ItemTemplate>
</asp:Repeater>

This is a really simple solution when the only thing you need to do is bind a child control ( Repeater, DataGrid, GridView, etc ). The solution doesn’t really apply if you need to do more logic on the ItemDataBound event.

Advertisements

Radio button within a repeater problem

Recently I was developing a system to create tests and test questions.  For these tests our client wanted multiple choice questions.  To implement this I decided to have a list of textboxes for the answer text, and a radio button for each textbox to select the correct answer.   I knew that a RadioButtonList couldn’t have anything other than a radio button and text, so I went with a repeater.

<asp:Repeater ID="rptRadios" runat="server">
    <HeaderTemplate>
        <ul>
    </HeaderTemplate>
    <ItemTemplate>
        <li>
            <asp:RadioButton ID="rbRadio" runat="server" GroupName="RadioGroup" />
            &nbsp;
            <asp:TextBox runat="server" ID="txtRadio"></asp:TextBox>
        </li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>

Doing it this way caused the group name of each radio button to be inconsistent, because of the repeater. After a while of researching and not finding any good solutions I decided to try changing the group name of the radio buttons using jQuery.

$("input:radio").attr('name', 'RadioGroup');

That gave me the radio button functionality that I wanted, but it prevented me from getting the selected radio button on postback. So I decided to just implement the radio button functionality manually.

var radios = $("input:radio");
radios.click(function() {
     radios.removeAttr('checked');
     $(this).attr('checked', 'checked');
     return true;
});

Which gave me the correct functionality and I could still get the selected radio button and textbox on postback. Probably not the most elegant solution, but I couldn’t find any other way to do it.

Also I needed to make sure at least one of the radio buttons was selected so I added a CustomValidator that called a javascript function.

function ValidateRadioButtons(sender, args) {
     args.IsValid = $("input:radio:checked").size() > 0;
}