If you're using Master Pages
in ASP.NET and trying to resolve <script> or <style> references in the page head, you may have
run into this this show-stopper. If so, here's why it's happening, and here's
an easy way to fix it.
The Problem
The Solution
If you've worked with ASP.NET Master Pages, you've no doubt
taken advantage of automatic URL re-basing within the HtmlHead control. This bit of ASP.NET magic
helps assure that a content page has access to the correct resources:
<head id="head1" runat="server">
<title>My Page</title>
<link href="css/common.css" rel="stylesheet" type="text/css" />
</head>
When you add the runat="server" attribute to the <head> tag, the runtime treats it as an HtmlHead
control. The HtmlHead control has the ability to parse child <link> controls contained withing it, so that
when a content page is rendered, the URL in the href attribute points to the correct file.
Pretty sweet.
The Problem
Now, let's say you also want to place a reference to an external
JavaScript file in the <head>, so
you do this:
<head id="head1" runat="server">
<title>My Page</title>
<link href="css/common.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="javascript/leesUtils.js"></script>
</head>
Naturally, you might assume that URL re-basing will help you out
here, but you'd be mistaken. Unfortunately, the runtime doesn't automatically
re-base URLs in <script> or <style> tags, so any page not in the same directory as your master
page won't find your script or CSS files. Bummer.
Soooo... you figure if HtmlHead won't resolve your URL
automatically, you'll wrestle that sucker manually by using the Control.ResolveUrl method. ResolveUrl can take a URL that
is relative to the root of the application and correct it for the current
request path. You'd then use a code block to substitute the correct URL at
runtime:
<head id="head1" runat="server">
<title>My Page</title>
<link href="css/common.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="<%= ResolveUrl("~/javascript/leesUtils.js") %>"></script>
</head>
But then, when you run the page, you get the following System.Web.HttpException :
The Controls collection cannot be modified because the control
contains code blocks (i.e. <% ... %>).
Ouch. As we can see, page headers don't play nice with code
blocks. That's no fun. What to do?
The Solution
I've seen various solutions for this published before. Some will
tell you to simply move the code block to the form or to a ContentPlaceHolder,
which requires you to move the tag out of the header (and which will fail if
you populate the placeholder in a content page). Other solutions involve
dynamically creating elements and adding them to the HtmlHead.Controls collection at runtime. However, I've
come up with something I find a lot easier and more straightforward -- while
leaving the <script> tag in the header where it belongs.
First, start the code block with <%#
instead of <%=
:
<head id="head1" runat="server">
<title>My Page</title>
<link href="css/common.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="<%# ResolveUrl("~/javascript/leesUtils.js") %>"></script>
</head>
This changes the code block from a Response.Write code block to
a databinding
expression. Since <%# ...
%>databinding expressions aren't code blocks, the CLR won't
complain. Then in the code for the master page, you'd add the following:
protected void Page_Load(object sender, EventArgs e)
{
Page.Header.DataBind();
}
The DataBind method evaluates all the databinding
expression(s) in your header at load time, and you're good to go. No muss, no
fuss, no ring around the collar.
No comments:
Post a Comment