Introduction
Users want flexibility in the websites they visit; they want to define what content they see, and how it is shown. Website developers want to give them that ability, but need to balance it against maintainability. Using Cascading Style Sheets (CSS), the developer can specify certain base layouts, or themes, and allow the user to change these at runtime. However, creating separate files for all possibilities is just not reasonable. A better way would be to change certain CSS values at runtime based on settings specified by the user, yet CSS does not have variables that can be evaluated at runtime.
Solution
To get around the problem of not having variables in CSS, one must read the CSS file and replace the given values at runtime. The good news is that in ASP.NET, this is a relatively easy task.
body
{
background-color:#BG_COLOR#
}
Generic HTTPHandler
Using Visual Studio 2005, you can easily add a Generic HTTPHandler.
This will create an ashx file and add it to your project. This file implements the IHTTPHandler
interface, with its one and only method, ProcessRequest
, and includes the WebHandler
page directive.
<%@ WebHandler Language="C#" Class="Handler" %>
The .NET Framework treats these files as HTPPHandlers without the need to register them in the <httpHandlers>
section of the web.config file.
<%@ WebHandler Language="C#" Class="CSSHandler" %>
The code-behind:
using System;
using System.Web;
using System.Configuration;
public class CSSHandler : IHttpHandler
{
public void ProcessRequest (HttpContext context)
{
context.Response.ContentType = "text/css";
string File = context.Request.QueryString["file"];
string Path = context.Server.MapPath(File);
if(System.IO.Path.GetExtension(Path) != ".css")
context.Response.End();
if(!System.IO.File.Exists(Path))
context.Response.End();
using( System.IO.StreamReader css = new System.IO.StreamReader(Path) )
{
string CSS = css.ReadToEnd();
CSS = CSS.Replace("#BG_COLOR#",
ConfigurationManager.AppSettings["BGColor"]);
context.Response.Write(CSS);
}
}
public bool IsReusable
{
get { return false; }
}
}
As we can see, the ProcessRequest
method simply opens the file specified on the query string, reads it, and uses string replace to add the value for the variable that has been specified in the web.config file. Not much to it.
<link rel="Stylesheet" href="CSSHandler.ashx?file=default.css" />
<appSettings>
<add key="BGColor" value="Red"/>
</appSettings>
Limitations
Using a generic webhandler has a disadvantage in that you must specify the style sheet to parse. This breaks down when using ASP.NET 2.0 Themes, however, because any stylesheet placed in the theme folder will automatically be linked, no need to manually add it to your web pages. Although you can manually add each one, it isn't a very maintainable model.
Better solution
A better solution is to create a custom HTTPHandler and add it to the httpHandlers
section of the web.config file.
<httpHandlers>
<add verb="*" path="*.css"
type="CustomHandler.CSSHandler, CustomHandler"/>
</httpHandlers>
The code:
public class CSSHandler : IHttpHandler
{
#region IHttpHandler Members
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(System.Web.HttpContext context)
{
string File = context.Request.PhysicalPath;
using(System.IO.StreamReader reader = new System.IO.StreamReader(File))
{
string CSS = reader.ReadToEnd();
CSS = CSS.Replace("#BG_COLOR#",
ConfigurationManager.AppSettings["BGColor"]);
context.Response.Write(CSS);
}
}
#endregion
}
The only difference from the previous example is that the CSS file to parse is obtained from the context.Request.PhysicalPath
property. Since the handler is registered for CSS files, it will process any stylesheet file regardless of its location in the web project.
Conclusion
This article has hopefully shown a method that can be used to provide dynamic settings to an otherwise static file and give website users a more positive experience.