Some time ago I ran into Martin Fowler’s Fluent Interface, and something clicked right then and there: I can build a control container following this paradigm. Few things numb my brain more than instantiating server controls by hand, filling out their properties and adding to some container. This happens a lot when developing data-bound and templated server controls.
Back in April, I said the HtmlHead control was very weak. I don’t expect Microsoft folks to over-engineer this control by designing too much up-front, but it would help to have some common functionality there.
For example, to add a meta tag to <head>, you create an instance of HtmlMeta, fill out its properties and then add it to Page.Header.Controls. All this to add one single meta tag! Repeat this several times and the code gets very repetitive and long.
Same goes for images, hyper links, etc. This leaves a developer open to improperly initializing a control. As another example, consider HtmlImage. Nothing stops you from NOT initializing its Alt property, although the alt text (not alt “tag”!) should always be set, even if it’s only an empty string.
MSDN says, “On newer browsers, this caption [Alt] also appears as a ToolTip.” No, it doesn’t. Internet Explorer displays it as a tooltip, but other browsers don’t (which is how it’s supposed to be). You want a tooltip—use an attribute reserved specifically for this purpose: title. Since there’s no Title or Tooltip property on HtmlImage, you add it via the Attributes collection. Again, no rocket surgery, but you do it several times and you get plenty of repetitive code.
And what if you need to emit a link to a style sheet or external JavaScript file? Or my favorite one: @import several stylesheets:
<style type='text/css' media='screen,projection'>
@import '/styles/layout.css';
@import '/styles/colorcode.css';
@import '/styles/booktip.css';
</style>
How do you construct that with convential means?
And then it hit me: build one or more factories to take care of dirty work of proper instantiation of controls and provide a container that talks to these factories and exposes a rich Fluent Interface—a Fluent Control Container.
For example:
new FluentControlContainer ()
.WithImage ("…",
"Alt text goes here")
.WithHyperlink ("http://microsoft.com",
"Microsoft", "Microsoft corporate", LinkTarget.Blank)
.WithExternalJavaScript ("http://www.foo.com/bar.js")
.WithMetaKeywords ("keywords go here")
.WithHttpEquivMetaTag ("content-type", "text/html; charset=utf-8")
.WithLinkedStylesheet (
StylesheetMedia.Print | StylesheetMedia.Handheld,
"styles/styles.aspx?print")
.WithImportedStylesheets (StylesheetMedia.Screen,
"/styles/layout.css",
"/styles/colorcode.css",
"/styles/booktip.css")
.WithHeadLink ("alternate",
"application/rss+xml", "RSS", "/rssfeed.aspx")
The container should be similar to PlaceHolder, its only concern being to aggregate controls and provide a convenient interface. To this end, the plain vanilla Control class works just fine. As such, you can define it declaratively:
<anr:FluentControlContainer id="Foobar" runat="server">
// Add controls here as you would to a regular PlaceHolder
</anr:FluentControlContainer>
Where to draw the line on “rich”?
To quote Joshua Kerievsky (Refactoring to Patterns, p. 1):
When you make your code more flexible or sophisticated than it needs to be, you over-engineer it. Some programmers do this because they believe they know their system’s future requirements. They reason that it’s best to make a design more flexible or sophisticated today, so it can accommodate the needs of tomorrow. That sounds reasonable—if you happen to be a psychic.
It would be daunting, if not impossible, to design a Fluent Control Container to handle every permutation of properties of this or that control, as well as follow every HTML spec. In fact, I think the only feasible common denominator here is the idea itself. You look at your project needs and add only what you need when you need it. This approach should seem more natural to the TDD crowd (although I included no unit tests in the download).
Feel free to grab the source code and share your ideas and suggestions. You can open the project as a web site in Visual Studio 2005.