Valid XHTML within .NET

Suggested Solution

Although there are potentially many ways to solve issues like the one detailed above, this document will concentrate on inheriting and extending the base ASP.NET controls to produce the results we desire.

Step 1: Creating a Custom Assembly

To start with we'll create two new classes inside a namespace that we can use to begin building our assembly based solution. The first class will extend System.Web.UI.Page and the second will extend System.Web.UI.HtmlControls.HtmlForm . Create a new C# file called Liquid.Tutorial.cs and save it somewhere outside of your root IIS directory (we'll compile this file into a library class a little later on) and copy the code in Listing 02 into the newly created file.

Listing 02

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()
    {
    }
  }
  public class CustomForm : HtmlForm
  {
    public CustomForm()
    {
    }
  }
}

First this code imports all the namespaces that we'll be using, then it extends two default classes and accesses the asp:Literal controls in our ASP.NET page. Compile the newly created file into a library .dll file using the following command line syntax:

csc /t:library Liquid.Tutorial.cs

Copy the .dll file that is created into the /bin directory of your Web server. Now we have a base assembly in place we can create a new ASP.NET page that utilises our new classes.

Creating a Custom Page

Create a new ASP.NET page in the root folder of your IIS development machine; call the page replacementform.aspx and copy the code from Listing 03 into it.

Listing 03

<%@ Page Inherits="Liquid.Tutorial.CustomPage" AutoEventWireup="True" %>
<%@ Register TagPrefix="Liquid" Namespace="Liquid.Tutorial" Assembly="Liquid.Tutorial" %>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-GB">
  <head>
  <meta http-equiv="Content-Type"
    content="application/xhtml+xml; charset=utf-8"/>
    <title>Replacement ASP.NET Form</title>
  </head>
  <body>
    <h1>Replacement ASP.NET Form</h1>
    <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>

You'll recognise most of this code from the currentform.aspx file we created earlier, notice the new @Page and @Control registration lines at the top of the code as well as the new Liquid:CustomForm tag that replaces the form runat="server" tag. Also notice the asp:Literal controls that act as placeholders for the soon to be created view-state field and response text. If you now point your browser to replacementform.aspx you should see something similar to Figure 1b .

Figure 1d
Figure 1b

Step 3: Fixing the Name Attribute

Now that we have a working assembly let's take another look at the first error we encountered:

  1. There is no attribute called name within a form tag.

ASP.NET writes the form tag, the correct attributes and the offending attribute to the page when the HtmlForm.Render() method is called. As we're clearly going to need to make some changes to this particular method it is worth taking a moment to see roughly what goes on when this is called:

  • HtmlForm.Render()
    • Validate that page is not null
    • Write the smartnavigation scripts
    • Write the start of the form tag
  • HtmlForm.RenderAttributes()
    • Write the attribute tags
  • HtmlForm.RenderChildren()
  • Page.OnFormRender()
    • Ensure no more than one form exists on a page
    • Write hidden fields
    • Write the view-state hidden field
  • HtmlContainerControl.RenderChildren()
    • Render all code between form tags
  • Page.OnFormPostRender()
    • Write out additional script blocks
  • Write out the end form tag

As you can see the first method that we're likely to be interested in is HtmlForm.RenderAttributes() as that's the place where our problematic name attribute is written out. Luckily for us that method happens to be declared virtual so it's pretty straightforward to override it and replace it with our own code found in Listing 04 .

Listing 04

protected override void RenderAttributes(HtmlTextWriter output)
{
  output.WriteAttribute("id", this.ID);
  output.WriteAttribute("method", this.Method);
  output.WriteAttribute("action", HttpContext.Current.Request.Path);
}

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. Add this code to the CustomForm class in our Liquid.Tutorial.cs file, compile and copy the assembly file to the Web server's /bin directory then run the replacementform.aspx page. If we view the page source code we can see the problematic name attribute has disappeared.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“XML is like violence - if it's not working for you, you're not using enough of it.”