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;
}

Easy “No Item Template” In Repeater

A “No Item Template” is some content that you want to display if a Repeater is bound with no items. There is no built in tag to handle empty data in the repeater in markup. But, it is easy to add a “No Item Template” inside your markup without having to extend the Repeater class.

Simply set the Visibility property of your content to the number of items bound to the repeater inside the FooterTemplate tag. Instead of relying on the ID of the Repeater in the conditional, I just cast Container.Parent as a Repeater. This is better than the alternative because:

  • There is less repeated markup in case the ID changes.
  • You can use the snippet inside of naming containers such as nested Repeaters, to avoid the error: “The name ‘repItems’ does not exist in the current context”

Here is what it looks like:

<asp:Repeater id="repItems" runat="server">
	<FooterTemplate>
		<asp:Label Text="No Items Found" runat="server" 
			Visible='<%# ((Repeater)Container.Parent).Items.Count == 0 %>' />
	</FooterTemplate>
	<ItemTemplate></ItemTemplate>
</asp:Repeater>

Pretty simple trick, but this feature is not something that I want to have to add extra code for and event handlers for, and the snippet does the job.