Tuesday, June 27, 2006

Making Dynamic CSS content with ASP.Net

I had the situation where there was a need to modify a css file on the fly.
It is like the poor man's Theme but it is slightly different.
Let's say you want the H1 background color to be configurable by the user. This
can be achieved via the Profile and by some other means.
For the sake of this demo, I will use the Session state. Let's say I have
a stylesheet like this

H1 { background-color:Red; }

in a file called StyleSheet.css in the application root.

What I want to do is to replace the "Red" with "Blue" for instance according to
some value in the Session. The first naive approach is to read the css in memory and
do a string replace. This is very brittle and just a bad hack. Some may get smarter
and use a regular expression.

The approach I am taking here is the following. We already have something in ASP.Net
that does text substitution! Yep, an aspx page does just that with server side
code injection. So, conceptually what we want is this:

H1 { background-color:<%= ColorManager.Color %> ; }

Where ColorManager is some class we write to get the color from the Session.

As you are aware, css does not support this kind of syntax. Can we just fool the
system to do this? If you are in love with the ASP.Net pipeline, you can....

Step 1 :
We rename StyleSheet.css to StyleSheet.aspx and make a 'page' like this

<%@ Page Language="C#" %> H1 { background-color:<%= ColorManager.Color %>; }

Step 2:
We make ColorManager.cs and put it in App_Code. A static class will do.
For now you can just stub this out like this

public static class ColorManager
{
static public string Color
{
get
{
if (HttpContext.Current.Session["H1COLOR"] == null)
return "red";
return (string)HttpContext.Current.Session["H1COLOR"];
}
set
{
HttpContext.Current.Session["H1COLOR"] = value;
}
}
}

Step 3:
We need to add a link to our dynamic stylesheet from a test page.
The perfect spot to put it is in the <HEAD>.

<head runat="server"> <link rel="stylesheet" href="StyleSheet.aspx" type="text/css" /> </head>

The href is pointing to our StyleSheet.aspx. The browser could not care less about the
address here as long as the type is correctly set to "text/css". The browser will
interpret the web response as a css text (which it is).

Here is the kicker, you can still use Themes with this approach. By using Themes, the
framework will add the css from the Theme directory as <link> in the <HEAD runat="server">
after our own dynamic css.

Here is a test page to tie all this together. Each time you click on the button
the H1 color changes and you can even see the dynamic css by clicking on the "See CSS".

So much power in ASP.Net. The question is how we can take advantage of it.

CF

default.aspx:

<%@ Page Language="C#" AutoEventWireup="true" %>
<script runat="server">
protected void Bt1_Click(object sender, EventArgs e)
{
ColorManager.Color = (ColorManager.Color == "red") ? "blue" : "red";
}
</script>
<html xmlns=" http://www.w3.org/1999/xhtml" >
<head runat="server">
<link rel="stylesheet" href="StyleSheet.aspx" type="text/css" />
</head>
<body>
<form id="form1" runat="server">
<H1>Hello World!</H1>
<asp:Button runat="server" ID="Bt1" OnClick="Bt1_Click" Text="Change Color" />
<asp:Hyperlink Text="See CSS" runat="server" ID="ToCSS" NavigateUrl="~/StyleSheet.aspx" />
</form>
</body>
</html>

4 comments:

Anonymous said...

I would suggest a generic handler ... .ashx-file. Then change the Response.ContentType to "text/css" and voila ... it's all generic :)

Anonymous said...

This is a great article. But can you give a visual basic version of this. I am using Visual Basic and I am not familiar with C#. Thanks

Anonymous said...

Not familiar with C#?.. How hard can it be to translate between vb and c#? - .Net certified Architect

Anonymous said...

Excellent. Thank you for sharing. You made my life a little better.