Extending Master Pages
Note: this entry has moved.
During last March I was writing a chapter for a soon to be released Wrox Professional book on Advanced ASP.NET. As you may already know, Wrox is gone by now, so my chapter (and the whole book) never made it to the public L
During that time (late-night time…) I wrote an entire chapter about Page Templates/Visual Inheritance/YourFavoriteNameGoesHere in ASP.NET. I ended up with a 50 pages chapter with more of its content dedicated exclusively to the Master Pages technique (coded by Microsoft’s David Ebbo) and how to extend it.
Although Master Pages was just a quick sample that David put together to demo a basic concept, it is, in my opinion, the best way to go if you’re looking to implement page templates in your ASP.NET application. But, as with any quickly put together sample, it leaves some room for improvement and fixes. I will summarize a couple of them here hoping that if you’re actually using Master Pages or considering using it you could benefit by knowing about them.
NOTE: if you’re not familiar enough with Master Pages or any other template technique, I recommend you to take a look at fellow MVP Paul Wilson’s articles on the subject before continuing this reading.
You’re already familiar? Ok then… read on J
Better integration with the ASP.NET parser
This means moving some code from Control.AddParsedSubObject to a ControlBuilder for executing code only once at parsing time. There is also room for adding a couple of new checking’s (i.e. enforcing attributes to be set).
Couple of fixes
Let’s take a look at the following fragment for ContentContainer.OnInit:
protected override void OnInit(EventArgs e) {
if (MasterPageFile == null)
throw new Exception("You need to set the MasterPageFile property");
// Load the master page
Control masterPage = Page.LoadControl(MasterPageFile);
foreach (Content content in _contents) {
// Look for a region with the same ID as the content control
Control region = masterPage.FindControl(content.ID);
if (region == null) {
throw new Exception("Could not find matching region for content with ID '" + content.ID + "'");
}
.
.
.
}
There are two main pitfalls (marked in bold) to watch out for in the above code:
1. As you already know the Control.FindControl method when called with a simply ID will search into the current naming container for the specified control; this will break when your Region controls are contained in a different naming container than the usercontrol itself, i.e:
<owc:Region runat=”server” id=”one” />
<owc:MyOwnNamingContainer runat=”server”>
<owc:Region runat=”server” id=”two” />
</owc:MyOwnNamingContainer runat=”server”>
<owc:Region runat=”server” id=”three” />
2. Checking against region == null it’s not enough to guarantee correct usage of Master Pages. For example it could happen that a page developer while creating a new page specified a Content control with an ID that has no correspondent Region control but matches some other control type; needless to say the check won’t fail and we’ll be in trouble. So we should be checking region against null plus that it is an instance of a Region type.
Support for inheritance between templates
This is by far the most powerful feature I’ve added to Master Pages. There is some real power (code reuse, simplified maintenance, etc) you could benefit from when being able to inherit between templates. The code to add this feature is quite straightforward, basically a modified ContentContainer that is aware of inheritance and a couple of support methods to walk the inheritance chain and setup Region controls appropriately.
Master Pages as an application-level feature
Coding a config section for Master Pages that you could use to easily setup it for your particular web application is definitively useful. With settable attributes like BaseUrl that would allow you to easily change the location where to look for templates.
VS.NET integration
Due to the fact that the page parser is currently not extensible, it is required that you manually add some repetitive declarations to any page that uses templates and also to your templates files. This tends to be a really boring and repetitive (did I already mentioned that?) task. I’ve tackled this by coding a couple of VS.NET wizards and templates (VS.NET templates this time!) that create the necessary skeleton code thus saving you from boring and rep… typing.
Other features I’ve coded are support for: I18N, design-time, programmatically handling Master Pages and something else I can’t remember right now J… I could address these features in a future post if there is enough interest in them.