Cross-page Postbacks

Create Page-to-Page Logical Links in ASP.NET

One of the most common snags that novice developers face when they first approachASP.NET is the fact that ASP.NET pages contain exactly one form that always posts to itself. In HTML and classic ASP, instead, you can have as many forms as needed in a page, each posting to a different page. The ASP.NET single-form model brings some key advantages, however. In particular, the model enables the automatic state maintenance mechanism that, through the viewstate, makes it possible for all controls in a page to preserve their state across postbacks. The ASP.NET form server control doesn t expose an Action property through which you can set the page that will receive any posted data. In spite of this, though, a multiple-form model in ASP.NET is definitely possible.

In this article, I ll first review the options available to have multiple forms in ASP.NET pages, then explore an ASP.NET 2.0-specific feature named cross-page postback that, although through a different programming interface, provides an Action-like attribute to some posting controls.

 

Multiple Forms in ASP.NET Pages

To be precise, an ASP.NET page can host as many server forms and as many HTML forms as needed, albeit with some exceptions. In this context, a server form is a <form> tag decorated with the runat=server attribute. A similar tag originates an instance of the HtmlForm class once the ASP.NET page is dynamically compiled. An HTML form is a simple <form> tag devoid of the runat attribute that is treated like plain text by the ASP.NET parser. You can have as many HTML form tags as you want in your pages, with no limitations.

What about the allowed number of server forms instead? Many developers believe that only one server form is allowed in an ASP.NET page. This is not entirely correct, but it is certainly the most common setting. You can have as many server forms as needed in an ASP.NET page provided that only one is visible at a time. The following markup won t raise any exceptions:

<form runat="server" id="Form1">

 :

</form>

<form runat="server" id="Form2" visible="false">

 :

</form> 

The page contains two server forms, but only one will be rendered to markup. The page is parsed, compiled, and rendered successfully. When processing a request for the page, the page handler scans the Controls collection of the page class and asks each control to render out. After processing the first HtmlForm control, the page sets an internal flag to remember that one form object has been processed already. Can you guess what happens next? When a second HtmlForm control is found, the page reads the value of the internal flag and throws an HTTP exception if the flag is set. Any server control with the Visible property turned off is never rendered to HTML, and thus an additional HtmlForm (invisible) control won t throw any exceptions.

Nicely enough, you can programmatically toggle the value of the Visible attribute on multiple HtmlForm controls across postbacks. This technique, for example, can be employed to obtain page wizards in ASP.NET 1.x applications.

By adopting the standard single-form model, you don t significantly limit your programming power. Multiple forms, though, are sometimes needed although probably not frequently. You would need them, for example, for functionality hosted by the application pages, such as search or login capabilities. In these cases, you might want to insulate in different pages certain functionality, such as data search. If the target page needs some input parameters, how would you deal with that?

In ASP.NET 1.x, some tricks could be employed to pass values from one page to the next and redirect. If plain HTML forms are used, you can post the contents of the form to a particular page, but can t rely on the ASP.NET viewstate and programming model to retrieve data. The drawback is that you have to retrieve posted data using the Request.Form collection, just as you would do in classic ASP.

 

Cross-page Postbacks

In ASP.NET 2.0, the HtmlForm control still lacks the Action attribute, and the single-form model is the default. However, in ASP.NET 2.0 you can configure certain page controls in particular, those that implement the IButtonControl interface to post to a different target page. This technique is known as cross-page posting.

A Web page that can post data to another page differs from a regular ASP.NET page in just one aspect. One or more of its button controls have the PostBackUrl property set to a URL. A page can include one or more button controls and, generally, any combination of button controls and submit buttons. In this context, a button control is any server control that implements the IButtonControl interface (see Figure 1).

 

Name

Description

CausesValidation

Boolean value, indicates whether validation is performed when the control is clicked.

CommandArgument

Gets or sets an optional parameter passed to the button s Command event along with the associated CommandName.

CommandName

Gets or sets the command name associated with the button that is passed to the Command event.

PostBackUrl

Indicates the URL that will handle the postback triggered through the button control.

Text

Gets or sets the caption of the button.

ValidationGroup

Gets or sets the name of the validation group to which the button belongs.

Visible

Boolean value, indicates whether the button control is rendered.

Figure 1: The IButtonControl interface.

The following code shows a server form in a page named, say, default.aspx that posts to target.aspx when the user clicks the button:

<form runat="server">

   <asp:textbox runat="server" id="Data" />

   <asp:button runat="server" id="buttonPost"

           Text="Click"

           PostBackUrl="target.aspx" />

</form> 

If you add to the page another <asp:Button> tag without the PostBackUrl attribute, the page will post to itself as the user clicks the new button. Submit and postback buttons can happily co-exist in the same ASP.NET page.

When the PostBackUrl property is set, the button control emits a click event handler bound to a new JavaScript function. At the same time, the post mechanism of the parent form is altered via script so that if the user clicks the button, the browser redirects them exactly to the page bound to the control.

 

Retrieving Posted Values

How can the target page retrieve the posted values? How does it know about the structure of the original page? What about the viewstate?

Any page that contains a button control with cross-page postback capabilities is automatically added a new hidden field named __PREVIOUSPAGE. The field contains the state information to be used to serve the cross-page request in lieu of the original viewstate. As a result, the target page receives information about the controls of the caller page and their state.

You use the PreviousPage property on the Page class to reference the posting page and all its controls. The following code shows how the target page can retrieve the content of a textbox defined in the caller form:

Protected Sub Page_Load(ByVal sender As Object,

 ByVal e As EventArgs)

   Dim txt As TextBox

    txt = DirectCast(PreviousPage.FindControl("TextBox1"),

     TextBox)

   :

End Sub

By using the PreviousPage property on the Page class, you can access any input control defined on the posting page. Note, though, that access to input controls occurs indirectly through the services of the FindControl method. There are two main inconveniences here.

First, FindControl looks up controls only in the current naming container. This means that if the control you are looking for is contained in a template, or is part of a master page region, you must first get a reference to the container, then search the container to locate the control. Second, a cast is always required to obtain the correct reference object to the control.

Nevertheless, the posting page class has a number of protected properties, each referencing a contained control. Why can t you access these properties directly? The target page has only the PreviousPage property to access the posting page. The PreviousPage property is of type Page and, as such, it can t provide access to members specific to a derived page class. In addition, the members of interest here are marked as protected and are not public. To avoid using FindControl altogether, a different approach is required.

 

The @PreviousPageType Directive

If you can t make assumptions about the URL or the type of pages calling into a given page via cross-page postback, then FindControl is the only safe option to implement cross postings. If you know exactly which pages can post to a given page, you can use the @PreviousPageType directive to instruct the page parser to generate a code where the type of the PreviousPage property matches exactly the type of the posting page.

Add the following directive in the target page:

<%@ PreviousPageType VirtualPath="default.aspx" %> 

The @PreviousPageType directive can accept either of two attributes: VirtualPath or TypeName. The former points to the URL of the posting page; the latter indicates the type of the calling page.

If the posting page is named, say, default.aspx, a target page that includes the preceding directive has the PreviousPage property declared of type ASP.default_aspx instead of Page. In this way, the code-behind of the target page can invoke any member that is public on default.aspx. Unfortunately, the list doesn t include any properties that represent constituent controls child controls, in fact, are marked as protected. In the code-behind of the calling page, you define additional public members to expose child controls:

Public ReadOnly Property External_TextBox1 As TextBox

  Get

     Return TextBox1

  End Get

End Property

The External_TextBox1 property simply exposes to the outside world the TextBox1 control of the page. Thanks to this little trick, a target page with the @PreviousPageType directive can run the following strong-typed code to retrieve posted values:

Dim theText As String

theText = PreviousPage.External_TextBox1.Text

It goes without saying that External_XXX is an arbitrary naming convention. You can use any name you want to expose as public properties constituent controls of the posting page.

What if the same target page can be invoked by multiple caller pages in the same application? In this case, @PreviousPageType doesn t work because there are multiple URLs to specify. In this case, you should resort to the other format of the directive, based on the TypeName attribute:

<%@ PreviousPageType TypeName="MyApp.BasePage" %> 

Suppose the target page provides a well-known service, say a search service. The number of input arguments is known and well-defined. Each caller page needs to provide these arguments; therefore, all caller pages have something in common a set of input controls to accept input values for the search. You define a base page class with a contracted set of controls (e.g., a TextBox with a well-known name to accept the search string). All caller pages will inherit from this base page class, thus sharing a common set of properties that the target page can invoke polymorphically.

 

Common Snags of Cross Postings

A page that serves as the target of a cross-page posting is a regular ASP.NET page; as such, it can be invoked also through the address bar or a hyperlink. If necessary, you can limit calls to the target page only to cross posts. Here s how to proceed:

If PreviousPage Is Nothing Then

  Response.Write("Sorry, you can only invoke me

    through cross-page posting.")

  Response.End()

  Return

End If

The code goes in the Page_Load event handler of the target page s code-behind class.

The Page class also lists a Boolean IsCrossPagePostBack property. The property seems to indicate that the target page is being invoked through a cross-page posting. The property, instead, indicates whether the caller page has started a cross posting and always returns False on the target page. What s the role of the property, then, if you can detect a cross posting by looking at the PreviousPage property on the target page?

PreviousPage is not null in two cases: when a cross-posting is made and when the target page is reached through Server.Transfer. Only in the former case, though, IsCrossPagePostBack is invoked if PreviousPage is True. The table in Figure 2 summarizes the possible scenarios.

 

PreviousPage

IsCrossPagePostback on PreviousPage

IsCrossPagePostBack on target page

Hyperlink

Nothing

False

False

Address bar

Nothing

False

False

Cross-page posting

Object

True

False

Server.Transfer

Object

False

False

Figure 2: Ways to invoke a target page.

Finally, what happens if the caller page contains validators? As long as validators work on the client side, the scenario is no way different from any other classic postback page; the postback won t occur if input is not valid. If client-side validation is disabled, or if a server-side validator is employed, the transition to the target page occurs anyway, regardless of the results of the validation process. In this case, though, by checking the IsValid property on PreviousPage, you can detect whether all was OK with the input from the caller page:

If Not PreviousPage.IsValid Then

  Response.Write("Sorry, the original page

    contains invalid input.")

  Response.End()

  Return

End If

Based on the response of the IsValid property, the target page can decide to refuse the call.

 

Conclusion

Passing values from one page to another is a task that can be accomplished in a variety of ways: using cross-page posting, server transfer, HTML forms, or query strings. Which is the most effective? Cross-page posting offers a familiar programming model, but can potentially move a significant chunk of data through the __PREVIOUSPAGE hidden field. In many cases, the target page simply needs a few parameters to start working. If this is the case, HTML client forms might be more effective in terms of data being moved. Be aware, though, that HTML forms require an ASP-like programming model which is not a real issue if it s limited to a couple of pages in the application.

The source code accompanying this article is available for download (in both VB and C#).

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish