Blog Archives About Contact

ASP.NET Server Control Parsing
Permalink :

Being self-taught in .NET is both a cool thing and a frustrating thing. It's cool because it's cool to be self-taught in anything...it's a bit of a confidence boost. I haven't had an hour's worth of .net training. Everything I know about .NET I've learned on my own. That's kinda neat, but there's a real downside.

See, when you're self-taught, you learn to solve the problem at hand. It's a rare thing to find someone who is self-taught who just picks up a book on a subject, reads it cover-to-cover, answers the questions at the end of each chapter, and is an expert. Even rarer is the person who sets up a long series of learning engagements to master the subject in a disciplined way. Instead, the self-taught individual usually jumps into the problem, wanders around in 20 different books and web sites, and eventually stumbles across enough answers that he solves his problem.

The downside? There's a lot of .NET that I have never been exposed to. I can work in self-taught mode for months and never see some of the most common aspects of .NET because they don't happen to meet the need at hand. Then, when the occasion finally surfaces in which I need to know those things, I feel a bit sheepish that I say I know .NET but don't know something basic that a class attender probably learned on day 2.

Today I had one of those experiences.

Probably anyone who does ASP.NET development knows how a server control parses the contents between its tags. But I didn't. I knew there had to be a way to get at the contents between the tags, but I was at a loss for how that actually happened, much less how to make my own custom control behave the way I want it to.

Actually, it's quite simple. By default, the ASP.NET page parser assumes that anything between the tags of a server control corresponds to a property of the server control. This works well for things like header and footer templates. If I had a server control called BigList and it had a property called HeaderTemplate that defined how its header should be built, the tags would look something like this:

<BigList id="bigList1" runat="server">
<HeaderTemplate><b>Header</b></HeaderTemplate>
[Other Stuff]
</BigList>

But here's where it gets interesting. you can turn that default behavior off and customize how the ASP.NET page parser looks at the contents of a tag. There are three quick steps:

  1. Add the ParseChildren attribute to the declaration of the control class and set it to false. Doing this tells the page parser to assume that everything between the tags is a server control. What's really nifty about this is that if there is any html between the tags that isn't a server control, the page parser turns that html into a literal control.
  2. Override the AddParsedSubObject method in the control class. The page parser calls this method on the server control each time it parses a new server control (or literal control). In this method, the control can analyze the type of object that was parsed and decide what to do with it. Normally, the server control just adds the newly-parsed object to its own Controls collection, and it will be rendered as a child control. By overriding this method, though, you have full control over what gets put in the Controls collection and what doesn't.
  3. Override the RenderContents method of the server control to change how the control should be rendered.

Here's a really quick (and kinda lame) example:

Say for some reason you wanted to grab the first bit of HTML inside the BigList tags and use it as the header, instead of defining a template for it. First, you would set the ParseChildren attribute to false in the source code for BigList:

[ParseChildren(false)]
public BigList : WebControl {
...
}

Then you'd override the AddParsedSubObject method. The point here is to grab any literal controls and use them as part of the header:

protected void override AddParsedSubObject(object obj) {
    // Grab literal controls and add them to the header
    if (obj is LiteralControl) {
        LiteralControl lc = obj as LiteralControl;
        this.header = this.header + lc.Text;
    } else {
        // Add all other controls to the child Controls collection
        if (obj is Control) {
            Controls.Add(obj as Control);
        }
    }
}

Then your tags in your aspx page might look like this:

<BigList id="bigList1" runat="server">
<b>Header</b>
[Other Server Controls]
</BigList>

This would accomplish getting the header set to <b>Header</b>, but it still wouldn't be rendered. To get it to render, you just override the RenderContents method:

protected override void RenderContents(HtmlTextWriter output) {
    // Write header
    output.WriteLine(this.header);

    // The rest is taken care of by the child controls
}

And with that, you've output your header.

I haven't yet exhausted the possibilities. I've only begun to show them to you. You can take this line of development pretty far and do a lot of interesting things with it (one of which I hope to show you soon). But if that isn't enough to make your server control behave the way you want it to, you can create a class that knows how to parse the content between your server control's tags and create objects by itself. But I'll get to that another day. I'm tired, and I need to go to bed!

Share This Page
printer iconPrint this page email iconSend it to a friend
My Reading List
Previous Post   Next Post