Al Nyveldt did it again. I’m always amazed whenever he does a screencast. He’s just so relaxed and natural when he explains tech stuff and he does it with such a confidence. I wish I had that talent.

He just made two new screencast about setting up BlogEngine.NET 1.2 for the first time. The first one is about installing it using the default XML data provider and the second is a more detailed installation guide for setting up SQL Server as a provider. They are very short and to the point. I like that. It’s always a joy to listen to people like him. Go check it out.

The first video also explains how to adjust the robots.txt file so search engines can index all your pages and posts with ease. Since Microsoft Live Search, Yahoo and Ask started supporting Google’s sitemap format, it has become somewhat painless to let those search engines index your entire website. That’s why it is important to know about the XML sitemap feature of BlogEngine.NET. The versions earlier than 1.2 also has this feature by the way.

BlogEngine.NET 1.2 only support XML and SQL Sever 2000/2005/2008 but the next version will support all standard T-SQL databases like MySQL, PostgreSQL, Oracle etc. out of the box.

I often use multiple stylesheets in a single website to keep things nicely separated. The only problem is that the client has to make multiple HTTP requests to get them all. Today, I thought it was about time I did something about it. The idea is to take all the referenced stylesheet in the <head> tag and combine them into a single reference at runtime. The only rule I had was that I couldn’t touch the way stylesheets are referenced. In other words, it had to be done only by writing C# and leave the HTML alone.

That means I should be able to import multiple stylesheets the way I normally would like so:

<head runat="server">
   <link rel="stylesheet" type="text/css" href="~/css/master.css" />
   <link rel="stylesheet" type="text/css" href="~/css/menu.css" />
</head>

Luckily, if the head tag has a runat=”server” attribute, all stylesheets are treated as HtmlControls we can reference in the code-behind. That's a prerequisite for this to work. What I needed was to things:

  1. Code to remove the stylesheets from the head tag
  2. An HttpHandler to combine all the reference stylesheets

The code

The following code removes all stylesheets from the head tag and adds a new one that points to the HttpHandler and passes the original stylesheet file names as URL parameters. It looks like this:

<link rel="stylesheet" type="text/css" href="~/stylesheet.ashx?stylesheets=~css/master.css,~/css/menu.css" />

Note that the file names are being URL encoded but that looked too messy for this example, so I just leaved them in clear text so it's easier to see what's going on. I use a custom base page so I put the following code in there. You could also place it in your master page or in every .aspx page you want this feature.

[code:c#]

protected void Page_PreRender(object sender, EventArgs e)
{
 CombineCss();
}

protected virtual void CombineCss()
{
 Collection<HtmlControl> stylesheets = new Collection<HtmlControl>();
 foreach (Control control in Page.Header.Controls)
 {
  HtmlControl c = control as HtmlControl;

  if (c != null && c.Attributes["rel"] != null && c.Attributes["rel"].Equals("stylesheet", StringComparison.OrdinalIgnoreCase))
  {
   if (!c.Attributes["href"].StartsWith("http://"))
    stylesheets.Add(c);
  }
 }

 string[] paths = new string[stylesheets.Count];
 for (int i = 0; i < stylesheets.Count; i++)
 {
  Page.Header.Controls.Remove(stylesheets[i]);
  paths[i] = stylesheets[i].Attributes["href"];
 }

 AddStylesheetsToHeader(paths);
}

private void AddStylesheetsToHeader(string[] paths)
{
 HtmlLink link = new HtmlLink();
 link.Attributes["rel"] = "stylesheet";
 link.Attributes["type"] = "text/css";
 link.Href = "~/stylesheet.ashx?stylesheets=" + Server.UrlEncode(string.Join(",", paths));
 Page.Header.Controls.Add(link);
}

[/code]

The HttpHandler

I’ve created an .ashx file that takes all the stylesheet references as a URL parameter separated by commas. It then iterates through them all, reads the .css file from disk, removes all whitespace and writes it to the response stream.

There is a serious IO overhead in this, so the HttpHandler caches the final response server-side so IO operations only take place the first time it is requested. It also adds a cache file dependency which means that whenever you change one of the stylesheet files, it reloads the cache. It also makes sure that the browsers will correctly cache the combined stylesheet by sending the correct cache headers.

Performance gains

I’ve done a test on two of my stylesheets I used for an old project. One is 7.11kb and the other is 20.2kb. That totals to 27.31kb and two HTTP requests. After I’ve implemented this feature I only have a single HTTP request and the total file size is only 13.08 because the whitespace is stripped. That’s more than half the size in kilobytes and just a single HTTP request was needed.

Implementation

Download the stylesheet.ashx file below and place it at the root of your application. If your website isn't located at the root then remember to update the references in the code above. You might also need to use the "~" when you reference the stylesheets in the head tag like you can see I did.

stylesheet.ashx (1.14 kb)