Lately I’ve been playing around with asp.net 2.0.
One of the new "features" of asp.net 2 is the introduction of Naming containers.
This is because a control declared as
<asp:TextBox ID="TextBox1" runat="server" />
will be rendered as
<input name="ctl00$ContentPlaceHolder1$TextBox1" type="text"
on the client side, thus breaking any script referencing TextBox1 with, for example, getElementById(‘TextBox1′).
This behavior is built inside asp.net and is triggered not only when using databoud controls (where it would be necessary to prevent having multiple controls with the same name, for example inside a datagrid), but it is also triggered when using Master Pages, where having multiple controls with the same name is almost impossible.
So, how do we code around it?
The client-side hack
While these methods will work for most projects, they still present a couple of issues:
- Generally quite hard to mantain, especially the first hack
- Code bloat. While bandwidth is costantly becoming less of an issue, the generated page will have elements with horribly long declarations
- It breaks existing scripts. You’ll have to re-check and re-code most of the scripts that should already work
- Extra workload on the client. Todays PCs are fast, so this is less of an issue, however it is something you should consider if you have a lot of asp.net controls in your page.
Let’s face it: the problem is really on the server, not on the client. Asp.net should not generate those horribly long IDs in the first place, unless we want to. So the best solution is a server-side hack.
How does asp.net know whether or not it should generate a unique id for our controls?
Every WebControl in asp.net exposes a property called (I’m sure you guessed it) NamingContainer, which tells asp.net what’s the parent control for our textboxes, labels and so on. All we have to do in our code is create a new class that will inherit the control we want to "sanitize" and hide that property from asp.net. Better said in code than in words:
/// A TextBox control without ugly IDs
public class TextBox : System.Web.UI.WebControls.TextBox
public override Control NamingContainer
Then, in our pages, all we have to do is register our new set of controls, like this:
<%@ Register TagPrefix="wtd" Assembly="app_code"
and replace every instance of the default asp controls with our own set of controls (a simple find&replace will do the trick), so
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<wtd:TextBox ID="TextBox1" runat="server"></wtd:TextBox>
And that’s it! Our textbox will now be rendered like this:
<input name="TextBox1" type="text" id="TextBox1" />
As pointed out by some of you in the comments, this method does not work in a traditional post-back scenario. See my new article for a possible solution to this problem.