Wensveen's Blog

April 12, 2011

A Fluent interface for creating Control hierarchies (method chaining)

Filed under: Uncategorized — Tags: , — wensveen @ 8:34 am

I was creating a custom ASP.NET control for a project and I noticed that creating Control hierarchies was a lot of work, and a bit clunky. For instance, create a table with a single row, with a single cell with just two Panels and some literals in them.

Control structure:

  • (this)
    • Table
      • TableRow
        • TableCell
          • Panel
            • Literal
          • Panel
            • Literal
Table table = new Table { BorderStyle = BorderStyle.None, Width = Unit.Percentage(100) };
TableRow tr = new TableRow();
TableCell td = new TableCell();

Panel ush = new Panel { CssClass = "UserSectionHead" };
ush.Controls.Add(new Literal { Text = "Title" });
td.Controls.Add(ush);

Panel usb = new Panel { CssClass = "UserSectionBody" };
usb.Controls.Add(new TextField { Text = "Enter title" });
td.Controls.Add(usb);

tr.Cells.Add(td);
table.Rows.Add(tr);
this.Controls.Add(table);

As you can see, there’s a lot of variables you need to create just to be able to add (nested) child controls to the control hierarchy. And this is just a very small example. Fortunately, most properties can be set in the initializer, otherwise the boilerplate would be even larger!

Borrowing from the fluent interface of the Apache Wicket Framework I wrote some extension methods to make this easier. Because I needed to create a table I wrote extension methods for System.Web.UI.WebControls.Table and System.Web.UI.WebControls.TableRow as well as for System.Web.UI.Control, where the latter is the most important for general usage.

public static class ControlExtensions
{
    public static T AddControls<T>(this T thisControl, params Control[] controls) where T : Control
    {
        foreach (Control control in controls)
        {
            thisControl.Controls.Add(control);
        }
        return thisControl;
    }

    public static Table AddRows(this Table thisTable, params TableRow[] rows)
    {
        thisTable.Rows.AddRange(rows);
        return thisTable;
    }

    public static TableRow AddCells(this TableRow thisTableRow, params TableCell[] cells)
    {
        thisTableRow.Cells.AddRange(cells);
        return thisTableRow;
    }
}

Now, we can create control hierarchies like this:

this.AddControls(
    new Table { BorderStyle = BorderStyle.None, Width = Unit.Percentage(100) }.AddRows(
        new TableRow().AddCells(
            new TableCell().AddControls(
                new Panel { CssClass = "UserSectionHead" }.AddControls(
                    new Literal { Text = "Title" }
                ),
                new Panel { CssClass = "UserSectionBody" }.AddControls(
                    teaserDropDown
                )
            )
        )
    )
);

Much easier to write and read, don’t you think?

The trick is that because the extension methods return the ‘this’ parameter, methods can be chained and nested. The extension method for System.Web.UI.Control needs to be generic because you want myTableCell.AddControls(...) to return an instance of type TableCell and not Control. You wouldn’t be able to add it to TableRow, because TableRow.AddCells expects TableCells.

See also: http://en.wikipedia.org/wiki/Method_chaining and http://en.wikipedia.org/wiki/Fluent_interface

Enjoy!

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

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

Create a free website or blog at WordPress.com.

%d bloggers like this: