I recently came across a situation where I needed to dynamically add HTML content to the <HEAD> section of every page on our site. I needed this, for a couple of reasons. 1) I wanted to allow users to dynamically specify different style sheets to apply to the entire site. 2) I had to import some .HTC references whenever a control on a page needed to use a sticky draggable div control.
The first thing I tried was to simply give my <head> tag an id and the runat=server attribute. This way, I could include a method to my site PageBase class that pops content into the <head> tag. By the way, PageBase is simply a class that inherits from System.Web.UI.Page, from which I base all of my aspx classes. If you're not already sub-classing a page base for your app, you may want to look into this. Anyhow, this worked, but had one MAJOR drawback... The Visual Studio IDE would remove the runat=server attribute from my <head> tag, whenever the view was switched to HTML or when the page was saved.. Well, I couldn't deal with having to remember to add it back every time I saved, so I went looking for a new approach.
The approach I came up with involves adding a couple of methods to my PageBase Class to register any content I want to have appear in my HEAD section:
private Hashtable m_registeredHeadContent;
public void RegisterHeadContent(string key, string content)
{
if(this.m_registeredHeadContent == null) {
this.m_registeredHeadContent = new System.Collections.Hashtable();
}
if(!m_registeredHeadContent.ContainsKey(key)){
this.m_registeredHeadContent.Add(key, content);
}
}
public string RegisteredHeadContent
{
get
{
string strReturn = null;
if(m_registeredHeadContent != null)
{
foreach(string s in m_registeredHeadContent.Values) strReturn += s;
}
return strReturn;
}
}
So, now whenever I want to add something, like an HTC import, I just add a call to the “RegisterHeadContent” method from my ascx controls or WebControl. Now how to get this content into the HEAD section of my page. You really have two options, add a classic style server tag, like so to your <HEAD> section:
<%=RegisteredHeadContent%>
or you could create a control deriving from System.Web.UI.HtmlControls.HtmlGeneric control, drop on your form in your <HEAD> section. This control needs to retrieve the RegisteredHeadContent from your PageBase class. I created one, which my friend Brandon Boyd aptly named for me “HtmlHeadContentInjector“ ;)
public class HtmlHeadContentInjector : System.Web.UI.HtmlControls.HtmlGenericControl
{
// Needs this constructor
public HtmlHeadContentInjector(string tagname){}
protected override void Render(HtmlTextWriter writer)
{
string headContent = ((PageBase)this.Page).RegisteredHeadContent;
writer.Write(headContent);
}
// Override, don't render atts
protected override void RenderAttributes(HtmlTextWriter writer){}
// Override, don't render end
protected override void RenderEndTag(HtmlTextWriter writer){}
}
Which option you choose is up to you. Personally, I don't like poking too many holes in my ASPX pages using the <%=%> syntax, but don't have any hard evidence against this method..
-Brendan