Templated User Controls in ASP.NET

Good design repeats itself. It works hard to convey a whole, a feeling of consistency. Once you understand a part of such a design, you know your way around all of it. This is often done by repetition, using the same elements, colors, styles, positioning, and so on. This is a good thing.

Good code never repeats itself. The number of techniques to avoid it are numerous, and all new languages compete in trying to remove as much repetition as possible (Especially the dynamic ones).

Good design repeats itself, good code does not.

With interface development, you face the conflict above over and over again. You get a design that (rightly) reuses the same concepts over and over, and you need to implement them in code that makes you write the same logic only once. This same time both when writing the code and later when fixing bugs in it, and deep inside, all programmers know that it’s the correct way to do things.

I’m currently working in a .NET project (EPiServer CMS 5), and is faced with a design that uses the same kind of boxes all over the site. The boxes only differ by color and content, so things like shadows and rounded corners are clear repetition that I want to do only once. I’ll do the shadows and corners with CSS, but for that I need a couple of wrapper divs. Divs that I only want to specify once, and then reuse.

The prequisites are:

  1. I want a flexible solution, so I’m not tied to a specific HTML structure (number of divs, or even if I use the div tag or not).
  2. No HTML in properties that get sent to user-controls
  3. No HTML in code-behind (a common way in .NET to split logic (code-behind) and templates (ASP.NET and HTML))

What I came up with was templated user controls. They provide a way to write controls that wrap any other controls you may have, and add content around them. And it’s easy to write and user. This is how the one I wrote is used:

<MyProject:Box runat="server">
    <Contents>
        <h2>Random header...</h2>
        <asp:Repeater runat="server">...</asp:Repeater>
        ...
    </Contents>
</MyProject:Box>

It simply wraps anything inside it (in this case a heading tag and a asp repeater), and lets me do whatever I want with them. In my case, I wanted to add some generic HTML around lots of different content, but you could do anything you wanted.

This is how the above was implemented. First the code-behind:

using System.Web.UI;

namespace MyProject.templates.units
{
    [ParseChildren(true)]
    public partial class Box : System.Web.UI.UserControl
    {
        private ITemplate contents = null;

        [TemplateContainer(typeof(TemplateControl))]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [TemplateInstance(TemplateInstance.Single)]
        public ITemplate Contents
        {
            get
            {
                return contents;
            }
            set
            {
                contents = value;
            }
        }

        void Page_Init()
        {
            if (contents != null)
                contents.InstantiateIn(PlaceHolder1);
        }
    }
}

… and then the “code-front”:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Box.ascx.cs" Inherits="MyProject.templates.units.Box" %>
<div class="box">
    <div class="boxwrapper">
        <asp:Placeholder runat="server" ID="PlaceHolder1" />
    </div>
</div>

I think this is a really useful way to write user controls, especially for those of you that work as interface developers in a .NET world. Asking the people around me I found that quite a few didn’t know how templated user controls worked, so I hope I will be of use to some of you out there. Happy coding!

8 responses to “Templated User Controls in ASP.NET

  1. Great article! This was news for me, and will come in handy in future projects.

  2. Nice trick. I usually do this with controls inheriting from asp:panel, which lets me do this inline. I render html from code behind though (building my own control tree, not pasting markup).

    Your example is much cleaner, the surrounding markup can always be changed without having to recompile (when IE8 comes out for an example :-)

    /Steve

  3. When do you think we should take the next step and start using css3 for purposes such as rounded corners? Browser support is not that good at the moment, i know, but don’t you too think it sucks to insert those extra divs just to get the corner effect?

  4. @Jonatan: Thanks!

    @Steve: Ah, interesting, I’ve never even heard of that trick. One more thing, did you find the article because it had “EPiServer” in it? You work with them right?

    @Anders: Well, not yet. I’m still struggling to get customers to not build for IE6, rounded corners with CSS is far, far, far away. (I’ll tell you when I switch, I promise :)

  5. Thanks for the tip, really neat. However, due to a bug in ASP.NET there may be problems in the designer and code-behind using some nested web controls within the templated control. If you experience this, you need to explicitly point out the nested control in a slightly odd way:

          ...

    and in code-behind:

    Repeater myNestedRepeater = (Repeater)MyBox.FindControl("PlaceHolder1").Controls[0];

    Hope it helps somebody.

  6. @Mårten Berg: Yes, that’s why the example includes TemplateInstance.Single, which means that you can reference controls inside directly. It’s a neat trick that works well for all the simple cases (like this one). If you start looping you need to throw in some FindControls and remove the TemplateInstance.Single. Thanks for your comment!

  7. I just want to say thanks… I’ve been doing template controls for a while now and every once in a while I’ve gotten weird errors around the designer file not liking it. Your simple example allowed me to see what I was doing wrong. This is going to save me a TON of time!

Comments are closed.