views:

577

answers:

5

For some reason by css link in a webforms master page is getting mangled by ASP.NET.

The page using the masterpage is located in /subdir1/subdir2/page.aspx

Not sure why it is happening but here is a snippet of the code:

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
    <link href="<%= MyNamespace.Helpers.UrlHelper.CssRoot %>Site.css" rel="stylesheet" type="text/css" />
    <script src="<%= MyNamespace.Helpers.UrlHelper.JavascriptRoot %>jquery-1.3.2.min.js" type="text/javascript"></script>
    <asp:ContentPlaceHolder ID="cphHead" runat="server">
    </asp:ContentPlaceHolder>
</head>

The Html output that is being created is:

<html xmlns="http://www.w3.org/1999/xhtml" >
<head><title>
    Untitled Page
</title><link href="../../%3C%25=%MyNamespace.Helpers.UrlHelper.CssRoot%20%25%3ESite.css" rel="stylesheet" type="text/css" />
    <script src="/Javascript/jquery-1.3.2.min.js" type="text/javascript"></script>
</head>

Why is this working for the script tag but mangling the link tag and not actually executing the code included. If I change the 'link' tag to be a 'script' tag (which is wrong but for testing purposes) it produces the proper html I would expect. Why is ASP.NET messing with my link tag for my Css but not the script tag for the javascript?

Is there something special about the link tag to make ASP.NET think it needs to mangle it?

A: 

Try runat="server" tags in the elements

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
    <link runat="server" href="<%= MyNamespace.Helpers.UrlHelper.CssRoot %>Site.css" rel="stylesheet" type="text/css" />
    <script runat="server" src="<%= MyNamespace.Helpers.UrlHelper.JavascriptRoot %>jquery-1.3.2.min.js" type="text/javascript"></script>
    <asp:ContentPlaceHolder ID="cphHead" runat="server">
    </asp:ContentPlaceHolder>
</head>

Edit: Do you have to use the helper object? Is it doing more than just making your links dynamic? When runat="server" is set, you can sometimes use dynamic URLs using the ~ character. You could try this instead:

<head runat="server">
    <link id="SiteCssLink" runat="server" href="~/Css/Site.css" rel="Stylesheet" type="text/css" media="all" />
</head>

Edit #2: Try injecting the URL into the link element from the Page_Load or Page_PreRender events in the code behind of the page. You will need the element to have runat="server" for this to work.

Protected Sub Page_PreRender(ByVal sender as Object, ByVal e as System.EventArgs) Handles Me.PreRender
    SiteCssLink.Href = Page.ResolveClientUrl(String.Concat(MyNamespace.Helpers.UrlHelper.CssRoot, "Site.css"))
End Sub
Chris Porter
Why would I need the runat server tag? I don't need it for the script tag so why would I need it for the link tag? Also, I don't need the accessible at the server.
Kelsey
ASP.NET Webform handling of the head tag is goofy at best. I've had luck adding that tag when working on rendering issues. This might not help, but its good practice.
Chris Porter
I just tested it by adding runat="server" to the link tag. Doesn't do anything :(
Kelsey
I could use href="~/Css/Site.css" but that really is not a solution I want to use. If for some reason I need to move the Css directory, I need to edit every single page that uses that type of a link instead of changing one function.Also, I don't like the fact that I could do it that way for the Css links ONLY. My script links would still need to be done the way I am doing it.
Kelsey
You should be able to use Edit #2 option for your script tags as well.
Chris Porter
There are a few solutions that will get things to work. My main question is WHY is this happening to just the link tag. Trying to understand what is happening in the background.
Kelsey
A: 

You could try changing

<link href="<%= MyNamespace.Helpers.UrlHelper.CssRoot %>Site.css" rel="stylesheet" type="text/css" />

to

<link href='<%= MyNamespace.Helpers.UrlHelper.CssRoot %>Site.css' rel="stylesheet" type="text/css" />

Note the single quotes on the second instance

Dave_Stott
Changing it to single quotes does nothing. It produces the exact same result.
Kelsey
+1  A: 

Perhaps a solution would be to specify your <link> and <script> tags from your master page's codebehind.

private void ConstructLinkAndScriptTags()
{

string cssTag = "<link  href='" +   MyNamespace.Helpers.UrlHelper.CssRoot + "Site.css' rel='stylesheet' type='text/css' runat='server' />";

cph.Controls.Add(new LiteralControl(cssTag));
}
p.campbell
+3  A: 

This is a separate answer based on approach and might be more what you are looking for. The reason I found for the string mangling is the HtmlLink object has internal handling of the href value during rendering. Using .NET Reflector I found an Overrides RenderAttributes method. This is the code for it:

Protected Overrides Sub RenderAttributes(ByVal writer as HtmlTextWriter)
    If Not String.IsNullOrEmpty(Me.Href) Then
        MyBase.Attributes.Item("href") = MyBase.ResolveClientUrl(Me.Href)
    End If
    MyBase.RenderAttributes(writer)
End Sub

What I believe is happening is the RenderAttributes method is being called before your helper line is being parsed and is using ResolveClientUrl against the string "<%= MyNamespace.Helpers.UrlHelper.CssRoot %>Site.css". The solution of using "~/" URL strings isn't affected by this because ResolveClientUrl is able to understand that notation.

I see two solutions for you at this point. 1) Use the Edit #2 approach of injecting the helper's URL string into the element during Page_Load or Page_PreRender, or 2) Create your own Overrriden version of the HtmlLink element that doesn't try to use ResolveClientUrl. #1 would definitely be the easier solution.

Hope this helps shed some light on the issue.

Chris Porter
Nice sleuthing, kind of borks the T4MVC template helpers as well. No love for: <link rel="stylesheet" type="text/css" href="<%= Links.Content.Site_css %>" />
Kelly Adams
I had the same problem, also because I was trying to use the T4MVC helpers. I ended up writing a HtmlHelper that outputs the whole thing. I can now do <%=Html.CssLink(Links.Content.site_css)%> public static string CssLink(this HtmlHelper helper, string cssPath) { return string.Format("<link href=\"{0}\" rel=\"stylesheet\" type=\"text/css\" />", cssPath); }
Daniel
A: 

It was not wise for ASP.NET team to make link tags auto-adjust their href attributes, but the best way I've found to deal with it is to:

  1. Dynamically insert the link tag into a Literal control.
  2. Remove the runat="server" from the head tag.