C# Nested Repeaters ASP .Net

Nested repeaters are a very common topic in ASP .Net so I thought it would be a good subject to write on, especially considering the number of solutions that exist when you Google “nested repeaters” that offer plug-and-play answers, but don’t actually explain why one approach may be more optimal than another.

The Solution

XML/HTML for your repeaters:

<asp:Repeater runat="server" ID="rptRobotFactories" EnableViewState="false" OnItemDataBound="rptRobotFactories_ItemDataBound">
                        <ItemTemplate>
                            <div id="header">
                                <%# (((System.Data.DataRowView)Container.DataItem)["FACTORYNAME"]) %>
                            </div>
                            <asp:Repeater runat="server" ID="rptRobots" EnableViewState="false">
                                <HeaderTemplate>
                                    <ul>
                                </HeaderTemplate>
                                <ItemTemplate>
                                    <li>
                                        <%# Eval("ROBOTNAME") %>
                                    </li>
                                </ItemTemplate>
                                <FooterTemplate>
                                    </ul></FooterTemplate>
                            </asp:Repeater>
                        </ItemTemplate>
                    </asp:Repeater>

Code-behind to load data into rptRobotFactories:

//don't forget these guys. save the practice on your typing, finger joints will thank you later =)
using System.Data;
using System.Data.SqlClient;

//a few more variables than necessary were used for code readability. feel free to condense.
//call this function on page load. don't forget your if not postback check
    private void LoadFactories()
    {
        //don't forget to wrap in your exception handling!

          SqlConnection conn = new SqlConnection(
            ConfigurationManager.ConnectionStrings["RobotDBCon"].ConnectionString);

        SqlDataAdapter da = new SqlDataAdapter();
        SqlCommand cmd = new SqlCommand("SP_GET_ROBOTS_IN_FACTORIES", conn);

        cmd.CommandType = CommandType.StoredProcedure;
        //generally you'd probably have some parameters here

        da.SelectCommand = cmd;

        DataTable dtRobots = new DataTable("robots"); //first we get the robots
        
        da.Fill(dtRobots); //notice there's only one SP call and one datatable filled

//this might seem strange using a string array for columns, but the alternative is not as clean IMO
        string[] cols = new string[1]; //if you're uncomfortable using string as primary key add another column and use ID
        cols[0] = "FACTORYNAME";

//these next two lines are the magic makers
        DataTable dtRobotFactories = dtRobots.DefaultView.ToTable("robotfactories", true, cols).Clone();

        dtRobots.DefaultView.ToTable("robotfactories", true, cols).AsEnumerable().Distinct().CopyToDataTable(dtRobotFactories, LoadOption.OverwriteChanges);

        DataSet ds = new DataSet();
        ds.Tables.Add(dtRobotFactories);
        ds.Tables.Add(dtRobots);
       ds.Relations.Add("robotsinfactories",
ds.Tables["robotfactories"].Columns["FACTORYNAME"],
ds.Tables["robots"].Columns["FACTORYNAME"],false);
        ds.Relations[0].Nested = true;

        rptSegments.DataSource = ds.Tables["robotfactories"];
        rptSegments.DataBind();

        conn.Close();
    }

Last piece of code is our itemdatabound event of the parent repeater which loads the child data. Alternatively you can put this inline as the datasource property with a one liner but this approach may allow you more flexibility later:

    protected void rptRobotFactories_ItemDataBound(object sender, RepeaterItemEventArgs e)
    {
        if ((e.Item.ItemType == ListItemType.Item) || (e.Item.ItemType == ListItemType.AlternatingItem))
        {
            Repeater rptRobots = (Repeater)e.Item.FindControl("rptRobots");
            rptRobots.DataSource = ((DataRowView)e.Item.DataItem).CreateChildView("robotsinfactories");
            rptRobots.DataBind();
        }
    }

And that’s it! Your result should look something like:

FACTORY1
 *ROBOTA1
 *ROBOTA2
FACTORY2
 *ROBOTB1
 *ROBOTB2

What makes this code different?

The use of data relations is disputable. Rightfully arguable, depending on the scope of your project and the data being returned and formatted, this may be overkill.

Multiple data relations and tables can use excess resources when simple string variables could be pulled from the dataset and the same achieved with only one repeater or listview.

I do not contest this point but keep in a mind, a simple re-usable design pattern can be rapidly applied to many different sets of data, whereas a customized variable or array based solution will likely be specific to your initial implementation, and not easily re-adapted in another page or separate project.

Ultimately, code patterns will improve your code readability and productivity as a developer.

See my article on how to create secure relational databases in sql for more information on why the result should come back in a single stored procedure rather than making multiple calls.

Advertisements

About Ronnie Diaz

Ronnie Diaz is an enterprise software engineer responsible for front-end and back-end development for companies in many industries. Heavily involved in cloud development, online retail, e-commerce and electronic ordering, fulfillment and customer relational systems.

Posted on June 16, 2011, in Programming & Development and tagged , , , , , , , , . Bookmark the permalink. Leave a comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: