ASP.NET 2.0 and MCMS – glitches with themes

Themes are a very nice feature in ASP.NET 2.0 to allow personalization and customization of a site very easily. Even without recompile an web administrator would be able to change the look and feel of a site completly by just changing the theme in the web.config (e.g.).

So it would be great if you could use Themes for your MCMS website, wouldn’t it?

Unfortunatelly it seems the developers of ASP.NET 2.0 did not have systems like MCMS in mind when they implemented this feature: the way they generate the links to the resources in the Themes folder does not work when an ISAPI filter rewrites the incoming URL as the links are implemented as relative links. E.g. a stylesheet inside a themes folder will be inserted into the rendered output of a webform in the application root in the following way:

<link href=“App_Themes/Blue/StyleSheet.css” text=“text/css” rel=“stylesheet” />

The above tag works perfectly fine inside a normal ASP.NET web application.


Assume a ASP.NET 2.0 web form project in the following virtual path:


The browser will request Webform1.aspx using the following URL: http://servername/WebFormProject/Webform1.aspx. The browser will now resolves the relative stylesheet link as http://servername/WebFormProject/App_Themes/Blue/StyleSheet.css which is correct.

Now assume the same situation for a MCMS template project:


If the browser would request the Template file Template1.aspx in the same way Webform1.aspx was accessed anything would be fine. But MCMS hides the details of the template file behind a friendly URL of a Posting!

So lets assume you have a posting Homepage in a Marketing channel:


Now the browser will request the Homepage template using the following URL: http://servername/Marketing/Homepage.htm. The browser now resolves the relative stylesheet link as  http://servername/Marketing/App_Themes/Blue/StyleSheet.css which is NOT correct!

To allow relative links to work correct with MCMS Microsoft implemented a feature in the RobotMetaTags control to which renders a <base…> tag that tells the browser which URL to use as prefix for relative links. So in all situation where a <base…> tag can be used the relative link will work correct.

Unfortunatelly there are a couple of situations where the <base…> tag cannot be used. The main reason for this is that a <base…> tag has to contain the full URL including the protocol and the servername. For the Homepage posting above the <base> tag would look as follows:

<base href=“http://servername/Marketing/Homepage.htm”>

In a hosting scenario where a publishing firewall or proxy forwards the request to an internal web server the servername in the <base…> tag will now hold the internal servername rather than the DNS name entered in the browser. If this <base…> tag is now sent back to the client then the client will most likely not be able to resolve this internal name in the <base…> tag and the request to the resources will fail. So in a hosting environment either a proxy or firewall that can do content rewrite is required – means the firewall needs to adjust the link in the <base…> tag to reflect the external DNS name – or the base tag has to be disabled.

So in case you can’t use the <base…> you need to have a different way to ensure that the App_Themes folder is accessed with the correct URL. As ASP.NET has no way to configure the way it generates the URL to the themes folder we need to modify this URL after ASP.NET has generated it. But before the page is being rendered. This can be done using a custom HttpModule that registers a new PreRender event to do the modification.

This new PreRender event will have to correct the link

<link href=“App_Themes/Blue/StyleSheet.css” text=“text/css” rel=“stylesheet” />

as follows:

<link href=“/TemplateProject/App_Themes/Blue/StyleSheet.css” text=“text/css” rel=“stylesheet” />

This new link will work in all situations as it

  1. does not contain the server name
  2. is not a URL relative to the template project

The HttpModule below will do the job and will adjust the reference to the “App_Themes” folder:

using System;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using Microsoft.ContentManagement.Publishing;
using Microsoft.ContentManagement.WebControls;

namespace StefanG.ASPNET20_HttpModule

    public class ASPNET20_ThemeCorrection_HttpModule : IHttpModule 
        public void Init(HttpApplication httpApp) 
            httpApp.PreRequestHandlerExecute += new EventHandler(this.OnPreRequestHandlerExecute);

        public void Dispose() 
            // Nothing to do.

        public void OnPreRequestHandlerExecute(object sender, EventArgs e) 
            HttpContext ctx = ((HttpApplication)sender).Context;
            IHttpHandler handler = ctx.Handler;

                if (CmsHttpContext.Current.ChannelItem != null)
                    ((System.Web.UI.Page)handler).PreRender += new EventHandler( this.OnPreRender );
                // this will happen if the request is in the middle of an expired forms authentication login
                // we just ignore this.

        public void OnPreRender(object sender, EventArgs eventArgs)
            Page currentPage = sender as Page;
            HtmlHead pageHeader = currentPage.Header as HtmlHead;
            if (pageHeader != null)
            foreach (Control control in pageHeader.Controls)
                HtmlLink link = control as HtmlLink;
                if (link != null && VirtualPathUtility.IsAppRelative(link.Href))
                    link.Href = VirtualPathUtility.ToAbsolute(link.Href);



  1. Come avevamo anticipato con un precedente post, ASP.NET 2.0 sar&#224; supportato con il SP2 di MCMS, ma con…


  2. Now after MCMS 2002 SP2 has been released I would like to point you again to an article series I have…


  3. Dude you are the man,  thanks,  this fix did it i changed the RenderBaseHref=false then added the reference in the web config to the new class

    <add type="HttpModules.ASPNET20_ThemeCorrection_HttpModule"name="ASPNET20_ThemeCorrection_HttpModule"/>

    and bam worked like a champ.


  4. It really work marvellously!! saved a lot of time. Thanks Stefan.



Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.