Printer Friendly Version      Send     
Click to Rate and Give Feedback
Related Articles
We introduce you to the benefits of building composite applications with the Composite Application Guidance for WPF from Microsoft patterns & practices.

By Glenn Block (September 2008)
ADO.NET Data Services provide Web-accessible endpoints that allow you to filter, sort, shape, and page data without having to build that functionality yourself.

By Shawn Wildermuth (September 2008)
See how routed events and routed commands in Windows Presentation Foundation form the basis for communication between the parts of your UI.

By Brian Noyes (September 2008)
Technology changes at a lightening-fast pace. This month Howard Dierking considers how the rapid changes affect developer priorities and magazine focus.

By Howard Dierking (September 2008)
More ...
Articles by this Author
This month Dino builds a service layer that authenticates users of Silverlight 2 and ASP.NET AJAX services to prevent illegal access to sensitive back-end services.

By Dino Esposito (September 2008)
Dino Esposito compares the use of AJAX patterns and DOM manipulations to the use of the ASP.NET partial rendering engine.

By Dino Esposito (August 2008)
In this installment, the author provides an enhanced implementation of the BST pattern and compares it to HTM solutions.

By Dino Esposito (July 2008)
AJAX is meant to go beyond mere partial page rendering. Find out where Dino Esposito thinks dynamic pages are headed in the future with ASP.NET AJAX.

By Dino Esposito (June 2008)
This month we begin a look at the Single Page Interface (SPI) model and some design patterns for designing AJAX applications.

By Dino Esposito (May 2008)
This month, use nested ListView controls to create hierarchical views of data and extend the eventing model of the ListView by deriving a custom ListView class.

By Dino Esposito (April 2008)
This month Dino Esposito shows you how to get Windows-style modal dialog boxes for your Web applications thanks to the Ajax Control Toolkit and some clever coding.

By Dino Esposito (Launch 2008)
This month Dino looks at AJAX control extenders again, adding more advanced features including masked editing and autocompletion.

By Dino Esposito (February 2008)
More ...
Popular Articles
The goal of the ADO.NET Data Services Framework is to create a simple REST-based framework for exposing and consuming data-centric services easily.

By Elisa Flasko and Mike Flasko (August 2008)
We introduce you to the benefits of building composite applications with the Composite Application Guidance for WPF from Microsoft patterns & practices.

By Glenn Block (September 2008)
Howard Dierking talks to the inventor of C++, Bjarne Stroustrup, about language zealots, the evolution of programming, and what’s in the future of programming.

By Howard Dierking (April 2008)
Learn how you can peer-enable business applications by allowing them to share state in a serverless peer network.

By Kevin Hoffman (July 2008)
More ...
Read the Blog
SQL Server 2008 supports a new data type, HierarchyID, that helps solve some of the problems in modeling and querying hier­archical information. In the September 2008 issue of MSDN Magazine, Kent Tegels introduces you to the ...
Read more!
Many people using SharePoint technologies don't realize that there is auditing support built directly into the Windows SharePoint Services (WSS) 3.0 platform. In the September 2008 issue of MSDN Magazine, Ted Pattison walks you through a ...
Read more!
The September 2008 issue of MSDN Magazine is now available online. Here's what's in the issue: Hierarchy ID: Model ...
Read more!
Silverlight 2 features a rich and robust control model that is the basis for the controls included in the platform and for third-party control packages. You can also use this control model to build controls of your own. In the August 2008 issue of MSDN Magazine, Jeff Prosise describes how to ...
Read more!
In the August 2008 issue of MSDN Magazine, Matt Milner covers several topics regarding development with Windows Workflow Foundation, some that are intended to address specific reader questions, such as how to safely share a persistence database ...
Read more!
LINQ is a powerful tool enabling quick filtering data based on a standard query language. It can tear through a structured set of data using a simple and straightforward syntax. In the August 2008 issue of MSDN Magazine, Jared Parsons demonstrates a ...
Read more!
More ...
Cutting Edge
ASP.NET Forms
Dino Esposito

Forms are an essential piece of ASP.NET—the ASP.NET Web programming model itself wouldn't be possible without forms. The use of forms is not constrained in pure HTML, but it is subject to some restrictions in ASP.NET. In ASP.NET pages, a single form can post to itself, and the model provides for generalized control state management and postback events. Writing ASP.NET apps is easy and effective because of the single form model.
The restrictions on forms in ASP.NET may sound weird and arbitrary at first, but actually the model is quite straightforward to work with. However, there is one real-world scenario that the ASP.NET 1.x form model doesn't address: having multiple, highly specialized forms in the same page. For example, you can't have a search box that posts to a different page.
In the May 2003 issue of MSDN®Magazine, I wrote a column about forms programming in ASP.NET 1.x (see Cutting Edge: Form-based Programming in ASP.NET. With the introduction of ASP.NET 2.0, a few things have changed that specifically address the theme of posting to different pages. This month I'll discuss forms programming in ASP.NET 2.0.

Rendering Forms
Let's start exploring the universe of forms in ASP.NET with some consideration of how forms (and contained controls) are actually rendered. In an ASP.NET page, the <form> tag can be the child of various container controls such as <table>, <div>, or <body>; however, in most pages it is simply the child of <body>. If a non-container control (TextBox, for example) is placed outside the form tag, an HttpException is thrown at run time (no check is made for this at compile time). Take a look at the following code, which is excerpted from the TextBox's AddAttributesToRender method:
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
    if (this.Page != null)  this.Page.VerifyRenderingInServerForm(this);
    ...
}
A call to Page's VerifyRenderingInServerForm method does the job. (Be aware of this behavior when you write your own custom server controls.)

The HtmlForm Class
HtmlForm inherits from HtmlContainerControl, which provides the form with the capability of containing child controls. HtmlForm provides programmatic access to the HTML <form> element on the server through the set of properties listed in Figure 1. As you can see, the interface changes in HtmlForm between ASP.NET 1.x and ASP.NET 2.0 are limited to a few properties.
A form must have a unique name. One is assigned automatically by ASP.NET if you don't specify one. You can set the form's identifier by using either the ID or Name property. If both are set, the ID attribute takes precedence. It is important to note, though, that any programmatic use of the Name attribute compromises the XHTML compliance of the page. In XHTML, elements are identified by ID and not by Name. So generally speaking, you're better off relying on the ID property.
The parent object of the form is the outer container control that has the runat attribute set. If such a control doesn't exist, the page object is set as the parent. Typical containers for the server form are <table> or <div> if they are marked as server-side objects.
Figure 2 lists some of the methods available on the HtmlForm class that you'll be using most often. All the methods listed in the table are inherited from the base System.Web.UI.Control class. Note that the FindControl method searches only among the form's direct children. Controls belonging to an inner container or a child of a form's child control are not found.

Managing Multiple Forms
Generally, embracing the single-form model and renouncing system support for multiple forms is not a big sacrifice. Some pages, though, would have a more consistent and natural design if they could define multiple forms—at least, forms with logically related groups of input controls. For example, think of a page that provides some information to users, but also needs to supply an additional form containing a search or a login box.
You could incorporate search and login functionalities in ad hoc classes and call those classes from within the page displayed to the user. This is not necessarily the best way to factor out your code, however. If you're porting some old code to ASP.NET, you might find it easier to insulate login or search code within a dedicated page. But how do you force the page to post data to this page?
In the single-form model, each page always posts to itself and doesn't supply a hook for developers to set the final destination of the postback. In HTML and ASP programming, the Action property of the form is simply not exposed on the ASP.NET HtmlForm class. The single-form model is so closely integrated with the ASP.NET platform that you can only take or leave it—or, as an additional choice, code the old ASP way without server forms. As you'll see later, in ASP.NET 2.0, posting data to a different page is possible, but the implementation of the feature passes through some new capabilities of button controls. For now, let's see what is involved in using additional HTML non-server forms.
In ASP.NET, an exception is thrown if multiple HtmlForm controls are being rendered. When the first HtmlForm control in the page is rendered, a Boolean flag is flipped to true. The flag thus indicates whether any other HtmlForm control has been rendered. When another HtmlForm attempts to render, the flag will already be true causing an exception to be thrown.
Nothing bad happens if the Web Form contains one server form and any number of <form> tags devoid of the runat attribute. Without the runat attribute, any tags become pure and simple HTML markup and are rendered verbatim (see Figure 3).
The page contains two forms, the second of which is an HTML form and lacks the runat="server" attribute. As such, it is completely ignored by ASP.NET. The markup served to the browser legally contains two <form> elements, each pointing to a different action URL.
Although functional, this code has a major drawback: you can't use the ASP.NET programming model to retrieve posted data in the action page of the client form. When writing search.aspx, the action page for the HTML client form, you can't rely on page controls that read and update their state from view state and posted values. (The apparent statefulness of ASP.NET server controls is obtained by making pages post to themselves.) To know what's been posted to search.aspx, you must resort to the old-fashioned yet effective ASP model of looking directly into the collection of posted data:
protected void Page_Load(object sender, EventArgs e)
{
    // Use the Request to retrieve posted data
    string textToSearch = Request.Form["Keyword"].ToString();
    ...
    // Use standard ASP.NET to populate the page UI
    KeywordBeingUsed.Text = textToSearch;
}
You use the protocol-specific collections of the HttpRequest object (available from Page.Request as well as HttpContext.Current.Request) to retrieve posted data—Form if POST is used, QueryString if GET is used, or Params if you want combined access to Form, QueryString, ServerVariables, and Cookies. The HttpRequest object is populated with data before the page is created, so any call to Page.Request works from any of the page's events. In self-posting ASP.NET pages, you don't need to use Request because you can rely on a strongly typed programming model, but the old, faithful HttpRequest object is still there for you to use if you need it.
It is also interesting to note that when the user clicks on the Search button, the search.aspx page is invoked, and it receives only the values posted through the HTML form. No view state is posted, and no extra data is passed. If you have to post to another page, the old-fashioned approach is still going to be the most effective performance-wise. As you'll see later in this column, the cross-page posting feature of ASP.NET 2.0 still moves around a fairly large, view-state-like field.

Multiple <form> Tags
If multiple server forms are declared in the same Web Form, an exception is thrown. Not so obvious, and not very well known, is that a Web Form can actually contain as many server-side forms as needed, so long as only one is visible and rendered at a time. For example, a page with three <form> tags marked runat="server" is allowed, but only one form's Visible property can be set to true. By playing with the Visible property of the HtmlForm class, you can change the active server form during the page's lifetime. This little trick doesn't really solve the problem of having multiple active forms, but it can still be helpful at times.
Let's consider the page in Figure 4. As you can see, all <form> tags are marked runat="server", but only the first is visible. Mutually exclusive forms are great for implementing wizards in ASP.NET 1.x. By toggling the form's visibility in button event handlers, you can obtain a wizard-like behavior, as depicted in Figure 5.
Figure 5 Wizard-Like Page in Action 
The trick is rather useless in ASP.NET 2.0 because you find two new controls, MultiView and Wizard, ready for the job. The MultiView control employs logic nearly identical to multiple exclusive forms, except that it relies on panels rather than full-fledged forms. MultiView allows you to define multiple and mutually exclusive HTML panels. The control provides an API for you to toggle the visibility of the various panels and ensure that exactly one is active and visible at a time. The MultiView control doesn't provide a built-in user interface. The Wizard control is just a MultiView plus some wizard-like predefined UI blocks. I covered the Wizard control in the November 2004 issue of MSDN Magazine (see Cutting Edge: The ASP.NET 2.0 Wizard Control).

Cross-Page Posting
ASP.NET 2.0 offers a new built-in mechanism to override the normal processing cycle and to allow a page to post back to another page. In general, postbacks occur in one of two ways: through a Submit button or through a script. Submissions through a button typically point automatically to the address that the posting form indicates. More flexibility is possible when the post occurs through a script. In ASP.NET 2.0, you can configure certain page controls—in particular, those that implement the new IButtonControl interface—to post to a different target page. This is referred to as cross-page posting.
Core controls that implement the IButtonControl interface are Button, ImageButton, and LinkButton. In general, by implementing IButtonControl, any custom control can act like a button on a form. The IButtonControl interface is a clear example of the refactoring process that ASP.NET went through in the transition from version 1.x to 2.0. The IButtonControl interface now groups a few properties that most button controls (including some HTML button controls) support since ASP.NET 1.x. In addition, a few new properties heralding new functionality have been added, like PostBackUrl and ValidationGroup. Figure 6 details the IButtonControl interface. The following code snippet shows how to proceed:
<form runat="server">
    <asp:textbox runat="server" id="Data" />
    <asp:button runat="server" id="buttonPost" Text="Click" 
        PostBackUrl="target.aspx" />
</form>
When the PostBackUrl property is set, the ASP.NET runtime binds the corresponding HTML element of the button control to a new JavaScript function. Instead of the __doPostback function normally used, it uses the new WebForm_DoPostBackWithOptions function. The button defined previously renders the following markup:
<input type="submit" name="buttonPost" id="buttonPost" value="Click"
     onclick="javascript:WebForm_DoPostBackWithOptions(
         new WebForm_PostBackOptions("buttonPost", "", 
         false, "", "target.aspx", false, false))" />
As a result, when the user clicks the button, the current form posts its content to the specified target page. What about the view state? When the page contains a control that does cross-page posting, a new hidden field named __PREVIOUSPAGE is created. The field contains information about the posting page. The target page uses this information in order to build a stateful reference to the calling page object.
In the target page, you use the PreviousPage property, a new property on the Page class, to reference the posting page and all of its controls. Here's the codebehind for a sample target page that retrieves the content of a TextBox defined in the form:
protected void Page_Load(object sender, EventArgs e)
{
    // Retrieves some posted data
    TextBox txt = (TextBox) PreviousPage.FindControl("TextBox1");
    ...
}
By using the PreviousPage property on the Page class, you can access any input control defined on the posting page. Access to input controls is weakly typed and occurs indirectly through the FindControl method. The problem here lies in the fact that the target page doesn't know anything about the type of the posting page. PreviousPage is declared as a property of type Page. As such, it can't provide access to members specific to a derived page class.
Furthermore, note that FindControl looks up controls only in the current container. If the control you are looking for lives inside another control (say, a template), you must first get a reference to the container and then search the container to find the control. To avoid using FindControl altogether, a different approach is required.
To retrieve values on the posting page, FindControl represents your only safe option if you don't know in advance which page will be invoking your target. However, when using cross-page posting in the context of an application, there's a good chance that you know exactly who will be calling the page and how. In this case, you can take advantage of the PreviousPageType directive to cause the target page's PreviousPage property to be typed to the source page class. In the target page, you add the following directive:
<%@ PreviousPageType VirtualPath="crosspostpage.aspx" %>
The directive can accept either of two mutually exclusive attributes—VirtualPath or TypeName. VirtualPath points to the URL of the posting page. TypeName indicates the type of the calling page. The PreviousPageType directive makes the PreviousPage property on the target page return an object of the same type as the page at the given path (or of the specified type if the TypeName attribute is used). This fact alone, though, is not sufficient to let you access input controls directly. In ASP.NET, each page class contains protected members that represent child controls. Unfortunately, you can't call a protected member of a class from an external class. In fact, only derived classes can access protected members of the parent class.
To work around this issue, in the caller page you must add public properties that expose any information you want posted pages to access. For example, imagine that crosspostpage.aspx contains a TextBox named _textBox1. To make it accessible from within a target page, you add the following code to the codebehind class:
public TextBox TextBox1
{
    get { return _textBox1; }
}
The new TextBox1 property on the page class wraps and exposes the internal TextBox control. In light of this code, the target page can now execute the following code:
Response.Write(PreviousPage.TextBox1.Text);
Being the potential target of a cross-page call doesn't automatically make a target page a different kind of page. There's always the possibility that the target page is invoked on its own, for example, through a hyperlink. When this happens, the PreviousPage property returns null and other postback-related properties (like IsPostBack) assume the usual values. If you have such a dual page, it's a good idea to add some extra code to discern the page behavior. The following code hosted in the Page_Load event makes the page work only through cross-page calls:
if (PreviousPage == null)     
{
    Response.Write("Sorry, that's not the right way to invoke me.");
    Response.End();
    return;
}

Redirecting Users to Another Page
In addition to the PostBackUrl property of button controls, ASP.NET provides another mechanism for transferring control and values from one page to another: the Server.Transfer method. When you call this method, the URL of the new page is not reflected by the browser's address bar because the transfer takes place entirely on the server—no client redirection ever occurs. The following code shows how to use the method in order to direct a user to another page:
protected void Button1_Click(object sender, EventArgs e)
{
    Server.Transfer("targetpage.aspx");
}
Note that any code following the call to Transfer in the page is never executed. In the end, Transfer is just a page-redirect method. However, it is particularly efficient for two reasons. First, no round-trip to the client is requested as is the case, for example, with Response.Redirect. Second, the same HttpApplication that was serving the caller request is reused to serve the new page request, thus limiting the impact on the ASP.NET infrastructure.
In ASP.NET 1.x, the spawned page can access the page object representing its caller by using the Handler property of the HTTP context object, as shown here:
Page caller = (Page) Context.Handler;
Because the Handler property returns a valid instance of the referrer page object, the spawned page can access all of its public members. It cannot directly access the controls because of the protection level already discussed.
This programming model also works in ASP.NET 2.0. However, in ASP.NET 2.0, things are simplified and using Handler is no longer needed. You can use the same programming model of cross-page postings and rely on a non-null PreviousPage property and the @PreviousPageType directive for strongly typed access to input fields. How can a page detect whether it's being called through a server transfer or through a cross-page postback? In both of these cases, PreviousPage is not null, but the Page.IsCrossPagePostBack on the PreviousPage object is true for a cross-page posting and false in case of a server transfer.

Conclusion
Passing values from one page to another is a task that can be accomplished in a variety of ways—cross-page posting, server transfer, HTML forms, cookies, session-state, and query strings, among others. Which one is the most effective? In ASP.NET 2.0, cross-page posting and server transfer offer a familiar programming model, but potentially move a significant chunk of data through the view state. Whether this information is really needed depends on the characteristics of the target page. In many cases, the target page just needs to receive a few parameters to start working. If this is so, HTML client forms might be more effective in terms of moving data. HTML forms, though, require an ASP-like programming model.
ASP.NET 2.0 adds a few new properties to the HtmlForm class, but doesn't change its core behavior, so self-posting pages are still the primary method of writing pages in ASP.NET. You can still mix client and server forms and also host multiple server forms provided that only one is visible at a time.

Send your questions and comments for Dino to  cutting@microsoft.com.


Dino Esposito is an instructor and consultant based in Rome, Italy. Author of Programming ASP.NET and the new book Introducing ASP.NET 2.0 (both from Microsoft Press), he spends most of his time teaching classes on ASP.NET and ADO.NET and speaking at conferences. Get in touch with Dino at cutting@microsoft.com or join the blog at weblogs.asp.net/despos.

© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.