Steve Holstad's "the bright lights"

"Just because your voice reaches halfway around the world doesn't mean you are wiser than when it reached only to the end of the bar." - Edward R. Murrow
in

December 2005 - Posts

ASP.NET 2.0 Master Pages - But how do I run JavaScript onLoad?

Great question.  I hit this problem 5 minutes into developing my first Master Pages website.  Inside the Master Page, you can define the usual JavaScript class file, or include in-page functions within the page header.  The problem is that these functions will be included for all pages served, creating unnecesary bloat.  I prefer to only include global functions within the Master Page script.

The easy solution here is to include any page-specific scripting within the content page, or in a small .js external file.  After a cursory look at the content page, you'll see where the problem lies...as a content page doesn't define an entire HTML document, you won't have your usual <body> tag to wire the onLoad event to.  And I'll bet you aren't interested in having the same onLoad event fire for every page, which wiring to the Master Page's body onLoad event would do.

Here's a method around the problem:  In the content page where the onLoad event is needed, hook into the Page_Load event in the code-behind class, and create a textual script function.  Then we can register the script using the ClientScript.RegisterStartupScript method.  This will register the script you create and run as if the page's onLoad event was called separately...a pretty nifty way around, I think.

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
' Initialize a stringbuilder object, much faster than string concatenation
Dim onloadScript As New System.Text.StringBuilder()
onloadScript.Append(
"<script type='text/javascript'>")
onloadScript.Append(vbCrLf)
onloadScript.Append(
"alert('This script is registered and run at the second page startup. Perfect!');")
onloadScript.Append(vbCrLf)
onloadScript.Append(
"</script>")
' Register script with page
Me.ClientScript.RegisterStartupScript(Me.GetType(), "onLoadCall", onloadScript.ToString())
End Sub

See it in action here.  It is interesting to note that your newly registered function will fire before the Master Page's onLoad, but both functions will still fire. 

One more suggestion:  I've seen a lot of people use StringBuilders to increase performance, but then include normal string concatenation within the Append call:  (like this:  myStringBuilder.Append("All your base" + " are belong to us.")  This is defeating the point: using the concatenation operator will cause a new string to be created and the value copied over...you've lost the benefit of the StringBuilder.  It's better practice to call .Append twice to create the desired value.

Master Pages in ASP.NET 2.0

So I've changed my blog layout again...just gives you another reason to check back, doesn't it?

I've been fortunate enough to be working on the new Resource Kit DVD for Visual Studio 2005, and I've been seeing some really great content coming in, which should be a nice kick off for asp.net 2.0 developers.  It has inspired me to refocus on my asp.net skills, so over the next few weeks I'll be honing in on specific topics that I think are really helpful to web developers.

Master Pages are new to asp.net, and along with themes & skins have really begun to remove the tedious tasks web devs have faced for years.  Everyone has at some point found themselves editing UI properties in Copy/Paste hell, changing the same values in multiple places.  This can happen when menu requirements change, UI designs are updated, or new content is needed.  Master Pages are another weapon against this type of tedious task, allowing you to focus more on writing valuable code.  Here's how they work:

Add a Master Page item to your web project.  You'll see an <asp:contentplaceholder> item placed in the master page's source.  This control will have an ID attribute, used to reference the control in the child pages, and the ubiquitous runat="server" attribute.  Don't add any content inside, this will hold all content from the content (child) pages you are about to define.  Add/Edit the Master Page as you normally would a web page, but keep in mind that this will be the core layout for all pages in this Master/Child family.  Many websites are based loosely on the box model, if you want this type of layout add your header, footer and side menus here.  This leaves a nice tidy container for content to slide inside

Master Page Added

Right-click the MasterPage.master file in the Solution Explorer, and select 'Add Content Page'.  Create as many content pages as you need.  VS2005 does a great of handling the interaction between the pages, even showing your content pages via Intellisense when you add a hyperlink.

Master Page Added

Look at your content page's HTML.  Notice anything different?  You should...the content page will only contain, (and only allow) code inside of the  <asp:contentplaceholder> control.  That should make sense, this page will inherit the Master Page's code for everything else.  There are methods for adding page specific code inside of a content page; one example I'll be examining later is how to add JavaScript for one content page without including unnecessary functions for all other content pages.

This setup allows you to keep all of your menu items, header and footer designs centralized, without duplicating code.  Keep in mind the Master Page can have multiple contentplaceholder controls...if a particular page doesn't want to use it, that's fine, just don't use it in your content page.

Happy webbing.

Working with XSLT and escaped HTML markup

I just encountered this issue working on an XSLT application:  The xml given by our data export service was escaping out any HTML markup found within the attributes of my target XML node.  This is done to ensure that the XML document remains well formed.  However, the frustration in this scenario is that a web browser will also ignore the escaped markup when rendering, resulting in text being sent to the screen containing my markup, but not acting on it as HTML.  (Not its fault, the browser handles this as expected)...so my text looked like:

Hey, this should be on <br> two lines!

instead of:

Hey, this should be on
two lines!

Initially there was some discussion that I could use CDATA for this.  Keep in mind that CDATA is really just escaping every markup element, saving you from having to escape all of the items individually.  Once we get to the browser, however, all differences between the two are lost.

Here's the short converstion with myself......

===========================================================

 My issue is that html markup is being escaped in order to maintain valid XML.  This is going to cause headaches when transformed via XSL, as I’ll have to catch and convert every instance of escaped elements, and un-escape them. 

============================================================

I found a way around this:  When selecting an attribute using xsl’s <value-of> method, an option exists to disable any escaped characters:

 <xsl:value-of select="@Description" disable-output-escaping="yes"/>

This allows my escaped markup to exist peacefully in the XML, but still render as HTML elements.  I wouldn’t recommend this method for open-ended production (it could allow badly formed structure to survive), but for this project we control both the source and destination of the data, and I feel confident this is a safe practice for our use.

============================================================= 

NOTE: 

I think I'm reversing myself on this one... Firefox does not (and will not) support the disable-output-escaping propery, as it violates the W3C spec for validity. I will be reorganizing my XML to move the attributes in question into separate elements, and then apply the CDATA markers.