Paulo Morgado

.NET Development & Architecture

Recent Articles

view all



Recent Readers

Visitor Locations

Visitor Locations


The opinions and viewpoints expressed in this site are mine and do not necessarily reflect those of Microsoft, my employer or any community that I belong to. Any code or opinions are offered as is. Products or services mentioned are purchased by me, made available to me by my employer or the manufacturer/vendor which doesn't influence my opinion in any way.

Making PathInfo And ASP.NET Themes Work Together

Updated on 2008.07.28 – The code was done in a hurry and, talking to my friend Luís, I noticed that I had forgotten to make a case insensitive comparison and that the code was not so obvious. So, I updated the code and added an explanation.

On my last post I wrote about the problem that arises when we try the use path infos and ASP.NET Themes and Skins together.

But most of the times you don’t care about the why you can’t. You just want to know how you can.

The way I see it, the right solution would be to render the URLs for the stylesheets rooted.

But since I can’t do that, the next best thing is the serve the wrongly addressed request properly.

But how can we do that?

The only way I could come up with, was an HTTP Module:

public class AppThemesModule : global::System.Web.IHttpModule
    private const string LocalThemesFolderName = "/App_Themes/";
    private static readonly int searchStartIndex;
    private static readonly int minimumLenghtForSearch;

    static AppThemesModule()
        int searchStartIndex = System.Web.HttpRuntime.AppDomainAppVirtualPath.Length;

        AppThemesModule.searchStartIndex = ((searchStartIndex == 1) ? 0 : searchStartIndex) + 2;

        AppThemesModule.minimumLenghtForSearch = AppThemesModule.searchStartIndex + AppThemesModule.LocalThemesFolderName.Length;

    #region IHttpModule Members

    public void Dispose()

    public void Init(System.Web.HttpApplication context)
        context.BeginRequest += HttpApplicationBeginRequest;


    void HttpApplicationBeginRequest(object sender, System.EventArgs e)
        System.Web.HttpApplication httpApplication = sender as System.Web.HttpApplication;

        string path = httpApplication.Request.Path;
        if (path.Length > searchStartIndex)
            int appThemesStartIndex = path.IndexOf(AppThemesModule.LocalThemesFolderName, searchStartIndex, System.StringComparison.OrdinalIgnoreCase);
            if (appThemesStartIndex > 0)
                httpApplication.Context.RewritePath("~" + path.Substring(appThemesStartIndex));

The code starts by initializing the static read-only field searchStartIndex with the start index of the search for the /App_Themes/ pattern. If the length of the application’s virtual path is 1, that means that it’s the root of the site and search start index is 0 instead of 1; otherwise the search start index will be the length of the application’s virtual path. 2 is added because there is no need to start searching the path just after the application’s virtual path (if the pattern was found just after the application’s virtual path, no replacement would be needed).

Than, the static read-only field minimumLenghtForSearch is initialized with the minimum length of the path to search for the pattern. There is no need to search for the pattern on paths shorter than the search start index plus the length of the pattern because, if found, no replacement would be needed.

Besides registering the module, you’ll have to configure your virtual directory so that all the files to be served out of the themes are handled by a StaticFileHandler.


rajbk said:

You could also root the url by using the following code in your base page (not tested completely). Similar code could be used in the master page also:

protected override void OnInit(EventArgs e
   foreach (Control headerControl in this.Page.Header.Controls)
       HtmlLink link = headerControl as HtmlLink;
       if (link != null)
           if (link.Href.StartsWith("~/App_Themes"))
               link.Href = Request.Url.GetLeftPart(UriPartial.Authority) +
# July 24, 2008 3:01 PM

Paulo Morgado said:

Hi Raj,

Great idea. Let me think about it for a while.

# July 24, 2008 5:47 PM