Staying On The Correct Path

In a current task where I had to consolidate the structure of a website and organize the files better, I ran across this interesting quirk.  When developing, we work in a virtual directory on our local machine, while on a real server, it runs in the root directory.  This poses problems whenever we want to use relative paths to images, style sheets, etc.

The key to fixing this is the VirtualPathUtility, which has the logic in it to determine the root of the web application and return a proper path.  I thought it would be easy to just use this object right in the markup like:

<head runat="server">
    <title></title>
    <link id="cssLinkOutside" href='<%=System.Web.VirtualPathUtility.ToAbsolute("~/StyleSheet.css")%>' rel="stylesheet" type="text/css" />
</head>

That didn’t work.  The rendered HTML turned out to be:

<link id="cssLinkOutside" href="&lt;%=System.Web.VirtualPathUtility.ToAbsolute(&quot;~/StyleSheet.css&quot;)%>" rel="stylesheet" type="text/css" />

Pretty literal.  The cause of this odd behavior is that the HEAD tag is set to runat=”server”.  If that piece is taken out, the embedded code works well.  So if you need the HEAD to be a server control – like when using themes – then what?  You can add code in the code-behind to do an attributes.add() on the LINK tag.  But having to add code in two places for such a simple need is just too much, especially when I was looking at updating a couple dozen pages.

The workaround is kind of surprising: Wrap the tag that contains dynamic code in a placeholder.

<head runat="server">
    <title></title>
    <asp:PlaceHolder runat="server" id="holder">
        <link id="cssLinkInside" href='<%=System.Web.VirtualPathUtility.ToAbsolute("~/StyleSheet.css")%>' rel="stylesheet" type="text/css" />
    </asp:PlaceHolder>
</head>

This renders the tag properly:

<link id="cssLinkInside" href='/WebTestbed/StyleSheet.css' rel="stylesheet" type="text/css" />