Main Points

User controls are a handy way of consolidating code. In that respect the concept is similar to a master page. Both take chunks of code that will be used on multiple pages and consolidate it in one location. However, where the master page is a template that uses "editable" content placeholder regions inside it to allow you to pull custom code from a content page. A user control on the other hand is a contiguous block of code that's pulled out of the page and inserted into a *.ascx file (which is referenced by the original page). It's a more object oriented approach though there are more advanced implementations; advanced enough that these blocks will be user controls for the foreseeable future until I can learn enough to determine if there's a real alternative.

User Control Development

I started breaking out the pretty stable pieces of code into user controls (.ascx files). Primarily this will help make the website more object oriented and the pieces reusable. I started with the main nav bar at the top of the page. I simply cut the HTML for that navbar and pasted it into a text file. Along with the code, the .ascx file has an @Control directive at the top of the file (just like a webform has an @Page directive). It looks like this.

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="SiteSpecificNavBar.ascx.cs" Inherits="SiteSpecificNavBar" %>

Except for replacing the word Page with Control, this is identical to a normal directive. We identify the language of the code behind, how events are wired (in this case automatically), identify the code behind file and the partial class that is built by this file and it's code behind. What's different obviously, is that the standard page HTML is absent, because this is effectively a more sophisticated Server Side Include. Finally, I placed this component file and it's code behind in a new Components folder. Back in original page, we have to register where the component is so it can be found and included on the page.

<%@ Register Src="~/Components/SiteSpecificNavBar.ascx" TagName="SiteSpecificNavBar" TagPrefix="echron" %>

The @Register identifies the path to the component file. It also includes two additional attributes that are easily understood if we compare them to ASP.NET controls. When you want to insert .NET controls, even one as simple as a label, they follow a specific form, e.g. <asp:Label>. The name of the tag obviously is Label, however, it also has a prefix, asp, that identifies it as a .net control. This avoids confusion if a company has built it's own control called label. The server adding the controls can still identify one from the other because all .net controls have the asp prefix. In registering our navbar component, I've defined the tag to be "SiteSpecificNavBar" and the tag prefix to be "echron". Now we have defined the name of the tag we need to call in order insert one of these onto the page. At the spot where the navbar code was removed from the page, this tag is now inserted.

<echron:SiteSpecificNavBar ID="compNavBarSS" AllowDuplicates="false" runat="server" />

The <echron:SiteSpecificNavBar> tag now inserts the code into our ASP.NET master page, and we set it's ID. In this case, the site specific navbar should only be allowed once on the page so a special AllowDuplicates attribute is set to false and the tag is told to run on the server. We can now insert this navbar into multiple pages, multiple master pages and still keep all the code in one location making it much more manageable, understandable, and easier to maintain. I like it so much, I'm trying to break out everything I think is worthwhile. So far, I've managed to move the entire footer into user controls. The Site specific nav bar and the site wide nav bar are the first two, but they were swiftly followed by the page links and the contact form. There may be more to come as well. I'm trying to figure out the best way to do the authorship and posting sections.

That didn't work, although I've been able to pull out one more small component. There are several tags in the head of the document which I grouped together to make a component. This is where I received an unpleasant surprise, content placeholder tags, <asp:contentplaceholder>, can't be pulled into a component. They can only be inserted directly into a *.master file, a master page. So the authorship and posting along with almost everything except the footer can't be moved into a user control. I reorganized the head section to consolidate the placeholders at the end of the head section and swept the other four tags in a small component. Everything else I looked at that's still in the master page has content placeholder tags running around inside them somewhere. So that's as far as I can get with user controls for the present.

Skip to Main Points

User Control Name Mangling

Name mangling is not an area where I'm completely happy with ASP.NET. I can see the general purpose for a web application that's massive and has many people working on it. However, it's certainly much higher end work than anyone I know is doing. It's had surprisingly little impact on the page however. At first I was confused by this, but most of my page is basic XHTML tags. They're not processed on the server so they aren't looked at much less mangled. The contact form is the only portion of the page where I used .NET controls so I can process email. So the output for the actual form components came out looking like this.

<ol>
<li>
<label for="tbEmail">Email (optional)</label>
<input name="ctl00$compContactForm$tbEmail" type="text" id="ctl00_compContactForm_tbEmail" class="tbEmail" />
</li>
<li>
<label for="tbComments">Questions and Comments</label>
<textarea name="ctl00$compContactForm$tbComments" rows="2" cols="20" id="ctl00_compContactForm_tbComments" class="tbComments"></textarea>
</li>
<li>
<input type="submit" name="ctl00$compContactForm$btnEmail" value="Email Us!" id="ctl00_compContactForm_btnEmail" class="btnEmail" />
<span id="ctl00_compContactForm_lblSent"></span>
</li>
</ol>

Each input, textarea, and span retain their original name and id as a root, however each has a prefix attached which identifies the file it came from. These all come from the ContactForm.ascx. Each id attribute has the prefix, ct100_compContactForm_; the ct100 indicates that the element is located in the ASP.NET master page, the compContactForm is the name of the Contact Form's class, and the underscores (_) are used as separators. Each name attribute has a virtually identical prefix except that the original name, ct100, and compContactForm are separated by dollar signs ($). Note that the class attribute is unaffected.

The most obvious problem that arises here is that the <label> tags no longer match up with the form field they're supposed to identify. Worse, it's been a while since I worked with labels so I couldn't remember whether the for attribute was tied to the id or the name attribute. Previously those attributes had been the same, but now the ids contain underscores and the names contain dollar signs. A quick Google search turned up to solutions. One, you can add the form field inside the <label></label> tags, eliminating the need for a for attribute. Two, the for attribute identifies the id of the form field it goes with, and I chose this option.

<li>
<label for="ctl00_compContactForm_tbEmail">Email (optional)</label>
<input name="ctl00$compContactForm$tbEmail" type="text" id="ctl00_compContactForm_tbEmail" class="tbEmail" />
</li>
<li>
<label for="ctl00_compContactForm_tbComments">Questions and Comments</label>
<textarea name="ctl00$compContactForm$tbComments" rows="2" cols="20" id="ctl00_compContactForm_tbComments" class="tbComments"></textarea>
</li>

I also lost all of the styling on the form elements, predictably. So I went back to the stylesheet and updated all the references to the original ids with the "ct100_compContactForm_" prefix. After that, everything worked like it did before. However, when I try to use the form it becomes obvious that one thing that did not transition into this master page test is the code to run the contact form. Ooops. I believe it "ought" to go into the ContactForm.ascx.cs file. I will have to try that out and find out how that works. Since it's a code behind to the user control (.ascx file) I would think I can use the "premangled" ids, but we'll see. If I need to change them, so be it.

Skip to Main Points
Continue to 8. Wiring It All Up -->
<---- Jump back to Master Page Development