W3C Validator and the ViewState

The problem:

I was having a bit of trouble validating my ASP.NET pages using the official W3C HTML Validator software.

It turned out that the problem was with the hidden VIEWSTATE field that the .NET framework inserts into the web forms automatically. The W3C validator doesn't like the underscores __ in the name/id attributes of the VIEWSTATE field.

The solution:

Although the answer to this issue can now be found in the official Microsoft ASP.NET forums (I started a thread a while back to find a solution to this issue), I decided it would be a good idea to post on a ASP.NET weblog so other developers would be able to find this information more easily.

Essentially the credit for finding this solution has to go to the author of the article: http://www.developerfusion.co.uk/show/4641/1/.

The solution required a C# Class file to be compiled into a DLL file and then placed in the Bin folder within the Web Application.

Note, for me to be able to compile the file succesfully I had to copy the file into the following folder on my hard drive C:\Program Files\Microsoft Visual Studio 9.0\VC and then open up the VS2008 Command Prompt and enter the line:

csc /t:library Liquid.Tutorial.cs

As well as this C# Class file I needed a ASPX page to test the solution. The ASPX code is directly below...

<%@ Page Inherits="Liquid.Tutorial.CustomPage" AutoEventWireup="True" %>
<%
@ Register TagPrefix="Liquid" Namespace="Liquid.Tutorial" Assembly="Liquid.Tutorial" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

<html lang="en-gb">
<
head>
<
title>ASP.NET 2.0 [Fix W3C ViewState Validation Issue]</title>
</
head>
<
body>

<Liquid:CustomForm id="myId" method="post" runat="server">

<fieldset>
   
<legend>Sample form</legend>

    <input type="submit"/>

    <asp:Literal id="inputViewState" runat="server"/>
</fieldset>

</Liquid:CustomForm>

<p>
   
<asp:Literal id="outputText" runat="server"/>
</p>


</
body>
</
html>

The code for the C# Class file is directly below...

using System;
using System.IO;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

namespace Liquid.Tutorial

{

        public class CustomPage : Page
        {
               
protected Literal inputViewState;
               
protected Literal outputText;

               
public CustomPage()
               
{
                }

               
/* 
                * To get and set the hidden form field we need to override the 
                * Page.LoadPageStateFromPersistenceMedium() and 
                * Page.SavePageStateToPersistenceMedium() methods to get and set 
                * the hidden form field value.
               
                * NOTE: LosFormatter is a somewhat undocumented class that is used to write 
                * the view-state object to a Base64 string and back again.
                */

                protected override Object LoadPageStateFromPersistenceMedium() 
                {
                        LosFormatter format =
new LosFormatter(); 
                       
String viewState = HttpContext.Current.Request.Form["__VIEWSTATE"].ToString();
                       
return format.Deserialize(viewState); 
                }

               
protected override void SavePageStateToPersistenceMedium(Object viewState) 
                {
                        LosFormatter format =
new LosFormatter();
                       
StringWriter writer = new StringWriter(); 
                        format.Serialize(writer, viewState);
                       
this.inputViewState.Text = "<input type=\"hidden\" name=\"__VIEWSTATE\" value=\"" + writer.ToString() + "\"/>"
               
}

                // The code writes an output string to the final asp:Literal control when the form is submitted correctly.
               
protected void Page_Load(Object sender, EventArgs e) 
                {
                       
if (Page.IsPostBack) 
                        {
                                outputText.Text =
"Is post back"; 
                        }
                }

                /* 
                * You'll have probably realised the one function we overwrote but didn't 
                * replace when we overrode the HtmlForm.RenderChildren() method, was the 
                * check to see if a form already exists on the page. The problem is the 
                * functionality to perform such a check is locked away in private functions 
                * so we'll have to create our own solution.
                */

                // The Boolean field will allow us to set and get a flag stating if a form already exists on a page.
               
private Boolean _fOnFormRenderCalled = false;
               
internal void OnFormRender() 
                {
                       
if(this._fOnFormRenderCalled) 
                        {
                               
throw new HttpException("A page can have only one visible server-side CustomForm tag"); 
                        }
                       
else
                       
{
                                this._fOnFormRenderCalled = true; 
                        }
                }
        }

        public class CustomForm : HtmlForm
        {
               
public CustomForm() 
                {
                }
                // This function adds/removes any attributes from the FORM tag
               
protected override void RenderAttributes(HtmlTextWriter output) 
                {
                       
/* 
                        * The code simply writes the attributes we are actually interested in
                        * and ignores the others. This could easily be extended should
                        * additional attributes be required (client-side form validation 
                        * for example) but to keep it simple we'll stick with those three. 
                        */
                        output.WriteAttribute("id", this.ID); 
                        output.WriteAttribute(
"method", this.Method);
                       
output.WriteAttribute("action", HttpContext.Current.Request.Path); 
                }

                // This function removes the ViewState field and adds it back in again
               
protected override void RenderChildren(HtmlTextWriter output) 
                {
                        /* 
                        * This overridden function simply gets the code between the form tags
                        * and writes it to the page. You'll find that it outputs valid HTML.
                        * Only now the form doesn't actually post back properly, this is 
                        * because there is an issue with all current versions of the 
                        * .NET runtime that require the hidden input form field to be present, 
                        * even if the value is blank.
                        */

                        foreach (Control c in base.Controls) 
                        {
                                c.RenderControl(output);
                        }

                       
/* 
                        * This line of code simply casts the base page class as the derived page
                        * class so that it can read and write the flag field.
                        * 
                        * The Boolean field (which is created above in the CustomPage class) 
                        * will allow us to set and get a flag stating if a form already exists 
                        * on a page. Set and get the property via the accessor property.
                        */

                        
((CustomPage)this.Page).OnFormRender(); 
               
}
        }
}


Hope this helps.

1 Comment

  • I have never had a problem with getting pages validating in .net. The viewstate seems to validate fine.

    The only error that I get when I tried the example of the link you referenced was:

    Line 13, Column 15: there is no attribute "name".



    This can easily be fixed by setting the following in the web.config just before the like so:




    Now the page should validate.

Comments have been disabled for this content.