As Ive posted before, I LOVE the ~ shortcut in ASP.NET. It allows you to use virtual absolute paths when developing your site.
One of the problems I often run into is when I am setting a path for something in a non ASP.NET file, such as a css file. For instance, I want to set the background-image attribute for a style in a css file, but unless I use a relative path, Im screwed. I usually would end up just adding the background-image attribute directly to the tag instead of putting it into a css file. Ill be the first to admit this is a lame workaround.
So once again, my frustration-threshold has been reached and I finally decided to do something about it. My result is a custom control that is used to add the
Instead the typical way of linking a stylesheet to your page:

<link rel="stylesheet" href="/stylesheet.css" />
I now use this:

<web:CssLink rel="stylesheet" Runat="server" href="~/stylesheet.css"/>
The CssLink control inherits directly from then HtmlLink control. Essentially, only 1 thing has changed. On Render, the control creates a duplicate resolved stylesheet. In this case, the resolved stylesheets fie name is stylesheet_resolved.css. It replaces all Virtual Application paths with the appropriate absolute application path. It then modifies the Href property to link to the new resolved stylesheet.
Of course we wouldnt want to do this on each request, so once the file has been resolved, the control will... update the Last Modifed Date attribute of the real css file to 01/01/1900. On Each subsequent request, the control will then ensure the Last Modified Date is still 01/01/1900. If it is, nothing happens. If the file was updated or replaced, thus changing the Last Modified Date, then the resolved file is deleted & recreated. See next paragraph.
Updated - 12/29/2005 8:58:23 PM
Per Jon's suggestion, the control will now compare the last modified dates of both the real css file and the resolved css file, assuming it exists. If the real css file's last modified date is greater than the resolved css file's last modified date, it's time to recreate the resolved css file. - Thanks again Jon for the suggestion.
To ensure the css file is being resolved properly when it's suppose to and also when it's not, turn trace on and check the line items.

public class CssLink : HtmlLink
{
public CssLink()
{
}
protected override void Render(HtmlTextWriter writer)
{
UpdateFile();
base.Render(writer);
}
protected void UpdateFile()
{
string absRealPath = this.Page.Server.MapPath(this.Href);
string hrefResolvedPath = this.Href.Replace(".css", "_resolved.css");
string absResolvedPath = this.Page.Server.MapPath(hrefResolvedPath);
this.Href = hrefResolvedPath;
FileInfo fiReal = new FileInfo(absRealPath);
FileInfo fiResolved = new FileInfo(absResolvedPath);
if(!fiReal.Exists)
return;
if(fiResolved.Exists && fiReal.LastWriteTime < fiResolved.LastWriteTime)
{
this.Page.Trace.Warn("CSS Update", "CSS file already up to date");
return;
}
this.Page.Trace.Warn("CSS Update", "Begin update CSS file with resolved application path");
if(fiResolved.Exists)
fiResolved.Delete();
string contents = null;
using(FileStream fs = new FileStream(absRealPath, FileMode.Open, FileAccess.Read))
{
using(StreamReader sr = new StreamReader(fs))
{
contents = sr.ReadToEnd();
sr.Close();
}
fs.Close();
}
using(StreamWriter sw = new StreamWriter(absResolvedPath, false))
{
string relAppPath = HttpRuntime.AppDomainAppVirtualPath;
if(!relAppPath.EndsWith("/"))
relAppPath += "/";
sw.Write(contents.Replace("http://www.csharper.net/", relAppPath));
sw.Close();
}
fiReal = null;
fiResolved = null;
this.Page.Trace.Warn("CSS Update", "End update");
}
}
The only caveat to this approach is that proper permissions MUST be given to the directory in which the css files reside. In my case, that wasnt a big issue because I keep all my stylesheets in a dedicated directory. I did run into file access problems when I deployed this to my server. I gave ASP_NET & IIS_USER proper permissions with no luck. The only thing that ended up working for me was to give the Everyone account delete/modify privs. While I absolutely HATE giving this much control away, its a minor issue since again, I have a dedicated stylesheet directory.
I welcome any suggestions as to how to improve this approach. Keep in mind, the control will need to know when the real css file has been updated so it can create a resolved version. Also keep in mind, no updates to the original css file can take place that content needs to be preserved.