When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips
Search

Sections:
Book Reviews
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
Web Hosts
XML
Information:
Advertise
Feedback
Author an Article

ASP ASP.NET ASP FAQs Message Board Feedback
 
Print this Page!
Published: Wednesday, May 5, 2004

A Look at ASP.NET's Adaptive Rendering

By Scott Mitchell


Introduction


ASP.NET Web pages, as you know, are made up of an HTML portion and a source code portion. The HTML portion contains a mix of HTML markup along with Web controls. When the ASP.NET Web page is visited, the Web controls are rendered into HTML markup. The static HTML markup, along with the rendered Web controls markup, is sent back to the visitor that requested the page, and displayed in their browser.

- continued -

The HTML rendered by Web controls, though, depends on the browser requesting the ASP.NET Web page. For this reason, ASP.NET Web controls are called adaptive. For example, if a visitor requests a page using Internet Explorer 6.0, they'll receive HTML 4.0-compliant markup; for example, a Label Web control whose ForeColor property were set to Red would render the following markup:

<span id="controlID" style="color: red">Label Text</span>

If, however, the same visitor were to use Netscape 4.72, they'd receive HTML 3.2-compliant markup. The HTML for the Label Web control would specify the color using a <font> element rather than a style attribute in the <span>.

<span id="controlID"><font color="red">Label Text</font></span>

While adaptive controls are a nice concept in general, ASP.NET's default implementation can be a bit frustrating since for modern browsers - such as recent versions of Mozilla, Firefox, Netscape, and Opera - ASP.NET controls render, by default, HTML 3.2-compliant HTML rather than HTML 4.0-compliant HTML. Fortunately, you can configure your Web application (or the entire Web server) to render HTML 4.0-compliant HTML for these modern, non-Microsoft browsers. In this article we'll see how ASP.NET decides whether to render the Web control HTML as 4.0-compliant or 3.2-compliant. We'll also look at how to configure your Web application so that modern versions of Netscape, Opera, and Mozilla cause ASP.NET to render 4.0-compliant markup.

How Web Controls are Rendered


Each and every ASP.NET Web page is derived either directly or indirectly from the System.Web.UI.Page class. This class provides the base methods and properties that represent an ASP.NET Web page. Whenever a request is received by the Web server for an ASP.NET Web page, Web server hands off the request to the ASP.NET engine, which creates an instance of the requested page's Page class and calls this class's ProcessRequest() method.

The call to the ProcessRequest() method kicks off the page's life cycle, which involves loading the page's control hierarchy, restoring the view state, firing the Load event, firing Web control events, saving the view state, and rendering the page. The diagram below graphically illustrates the ASP.NET page's life cycle. (Click here for a larger view...)

The ASP.NET page life cycle.

The specifics of this life cycle are not important to this article, but if you're interested in learning more might I suggest reading How ASP.NET Web Pages are Processed on the Web Server and The ASP.NET Page Object Model.

The ASP.NET Web page is rendered in the following manner: the Page class creates an instance of a System.Web.UI.HtmlTextWriter class, and then recursively calls the RenderControl() method of all of the controls in its control hierarchy, passing in the HtmlTextWriter instance. Each control, then, appends its HTML markup to the end of the same HtmlTextWriter instance. After all controls have been rendered, the Page class can return the entire page's HTML markup by returning the content in that HtmlTextWriter instance.

Adaptive Rendering with HtmlTextWriter


The System.Web.UI namespace contains two classes for rendering Web controls: HtmlTextWriter, which renders HTML 4.0-compliant markup, and Html32TextWriter, which renders HTML 3.2-compliant markup. (The Html32TextWriter class is derived from the HtmlTextWriter class.)

When the Page class enters its render stage, it starts by creating a new HtmlTextWriter instance. It determines what class instance to create - HtmlTextWriter or Html32TextWriter - by inspecting the TagWriter property of the Browser object. The Browser object contains read-only properties that provides information about the browser that was used to request the ASP.NET Web page. A Web browser is identified by its User-Agent string.

Whenever a Web browser requests a page, it sends along a User-Agent string in the HTTP headers that is used to identify the browser. Internet Explorer, for example, sends the User-Agent string Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705; .NET CLR 1.1.4322). Mozilla FireFox sends the User-Agent string Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.6) Gecko/20040206 Firefox/0.8. Some browsers don't send a User-Agent string, or send a "fake" User-Agent string. For example, with Opera you can instruct it to send the IE6 User-Agent string. There's an extension for Mozilla that offers the same functionality as well. (Interested in seeing what User-Agent string your browser is sending? Check out the User-Agent live demo.)

The ASP.NET engine examines the User-Agent string to determine the values for the Browser's properties. One of the properties is TagWriter, which, by default, is set to HtmlTextWriter for Internet Explorer 4.0 and up. For all other browsers, it is set to Html32TextWriter. Fortunately, these settings are configurable, so you can indicate that modern versions of Netscape, Mozilla, and Opera use the HtmlTextWriter class as opposed to Html32TextWriter. These settings can be made through the <browserCaps> section in either the maching.config or Web.config files. (Altering the settings in maching.config will set the default behavior for all Web applications on the Web server. To control the settings on an application-by-application basis, use the Web.config file.)

Examining the <browserCaps> Element


The <browserCaps> element contains a number of <case> elements. Each of these <case> elements has a match attribute, which contains a regular expression. If the regular expression matches with the User-Agent string sent by the browser, the properties inside the <case> element are applied to the Browser object. If you look in the machine.config you'll see that, by default, the TagWriter property is set to the Html32TextWriter class:

<browserCaps>
   ...
   

tagwriter=System.Web.UI.Html32TextWriter


   ...
</browserCaps>

Further down, you'll find a <case> element that matches Internet Explorer's User-Agent. Here, you'll find that the TagWriter property is set to the HtmlTextWriter class:

<browserCaps>
  ...
  <case match="^Mozilla[^(]*\(compatible; MSIE <snip>">
      browser=IE
      ...
      tagwriter=System.Web.UI.HtmlTextWriter
  </case>
</browserCaps>  

For non-Microsoft browsers, the TagWriter property remains the default - Html32TextWriter.

Configuring HTML 4.0-Compliant Rendering for Netscape, Mozilla, and Opera


Before we examine precisely how to configure the maching.config or Web.config files so that modern, non-Microsoft browsers render HTML 4.0-compliant markup, let's first discuss the motivation behind this. You may be wondering why go to this trouble. After all, while ASP.NET may render a Label with a red ForeColor as <span id="controlID" style="color: red">Label Text</span>, and render the same Label as <span id="controlID"><font color="red">Label Text</font></span> for Netscape users, in the end, they see the same thing, so what's the big deal?

Well, for the ForeColor, yes, this is the case, but if you set the BackColor property on a Label the HtmlTextWriter class uses the background-color CSS setting to specify a background color. Since there is no way to set a background color for an HTML element without CSS (short of using a <table> or some other trickery), the Html32TextWriter class simply ignores the BackColor property. In addition to this reason, another motivation behind 4.0-compliant markup is that the HtmlTextWriter class can render <div> elements; the Html32TextWriter class renders <div> as <table>s. This can cause some oddities for certain scenarios.

The following shows the <browserCaps> section you can add to your Web.config file to specify that modern, non-Microsoft browsers cause the Web controls to render HTML 4.0-compliant markup. Notice that in addition to setting the TagWriter property, the <browserCaps> section below also sets a number of other properties for the Browser object. (This <browserCaps> section was created by Rob Eberhardt, and is available here.)

<!-- 2003-12-03, Rob Eberhardt - http://slingfive.com/demos/browserCaps/ -->

<browserCaps>
  <!-- GECKO Based Browsers (Netscape 6+, Mozilla/Firebird, ...) //-->
  <case match="^Mozilla/5\.0 \([^)]*\) (Gecko/[-\d]+)? (?'type'[^/\d]*)([\d]*)/(?'version'(?'major'\d+)(?'minor'\.\d+)(?'letters'\w*)).*">
    browser=Gecko
    type=${type}
    frames=true
    tables=true
    cookies=true
    javascript=true
    javaapplets=true
    ecmascriptversion=1.5
    w3cdomversion=1.0
    css1=true
    css2=true
    xml=true
    

tagwriter=System.Web.UI.HtmlTextWriter


    <case match="rv:(?'version'(?'major'\d+)(?'minor'\.\d+)(?'letters'\w*))">
      version=${version}
      majorversion=${major}
      minorversion=${minor}
      <case match="^b" with="${letters}">
        beta=true
      </case>
    </case>
  </case>
  
  <!-- AppleWebKit Based Browsers (Safari...) //-->
  <case match="AppleWebKit/(?'version'(?'major'\d)(?'minor'\d+)(?'letters'\w*))">
    browser=AppleWebKit
    version=${version}
    majorversion=${major}
    minorversion=0.${minor}
    frames=true
    tables=true
    cookies=true
    javascript=true
    javaapplets=true
    ecmascriptversion=1.5
    w3cdomversion=1.0
    css1=true
    css2=true
    xml=true
    

tagwriter=System.Web.UI.HtmlTextWriter


    <case match="AppleWebKit/(?'version'(?'major'\d)(?'minor'\d+)(?'letters'\w*))( \(KHTML, like Gecko\) )?(?'type'[^/\d]*)/.*$">
      type=${type}
    </case>
  </case>
  <!-- Konqueror //-->
  <case match = "Konqueror/(?'version'(?'major'\d+)(?'minor'\.\d+)(?'letters'));\w*(?'platform'[^\)]*)">
    browser=Konqueror
    version=${version}
    majorversion=${major}
    minorversion=${minor}
    platform=${platform}
    type=Konqueror
    frames=true
    tables=true
    cookies=true
    javascript=true
    javaapplets=true
    ecmascriptversion=1.5
    w3cdomversion=1.0
    css1=true
    css2=true
    xml=true
    

tagwriter=System.Web.UI.HtmlTextWriter


  </case>
</browserCaps>   

Moving Forward - Client-Side Validation


While configuring ASP.NET to render HTML 4.0-compliant markup for users surfing with modern versions of Netscape, Opera, and Mozilla is a good first step, you might be disappointed to find out that even with this change, the validation controls still won't render client-side validation script. There are a couple of (annoying) reasons for this, but I'm going to save this discussion for a future article!

Until then, Happy Programming!

An Article on Client-Side Validation
I have authored an article discussing why client-side validation doesn't work in downlevel browsers, along with a discussion on how to implement client-side validation. Learn more at Client-Side Validation in Downlevel Browsers

  • By Scott Mitchell



  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article