Cross-page Postbacks
Create Page-to-Page Logical Links in ASP.NET
October 30, 2009
One of the most common snags that novice developers facewhen they first approachASP.NET is the fact that ASP.NET pages contain exactlyone form that always posts to itself. In HTML andclassic ASP, instead, you can have as many forms as needed in a page, eachposting to a different page. The ASP.NET single-form model brings some keyadvantages, however. In particular, the model enables the automatic statemaintenance mechanism that, through the viewstate, makes it possible for allcontrols in a page to preserve their state across postbacks. The ASP.NET formserver control doesn t expose an Action property through which you can set thepage that will receive any posted data. In spite of this, though, a multiple-formmodel in ASP.NET is definitely possible.
In this article, I ll first review the options availableto have multiple forms in ASP.NET pages, then explore an ASP.NET 2.0-specificfeature named cross-page postback that, although through a differentprogramming interface, provides an Action-like attribute to some postingcontrols.
Multiple Forms in ASP.NET Pages
To be precise, an ASP.NET page can host as many serverforms and as many HTML forms as needed, albeit with some exceptions. In thiscontext, a server form is a tag decorated with the runat=serverattribute. A similar tag originates an instance of the HtmlForm class once theASP.NET page is dynamically compiled. An HTML form is a simple tagdevoid of the runat attribute that is treated like plain text by the ASP.NETparser. You can have as many HTML form tags as you want in your pages, with nolimitations.
What about the allowed number of server forms instead? Manydevelopers 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. Youcan have as many server forms as needed in an ASP.NET page provided that onlyone is visible at a time. The following markup won t raise any exceptions:
: :
The page contains two server forms, but only one will berendered to markup. The page is parsed, compiled, and rendered successfully.When processing a request for the page, the page handler scans the Controlscollection of the page class and asks each control to render out. Afterprocessing the first HtmlForm control, the page sets an internal flag toremember that one form object has been processed already. Can you guess whathappens next? When a second HtmlForm control is found, the page reads the valueof the internal flag and throws an HTTP exception if the flag is set. Anyserver control with the Visible property turned off isnever rendered to HTML, and thus an additional HtmlForm (invisible) control won tthrow any exceptions.
Nicely enough, you can programmatically toggle the valueof the Visible attribute on multiple HtmlForm controlsacross postbacks. This technique, for example, can be employed to obtain pagewizards in ASP.NET 1.x applications.
By adopting the standard single-form model, you don tsignificantly limit your programming power. Multiple forms, though, aresometimes needed although probably not frequently. You would need them, forexample, for functionality hosted by the application pages, such as search orlogin capabilities. In these cases, you might want to insulate in differentpages certain functionality, such as data search. If the target page needs someinput parameters, how would you deal with that?
In ASP.NET 1.x, some tricks could be employed to passvalues 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 onthe ASP.NET viewstate and programming model to retrieve data. The drawback isthat you have to retrieve posted data using the Request.Form collection, just asyou would do in classic ASP.
Cross-page Postbacks
In ASP.NET 2.0, the HtmlForm control still lacks theAction attribute, and the single-form model is the default. However, in ASP.NET2.0 you can configure certain page controls in particular, those thatimplement 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 froma regular ASP.NET page in just one aspect. One or more of its button controlshave the PostBackUrl property set to a URL. A page can include one or morebutton controls and, generally, any combination of button controls and submitbuttons. In this context, a button control is any server control thatimplements 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: TheIButtonControl 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:
If you add to the page another tag without the PostBackUrl attribute, the page will post to itself as the userclicks the new button. Submit and postback buttons can happily co-exist in thesame ASP.NET page.
When the PostBackUrl property is set, the button controlemits a click event handler bound to a new JavaScript function. At the sametime, the post mechanism of the parent form is altered via script so that ifthe user clicks the button, the browser redirects them exactly to the pagebound to the control.
Retrieving Posted Values
How can the target page retrieve the posted values? Howdoes it know about the structure of the original page? What about theviewstate?
Any page that contains a button control with cross-pagepostback capabilities is automatically added a new hidden field named__PREVIOUSPAGE. The field contains the state information to be used to servethe cross-page request in lieu of the original viewstate. As a result, thetarget page receives information about the controls of the caller page andtheir state.
You use the PreviousPage property on the Page class toreference the posting page and all its controls. The following code shows howthe 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, youcan access any input control defined on the posting page. Note, though, thataccess to input controls occurs indirectly through the services of theFindControl method. There are two main inconveniences here.
First, FindControl looks up controls only in the currentnaming container. This means that if the control you are looking for iscontained in a template, or is part of a master page region, you must first geta reference to the container, then search thecontainer to locate the control. Second, a cast is always required to obtain thecorrect reference object to the control.
Nevertheless, the posting page class has a number ofprotected properties, each referencing a contained control. Why can t youaccess these properties directly? The target page has only the PreviousPageproperty to access the posting page. The PreviousPage property is of type Pageand, as such, it can t provide access to members specific to a derived pageclass. In addition, the members of interest here are marked as protected andare not public. To avoid using FindControl altogether, a different approach isrequired.
The @PreviousPageType Directive
If you can t make assumptions about the URL or the type ofpages calling into a given page via cross-page postback, then FindControl isthe only safe option to implement cross postings. If you know exactly whichpages can post to a given page, you can use the @PreviousPageType directive toinstruct the page parser to generate a code where the type of the PreviousPageproperty 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 twoattributes: VirtualPath or TypeName. The former points to the URL of the postingpage; the latter indicates the type of the calling page.
If the posting page is named, say, default.aspx, a targetpage that includes the preceding directive has the PreviousPage propertydeclared of type ASP.default_aspx instead of Page. In this way, the code-behindof the target page can invoke any member that is public on default.aspx.Unfortunately, the list doesn t include any properties that representconstituent controls child controls, in fact, are marked as protected. In thecode-behind of the calling page, you define additional public members to exposechild controls:
Public ReadOnly Property External_TextBox1 As TextBox Get Return TextBox1 End GetEnd Property
The External_TextBox1 property simply exposes to theoutside world the TextBox1 control of the page. Thanks to this little trick, atarget page with the @PreviousPageType directive can run the followingstrong-typed code to retrieve posted values:
Dim theText As StringtheText = PreviousPage.External_TextBox1.Text
It goes without saying that External_XXX is an arbitrarynaming convention. You can use any name you want to expose as public propertiesconstituent controls of the posting page.
What if the same target page can be invoked by multiplecaller pages in the same application? In this case, @PreviousPageType doesn twork because there are multiple URLs to specify. In this case, you shouldresort 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, saya search service. The number of input arguments is known and well-defined. Eachcaller page needs to provide these arguments; therefore, all caller pages havesomething in common a set of input controls to accept input values for thesearch. You define a base page class with a contracted set of controls (e.g., aTextBox with a well-known name to accept the search string). All caller pageswill inherit from this base page class, thus sharing a common set of propertiesthat the target page can invoke polymorphically.
Common Snags of Cross Postings
A page that serves as the target of a cross-page postingis a regular ASP.NET page; as such, it can be invoked also through the addressbar or a hyperlink. If necessary, you can limit calls to the target page onlyto 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() ReturnEnd If
The code goes in the Page_Load event handler of the targetpage s code-behind class.
The Page class also lists a Boolean IsCrossPagePostBackproperty. The property seems to indicate that the target page is being invokedthrough a cross-page posting. The property, instead, indicates whether thecaller page has started a cross posting and always returns False on the targetpage. What s the role of the property, then, if you can detect a cross postingby looking at the PreviousPage property on the target page?
PreviousPage is not null in two cases: when across-posting is made and when the target page is reached throughServer.Transfer. Only in the former case, though, IsCrossPagePostBack is invokedif PreviousPage is True. The table in Figure 2summarizes the possible scenarios.
PreviousPage
Hyperlink | Nothing | False | False |
Address bar | Nothing | False | False |
Cross-page posting | Object | True | False |
Server.Transfer | Object | False | False |
Figure 2: Ways toinvoke a target page.
Finally, what happens if the caller page containsvalidators? As long as validators work on the client side, the scenario is noway different from any other classic postback page; the postback won t occur ifinput is not valid. If client-side validation is disabled, or if a server-sidevalidator is employed, the transition to the target page occurs anyway,regardless of the results of the validation process. In this case, though, bychecking the IsValid property on PreviousPage, you can detect whether all wasOK with the input from the caller page:
If Not PreviousPage.IsValid Then Response.Write("Sorry, the original page contains invalid input.") Response.End() ReturnEnd If
Based on the response of the IsValid property, the targetpage can decide to refuse the call.
Conclusion
Passing values from one page to another is a task that canbe accomplished in a variety of ways: using cross-page posting, servertransfer, HTML forms, or query strings. Which is the most effective? Cross-pageposting offers a familiar programming model, but can potentially move asignificant chunk of data through the __PREVIOUSPAGE hidden field. In manycases, the target page simply needs a few parameters to start working. If thisis the case, HTML client forms might be more effective in terms of data beingmoved. Be aware, though, that HTML forms require an ASP-like programming modelwhich is not a real issue if it s limited to a couple of pages in theapplication.
The source code accompanyingthis article is available for download (in both VB and C#).
About the Author
You May Also Like