Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Getting access to the Head tag in Master Pages


:P
On this page:

I've been busily converting one of my apps to ASP.NET 2.0 as well as updating some core components and the base databinding mechanism of my overall framework. This app didn't really need an update from a functional perspective, but I used the application as my proving ground for a number of new concepts and ideas I've been tossing around – most of which have been discussed here at one time or another.

 

One thing that's still throwing me for a loop once a while is MasterPages. I'm still finding a few things here and there that are not quite natural to deal with. Here's one of them.

 

In my app I have several master pages for different 'areas' of the application. The main public facing interface gets one masterpage, the admin areas another and the reporting end another. Master Pages work great for basic skinning support and the ability to customize the layout.

 

But one thing that I've repeatedly run into is styling of pages dynamically. The Masters define the head section so all style references come for the theme CSS file(s). But on a number of pages – especially the admin pages – I need to customize the existing styles and or create one off styles for a specific page.

 

Easier said than done with MasterPages, since the style is defined in the head tag of the MasterPage. You can't easily define a Style tag inside of a Content panel:

 

<style>

.gridheader { text-align:center;height:20px;padding-top:2px; }

</style>

 

If you do something like this right in your Content Panel, the HTML will actually work, but it's actually badly formatted HTML. Worse – Visual Studio in won't let you switch into design view with a style tag inside of your content (depending on how your page is laid out but if it's between a <td> or <div> tag it won't let you).

 

So how do you get a style tag into the header? You could do it programmatically but the syntax for adding individual styles is not exactly short or intuitive. In fact, on a quick search I couldn't figure out how to do it short of writing out raw text into the header with a literal control (I'm sure there's an easier way though). Anyway, I don't think this is a workable solution.

 

The solution I settled on is this: Using a second content panel in the MasterPage that essentially extends the header into the Content pages.

 

<head runat="server">

    <title></title>

<link runat="server" id="styleTemp"

      href="../../App_Themes/Standard/Standard.css"

          rel="stylesheet" type="text/css" />

    <asp:ContentPlaceHolder runat="server" id="Headers">

    </asp:ContentPlaceHolder>

</head>

<body style="margin-top: 0px; margin-left: 0px">

 

If you don't need to add any headers to your content page – fine. Nothing further is needed. But if you now need to add say a stylesheet you can do this in the content page:

 

<%@ Page Language="c#" Inherits="Westwind.WebStore.Admin.EditInventoryItem" >

 

<asp:Content ID="Content1" runat="server"  ContentPlaceHolderID="Headers">

<style>

.gridheader { text-align:center;height:20px;padding-top:2px; }

</style>

</asp:Content>

 

<asp:Content ContentPlaceHolderID="Content" ID="Content" runat="server">

<h1>Inventory Item Edit Form</h1>

</asp:Content>

 

This solves the problem of giving the content page access to the header which is the one place where the client might need access to the content of the master. You can add styles, meta tags, scripts or whatever you want in the header right here.

 

 

Eeer, just as I'm finishing this up I notice that K.Scott Allen posted a similar note about a month ago. I suppose this is good enough of a tip to pass on again <g>.

 

One thing he mentions that I missed it that the Visual Studio HTML validation complains about the content placeholder in the header. But then again VS.NET complains about a lot of things that are legit <shrug>…


The Voices of Reason


 

Bob
May 26, 2006

# re: Getting access to the Head tag in Master Pages

Is there a reason why you wouldn't just put the style in a separate file? What are the advantages of doing it the way your doing it?

Rick Strahl
May 26, 2006

# re: Getting access to the Head tag in Master Pages

You can use an external CSS file, which I already do. But on many pages I have one or two style overrides of a style defined in this master style sheet, that differ from page to page dependning on the layout. I really don't want to clutter up the main stylesheet with 20 minor variations of a specific style. I suppose in hindsite better style class design might have dealt with this better but with the current state of affairs it's not a good option.

I do think you really should have the ability to add information to the header from within a content page. This gives the needed flexibility without any impact if you don't use it - it's pretty unobtrusive (except for the VS.NET Html Validation warning).

Rainer
May 27, 2006

# re: Getting access to the Head tag in Master Pages

Rick, I understand that you need to specify different styles to be applied to specific pages aside from your theme css-files?

I think (mostly all) style definition should always come from css-stylesheets, adding styles programmatically isn't the most optimal situation, 'cause style changes requires you to
change code and compile, instead changing style in the css-stylesheet.

I normally define several stylesheets, one for basic, one for media, several for advanced use or different pages and apply them into the pages so i can keep all stylesheets compact and short.

You can also import one stylesheet from another stylesheet. This allows you to link to your basic stylesheet from the page and then import your complicated styles into that stylesheet

like::
@import url(/css/layout.css)
@import url(/css/typografy.css)
@import url(/css/color.css)

This helps to remove some complexity from css-pages and allows you to manage all your stylesheets in one place (except theme stylesheets). Import rules need to be the first rules in a stylesheet or they may not work properly. Because imported stylesheets are considered to come before linked stylesheets, it's important to remember that the rules in your linked stylesheets will be overriding your imported rules and not the other way around.

Rick Strahl
May 27, 2006

# re: Getting access to the Head tag in Master Pages

Rainer thanks for the insight. I am pretty strict in using styles defined in CSS files. It's the special cases where one off styles are used that it gets painful to add these styles into either the main or even an imported style sheet.

I haven't worked with imports in a while - I tend to load styles from pages, primarily because I've always had path issues with imported CSS files.

Rick Strahl
May 27, 2006

# re: Getting access to the Head tag in Master Pages

One thing to watch out for with this method is to insert <style> tags is if you are using Themes. If Themes are used ASP.NET will inject the Theme Stylesheet reference at the very end of the Head tag. So if you override existing styles in the <asp:Content> panel it won't have any effect.

Grrr... that throws a wrench into this whole scheme.

Nicholas
May 28, 2006

# re: Getting access to the Head tag in Master Pages

Rick,

There is a Page.Header class you can use in ASP.Net 2.0 to add controls/etc. to..

Here's a VB.Net (yuck) example:

Dim cssLink As New HtmlLink()
cssLink.Href = "~/styles.css"
cssLink.Attributes.Add("rel", "stylesheet")
cssLink.Attributes.Add("type", "text/css")
Page.Header.Controls.Add(cssLink)

Viola.

# Adding Meta Tags and Stylesheets to ASP.NET 2.0 Pages Programatically

I came across a blog entry by Rich Strahl about adding stylesheets to ASP.NET 2.0 web pages. The way...

Rick Strahl
May 30, 2006

# re: Getting access to the Head tag in Master Pages

Just a note here...

A lot of people have mentioned how to add Style*sheets* programmatically. That's easy enough to do, but if you add styles, there's no programmatic equivalent that I know of. Other than writing a raw literal control into the header.

Steve Shepherd
June 01, 2006

# re: Getting access to the Head tag in Master Pages

BRILLIANT! I have explored a lot of 'programatic' ways to accomplish something as simple as adding a Content area to the header. SIMPLY BRILLIANT!

Sami
June 20, 2006

# re: Getting access to the Head tag in Master Pages

Thanks - Just what I needed... takes care of so many issues I've dealing with - Like the other post said: "BRILLIANT".


John Adams
June 29, 2006

# re: Getting access to the Head tag in Master Pages

I've studied this blog entry closely but found problems trying this technique for adding Javascript code that needs to be in the header and done at page load time (it just gets ignored; viewing the source of the rendered page shows none of my Javascript). Then I found this:
http://msdn.microsoft.com/asp.net/default.aspx?pull=/library/en-us/dnaspp/html/JAVAwASP2.asp#javawasp2_topic6
which talks about using Page.ClientScript.RegisterStartupScript
etc. I think your way seems much simpler but I'm having trouble getting it loaded into the <head>. Have you had any success using your technique in master and content pages to load Javascript into the header?

Rick Strahl
June 29, 2006

# re: Getting access to the Head tag in Master Pages

John, that's exactly what this code accomplishes if you put script into the header.

IAC, you shouldn't need script in the head of the page. That doesn't guarantee loading of script properly. Using RegisterStartupScript will put the script in the right place to be executed and that's usually the best way - it puts startup script just before the end of the form tag in a page. You can do this manually as well.

The right way to load script and make sure the page is loaded is to fire script from <body onload=''>.

John Adams
July 03, 2006

# re: Getting access to the Head tag in Master Pages

Thank you for commenting on this. I am still researching and confused about your advice in the blog entry you added on 6/29.

I am trying to avoid:
a. script injection via RegisterStartupScript
b. putting in-line Javascript in <head> of the Master Page (not all my content will need the same scripts)

I would like use the idea of your "declarative" structure, extending your concept of

a. declare a content placeholder in master page <head> section for script that needs to be in the header

b. declare a content placeholder in master page after </body> for script that needs to be there

c. establish separate content files for each that reference the same .master file.

I am trying to extend my current use of the Google Maps API which is all client side Javascript to operate in a framework of ASP.NET 2.0 master page plus content page(s).

I am finding I cannot get the "composite" page built with any Javascript in the <head> nor after the </body> tag by using ASP:Content controls that refer to placeholders in the master page.

Can you please clarify your guidance in light of my longer explanation?


Rick Strahl
July 03, 2006

# re: Getting access to the Head tag in Master Pages

John, I don't understand what you're asking at all. If you put a <script> tag into the header content panel as shown above the <script> tag will end up in the page header. This has nothing to do with the global master page, this is page specific.

I think you need to actually try this out to see it work - you might be visualizing this wrong.

All I'm saying is there are many ways to get script code into the page and that is one of them you can use.



John Adams
July 05, 2006

# re: Getting access to the Head tag in Master Pages

Rick, thanks for not giving up on me. I HAVE tried this out. I put scripts in content panels and they do not end up in the final merged result page (i.e. nothing there when I "view page source").

I zipped up my test web site so you can see how I have tried to follow your guidelines:

ftp://ftp.cbmiweb.com/pub/john_adams/web.zip

username = ftpuser password = cbmi

If you glance at my master page and my 3 content ASPX files you will see how I am simply trying to package stuff as you have suggested (along with script guidelines from Google).

I build the site in Visual Studio 2005 and run it and the only Javascript in the resulting page is stuff generated by ASP.NET.

John Adams
July 13, 2006

# re: Getting access to the Head tag in Master Pages

I guess you did give up on my questions and comments in this thread. I've noticed many other people struggling with this issue of injecting script into the header of master pages. I really want to avoid repackaging for using RegisterStartupScript so I have abandoned use of the master page concept despite its appeal for other reasons.

James Lapuz
July 27, 2006

# re: Getting access to the Head tag in Master Pages

Thanks you guys. Been messing around with turning the scrollbars on/off depending on the contentpage. With the sample code above, I was able to add stylesheet with no scrollbar in my C# server code at page load.

Craig Gibbons
August 01, 2006

# re: Getting access to the Head tag in Master Pages

This is pretty clean solution:

HtmlGenericControl control = new HtmlGenericControl();
control.TagName = "style";
control.InnerText = ".gridheader { text-align:center;height:20px;padding-top:2px; }";
Header.Controls.Add(control);

The same technique can be used for adding meta tags, using HtmlMeta.

Peter Lasne
August 01, 2006

# re: Getting access to the Head tag in Master Pages

Thanks! This put me on the right track for what I was wanting to do. I too needed to load an additional css on a few pages that were wrapping responses from other systems.

I am also using Atlas and doing the programmatic HtmlLink addition wasn't working on postbacks for some reason. It was processing, but the styles were reverting.

I was also concerned about the error for <asp:ContentPlaceHolder /> because I hate having warnings like that if I can help it.

Anyway, if you need to add a single css to a page programmatically, try this in the master page header:

<link id="page_css" />

...and this in your page code:

Dim objLink As HtmlLink = Page.Header.FindControl("page_css")
If Not IsNothing(objLink) Then
objLink.Href = "~/search/search.css"
objLink.Attributes("rel") = "stylesheet"
objLink.Attributes("type") = "text/css"
End If

thom
August 02, 2006

# re: Getting access to the Head tag in Master Pages

And waht about this?
add this 2 functions to masterpage .cs file

public void RegisterScriptSrc(string url)
{
Literal head_script;
Control ctrl = Page.Header.FindControl("ap_m_script");
if (ctrl != null) head_script = (Literal)ctrl;
else
{
head_script = new Literal();
head_script.ID = "ap_m_script";
Page.Header.Controls.Add(head_script);
}
head_script.Text += "\r\n<script type=\"text/javascript\" src=\"" + url + "\"></script>";
}

public void RegisterStyleSrc(string url)
{
HtmlLink csslink = new HtmlLink();
csslink.Href = url;
csslink.Attributes.Add("type", "text/css");
csslink.Attributes.Add("rel", "stylesheet");
Page.Header.Controls.Add(csslink);
}


Phillipe
August 10, 2006

# re: Getting access to the Head tag in Master Pages

FANTASTIC, Rick. Fantastic.

Niebs
August 18, 2006

# re: Getting access to the Head tag in Master Pages

I don't know but why would you not use Page.RegisterClientScriptBlock()

It writes whatever you insert into it in the head tag?

WOrks for my purposes...

Rick Strahl
August 18, 2006

# re: Getting access to the Head tag in Master Pages

The whole point of this excercise was to do markup and style definitions declaratively. In code there are a ton of different options of course, but to keep the page editable for the graphics folks code isn't going to do it.

Even so there are limitations to this approach. If you use Themes the Theme is injected last in the list of header items, so the themes will always override any settings made in the Content area as well as anything else (like script) that gets injected into the Header. So if you really need to *override* styles in the content of a master page you need to do something like this:

<div runat="server">
<style>
.MyOverrideTag {...}
</style>
</div>

Without the server DIV Visual Studio will flag <style> as invalid in the document body - grumble although it will work just fine.

Scott
September 04, 2006

# re: Getting access to the Head tag in Master Pages

Well you could always create a CustomPage class that inherits from your base Page class. Put the graphical code there. Build into the application the ability for the users to create page based styles that would get added automatically by your CustomPage code base in the PreInit function.

Someway like that would probably be how I would do it. I was looking for a way to ineject javascript to the top of each page when I stumbed onto your post here.

Scott
September 04, 2006

# re: Getting access to the Head tag in Master Pages

When I said "graphical code" in my comment above I meant the code to inject any custom stylesheets for the page.

I don't know how I got to "grahical code" :D

# ASP.NET Forums - Different head and meta tag content on each content page

# DotNetSlackers: Getting access to the Head tag in Master Pages

# ASP.NET Forums - Different head and meta tag content on each content page

Default description here

Jason Rogers
January 27, 2007

# re: Getting access to the Head tag in Master Pages

Take a look at this:

http://www.codeproject.com/useritems/PageTags.asp

uses the same technique of adding things to the page header, but allows you to do it on the @Page directive.

Nathanael Jones
May 02, 2007

# re: Getting access to the Head tag in Master Pages

There's a catch to this approach... The custom markup parsing that the HtmlHead control applies to its contents stops being applied once a intermediate ContentPlaceHolder is inserted. Thus, elements like link, meta, and script are parsed in as a single literal control, instead of individual HtmlLink, HtmlMeta, and HtmlGeneric controls. The most prominent effect of this is that path resolution is no longer applied. Application-relative paths are off-limits, and even relative paths won't work if you employ URL rewriting as you should. Absolute virtual paths will work, but are a bad long-term policy due to their fragility.

More info:
https://connect.microsoft.com/feedback/viewfeedback.aspx?FeedbackID=273683&wa=wsignin1.0&siteid=210

wawabear
May 15, 2007

# re: Getting access to the Head tag in Master Pages

Hey Rick, nice thought..never thought that this way will work!
Nice work!

ASP.NET Forums
May 17, 2007

# Different head and meta tag content on each content page - ASP.NET Forums

Default description here

K. Scott Allen
July 11, 2007

# K. Scott Allen : The ContentPlaceHolder – Not Just For Content


neuralsea
September 23, 2007

# re: Getting access to the Head tag in Master Pages

and yet this way:
<meta name="description" content='<asp:ContentPlaceHolder id="metaDescriptionRegion" runat="server"/>' />

complains that it:
Cannot find ContentPlaceHolder 'metaDescriptionRegion' in the master page...

Any ideas how I can access metaDesciptionRegion?

Neuralsea

Jim Peiffer
December 10, 2007

# re: Getting access to the Head tag in Master Pages

Thanks, I visited this and K.S. Allen's page. Both helped. Code works as explained.

Mark Solomon
February 25, 2008

# re: Getting access to the Head tag in Master Pages

This code helped with the exact problem I was having with master pages. I'm using VS 2008 and trying to get a ContentPlaceHolder to accept styles that I've already defined. Rick's technique seems to apply to style sheets as well as in-line styles--allowing me to apply them within the ContentPlaceHolder control on subsequent pages. And it appears that within VS 2008 my stylesheet ref in head isn't an absolute path. Yea!

EranD
December 13, 2008

# re: Getting access to the Head tag in Master Pages

When I insert the <asp:ContentPlaceHolder into the head tag of the master page I recieve an error message:"' unrecognized tag prefix or or device prefix 'asp'"

does anybody can help about how can I solve this problem?

Thanks

Rick Strahl
December 13, 2008

# re: Getting access to the Head tag in Master Pages

@Eran - that's a designer warning only and it should run just fine at runtime.

Mukesh
September 16, 2009

# re: Getting access to the Head tag in Master Pages

This is really cool . It worked for Me.
Thanks

Yves Richard
October 14, 2009

# re: Getting access to the Head tag in Master Pages

Mr. Strahl saves the day again. Sweet blog. Not tempted to go to the dark side of wind sports, kiting? have a nice day!

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024