29 November 2007

ASP .NET 2.0 User Controls

I'm struggling to find a good use for User Controls.

All I wanted to do was wrap some standard web controls (like a login box) in a pretty container. .NET's default theming is insufficient for this, as it allows you to theme certain properties, but doesn't allow you to customize the control to any large degree. For instance, what I wanted to do was create a pretty table-based box, which is about 19 lines of normal HTML/ASP:


<table class="prettyBox" cellpadding="0" cellspacing="0" border="0">
<tr>
<td class="nw"><br /></td>
<th class="n"><asp:Label ID="title" runat="server" Text="Label"></asp:Label></th>
<td class="ne"><br /></td>
</tr>
<tr>
<td class="w"><br /></td>
<td class="contents">
<asp:PlaceHolder ID="content" runat="server"></asp:PlaceHolder>
</td>
<td class="e"><br /></td>
</tr>
<tr>
<td class="sw"><span></span></td>
<td class="s"><span></span></td>
<td class="se"><span></span></td>
</tr>
</table>


And be able to specify them like so:


<uc:prettyBox ID="pb1" runat="server" Title="Log In">
<asp:Login ID="login1" runat="server"></asp:LoginControl>
</uc:prettyBox>


or even:


<uc:prettyBox ID="pb1" runat="server" Title="Log In">
<ContentTemplate>
<asp:Login ID="login1" runat="server"></asp:LoginControl>
</ContentTemplate>
</uc:prettyBox>


I was able to get the latter to work to a very limited degree, but it had enough limits that it wouldn't work for me.

First, I looked at a templated control from MSDN. Unfortunately, Microsoft's idea of a templated user control revolves more around the data than the UI, so this example is no help.

Another way is found here, and it correctly renders the controls inside the PlaceHolder, but in VS 2005, the designer refuses to keep tabs on any controls inside my user control, so without some manual accounting on my part (i.e. declaring the controls as page variables manually), I lose programmatic access to any controls inside the user control, which is unacceptable.

I found that I could user Mr. Seder's code and make the code designer behave correctly by putting these lines over the class declaration of the user control:


[Designer("ContainerControlDesigner")]
[ParseChildren(false)]


But this caused the ContentTemplate to be completely ignored, so all the controls inside my user control would render after the pretty box rather than inside it's PlaceHolder.

The closest working thing I found was this, but that was a lot more hassle than I wanted just to save myself 15-17 lines of code for each box, and all the controls were created dynamically. It's essentially a server control, which defeats the whole point of a user control that you can see more easily from a visual design standpoint.

I find it very frustrating that I can't make a simple user control that wraps HTML around other controls in ASP .NET 2.0. Probably what I'll end up resorting to is making two user controls, one called prettyBoxStart with a title attribute, and one called prettyBoxEnd, both of them being unary tags:


<uc:prettyBoxStart ID="pbs1" runat="server" Title="Log In" />
<asp:Login ID="login1" runat="server"></asp:LoginControl>
<uc:prettyBoxEnd ID="pbe1" runat="server />


It's pretty lame, and it's barely better than an include, but at least it works, unlike all that user control junk.

No comments: