Skip navigation.

Recent readingAll recent postsWhat Makes a "Strong Player"?

Introducing Fluent Control Container

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.

Comments

Comment permalink 1 Omar |
Wow. This is wicked! I’m also starting to get tired of creating instances of other controls, filling out their properties and adding them to a container control. This is definitely a great idea. I’ll start experimenting with this. Thanks.
Comment permalink 2 Adi |
Well, the idea is not bad at all. Regarding your first concerns about dealing with too many objects (controls) I do prefer to just use the TextWriter. After all, at the end of the day, that's what's used to spit the resulting code to the client, and instantiating too many objects on each request doesn't sound good. Think about creating a product interface (UI - (X)HTML) that you're gonna use at least 10 - 20 times per page. If that single block has 1 or more images (IMG), each with an alt attribute, 2 or more paragraphs (P), 1 list of options (UL - LIs), would you create each and every of these objects (*.HtmlControls ns) and set the appropriate properties? You're talking about more compelling code, I'm talking about speed. I guess these 2 combined would drive to something better.
Comment permalink 3 Milan Negovan |
Adi, you're talking about a different approach to build output. If you force controls to render themselves and stream their output to TextWriter ahead of time, they stop participating in the regular control life cycle.

When you add them to a Fluent Container, they maintain their view state, raise events, etc, which doesn't happen if you "flatten" them.

If I were to built an ordered or unordered list, I'd write a custom server control where this could be done much better than building such a list in a Fluent Container.
Comment permalink 4 ADI |
Milan,

Yes I am talking about a different approach to build output. No I'm not forcing the control(s) to render ahead of time, and they are participating in the regular control life cycle.

Viewstate and maintaining it has nothing to do with this. If you need it, you can use it. I prefer not to. Raising events ... nothing stops them to do so, they are participating to every step in the normal life cycle. It's just a faster way of processing a request. Taking care of their own renderring does not mean skipping steps, but you can skip a few if you want to (state, databinding, etc.), and things will happen even faster.

If you think at your FluentContainer as finally X/(HT)ML, you know what I mean. Don't get me wrong, I'm not saying that every .WithXYZ will append markup to the writer, not at all.

If you were to build a list (OL/UL) I think that choosing not to create a control at all would be better. Put it this way:
1) you have the data
2) you instantiate the control and populate it (this involves a loop)
3) at renderring time, that control will render it's own output, based on your rules (it's your control) - (this involves a loop also)

I think you're seeing my point now. What if you skip step 2 entirely, and when step 3's comming, you use the same logic to output that data?

I know this is a simple scenario, and the benefits are not huge. But think of it in a large, complicated one. I assure you it makes a difference.

Extending the FluentContainer idea, I can come up with something new:

If the container can only contain other containers, in other words they all have a common base, you can overload the .WithXYZs and provide a way to step in and out, one step at a time. I mean:

myContainer.WithAnotherContainer(..., ..., etc.) can return you myContainer, anotherContainer (the one you're adding), or the myContainer's container. This can become complicated, but it's an idea.

Looking forward for more comments.

Adi
Comment permalink 5 Milan Negovan |
Adi, yes, I see your point now. Also, this container is derived from Control and therefore can aggregate any other server control including other containers. So you can pick and choose. ;)

Emails and Notifications

Would you like to be notified when somebody responds to this post?  Would you like to have these comments emailed to you?

TrackBacks

Sorry, TrackBacks are not allowed.

Submit your comment

Please enter only text since all HTML tags except hyperlinks will be stripped. Hyperlinks will become live links. Any comments with flaming or offensive language will be deleted. Be courteous to other posters. Thank you.

Your name (required):
Your email (optional):
Your site's URL (optional):
Enter this number
Type in the number above:
Comment (required):