Go Beyond the Limits of ASP.NET Form

Introduction

One of the most common hitches that ASP developers encounter when they first approach ASP.NET is that managed Web applications must be written according to a single-form interface model. In the single form interface model each page always posts to itself by default using the POST HTTP method. The HTTP method and the target frame of the post can be programmatically adjusted using ad hoc form propertiesMethod and Target. The final destination of the postback can't be changed in any way.

You can correlate the terminologies a single-form interface to the single-document interface (SDI) and multiple-form interface to the multiple-document interface (MDI) in client server.

In HTML and ASP programming, the form element (i.e., <FORM>) features the Action property that programmers use to redirect to another URL after clicking. In ASP.NET, server-side forms are rendered using a class named HtmlForm under System.Web.UI.HtmlControls Namespace. This class doesn't provide the familiar Action property, nor does it supply an alternative property to perform the similar functionality. As a result, the single form interface model is so deep-rooted in ASP.NET that there's no way around it.

Following is the description found on MSDN regarding the limitation.

The action attribute is always set to the URL of the page itself. The action attribute cannot be changed; therefore, you can only post back to the page itself. (ref: Goto definition on MSDN )

In general, renouncing multiple forms is not a big sacrifice. Multiple forms are sometimes useful, but probably not as frequently as you may think. A scenario in which you would find it helpful to use multiple forms is when you implement form-based functionality in a large number of pagesin search, login dialogs, getting current stock quote for example.

This white paper is an attempt to illustrate the limitation of the ASP.NET Form control, which doesnt provide the facility of submitting data to other pages or sites.

Lets consider a situation where 

  • we want perform a search on google.com with search criteria from a asp.net Web Page 
  • Or we want to look at current stock quote of a Company listed on NASDAQ or NYSE by submitting information from an ASP.NET web page.

We cannot fulfill above requisites in currently available UI related controls in asp.NET.

My paper is concentrating on the aspects of providing the Action property to Server-side form object based on very own HtmlForm class itself which further can be extended to adopt multiple-form interface.

You can look at the demo of the new implementation at my DemoSite

HtmlForm class: reviewed

Before going into further detail, lets look a little review on HtmlForm.

The HtmlForm class derived from HtmlContainer class and implements the IAttributeAccessor interface. So, the base class (HtmlContainer) provides HtmlForm the ability to govern and contain the child controls; IAttributeAccessor interface provide the ability the read and write the attributes of opening tag (i.e., <FORM ..) itself. The HtmlForm class is not sealed and thus it is permissible for further inheritance as well as customization.

Following Screen shows how we have extended the HtmlForm class to new CHtmlForm class to add the Action property within it to get current Stock quote at Quote.com.

GoBeyo1.jpg

CHtmlForm: a more powerful shape of HtmlForm for ASP.NET

While we reviewed HtmlForm class, we found a starting point to think for providing Action property to it, because the HtmlForm class is not sealed and thus can be further inherited. As like other .NET control, it also features a bunch of protected, overridable methods that you could exploit to customize the overall behavior. You can achieve this with the help of reflection. As we know that
Reflection allows you to access any property on an object, even though the property is marked protected, internal, or private.

Let us start the process of customization of HtmlForm; CHtmlForm is an inherited class from HtmlForm class under System.Web.UI.HtmlControls Namespace. HtmlForm class provides 4 protected as well as public methods; one of them is RenderAttributes method. The purpose of this method is to provide user defined implementation of default rendering process. It has a parameter of the type HtmlTextWriter, to render final values of all attributes like Method, target, name, id, action to the parent object; for our case, it is the page object.

The HtmlTextwriter object formulated the a new Action attribute by resolving the hidden GetActionAttribute of HtmlForm object.

CHtmlForms Methods & Properties Description
Action(Property) Gets or setd the target url of the form
RenderAttributed(Protected Override) Overridden to render custom Action attribute. The main purpose of the overriding is to grab the action attribute of the original form.
GetBaseObjectActionAttribute(Private) Returns the normal action attribute of a server form.
 

Following code snippet shows the new implementation of RenderAttributes on the new CHtmlForm class. The complete listing of the CHtmlForm class along with the ready-to-deploy assembly can be located on the attached zip file.

You can also look at the demo of the Server-side control at my DemoSite

public class CHtmlForm : System.Web.UI.HtmlControls.HtmlForm
{
protected override void RenderAttributes(System.Web.UI.HtmlTextWriter writer)
{
// Prepare our own action , method and name
writer.WriteAttribute("name", this.Name);
writer.WriteAttribute("method",
this.Method);
writer.WriteAttribute("action",
this.ResolveUrl(this.Action), true);
// Remove from HtmlForm, with changes to Action
this.Attributes.Remove("name");
this.Attributes.Remove("method");
this.Attributes.Remove("action");
string submitEvent = this.Page_ClientOnSubmitEvent;
// Now check the onsubmit event associated with Htmlform
if (submitEvent != null && submitEvent.Length > 0)
{
// ok.. Is this form has a "OnSubmit" event
if (this.Attributes["onsubmit"] != null)
{
submitEvent = submitEvent +
this.Attributes["onsubmit"];
this.Attributes.Remove("onsubmit");
}
//Add some new Attributes to make the new form little more rich
writer.WriteAttribute("language", "javascript");
writer.WriteAttribute("onsubmit", submitEvent);
}
writer.WriteAttribute("id",
this.ClientID);
// following is meant for HtmlContainerControl
this.ViewState.Remove("innerhtml");
// following is meant for HtmlControl
this.Attributes.Render(writer);
}
/******************************************endOf RenderAttributes******************************************/
}

Following code snippet shows the how final structure of a page will look like:

<%@ Register TagPrefix="dtform" Namespace="DerivedTool.WebControls" Assembly="DerivedTool.WebControls.HtmlForm" %>
<dtform:CHtmlForm Action="some_other_page.aspx" runat="server" ID="Chtmlform1" NAME="Chtmlform1">
<
asp:TextBox id="symbols" runat="server" value=></asp:TextBox>
<
asp:Button runat="server" Text="check Quote" ID="Button1" NAME="Button1"/>
</
dtform:CHtmlForm>

Instruction to Deploy

Though deployment of this serverside control is very easy and simple, I am providing following the steps to test the control with ASP.NET.

  1. Extract all the files in the zip file to a folder say c:\deriveForm
  2. Make C:\deriveform as a Virutal directory by giving name as derivedemo, if you have IIS server installed on your machine, other wise put the folder on a machine having IIS installed in it.
  3. Make a folder named bin under C:\deriveform and copy DerivedTool.WebControls.HtmlForm.dll to this new folder
  4. Open your browser and navigate to http://localhost/derivedemo/derivedForm.aspx
    Or navigate to http://<SERVER>/derivedemo/derivedForm.aspx if you dont have IIS on the current machine.