You need to submit the information on one page—a form, for example—to another. You might want to do this in order to use one page to collect form data and a second page to process it.
The ASP.NET model does not
“normally” support submitting a
form to any page other than itself. If you are willing to revert to
the classic ASP model by removing the
runat="server
" attribute from the form element,
the page can be submitted to any other page in the same manner, as
was done in ASP pages. We don’t recommend this
approach, however, because it does not take advantage of the built-in
functionality provided by the server controls to automatically
recreate the controls and populate them with the data submitted with
the form, making your coding tasks much harder.
There are two ways to solve this problem:
Use the Server.Transfer
method
in
a button click event handler in the code-behind of the first page to
transfer control to a second page, after saving the contents of the
first in session scope. Example 3-7 through
Example 3-12 show the .aspx
and
code-behind files for the application that implements this solution.
Use the Server.Transfer
method in a button click
event handler in the code-behind of the first page to transfer
control, along with the form contents in the
viewstate
, to the second page. This approach is
described at the end of Recipe 3.2.3 and includes some
give-and-take about why one approach might be favored over the other,
since neither is entirely perfect.
The first solution uses the Server.Transfer
method
to transfer control to a second page when a button click event
handler executes in the code-behind of the first page. As with all
the recipes in this chapter, this solution is most easily explained
in the context of an example. Before we delve into the example,
though, it’s useful to relate what happens in
ASP.NET when a form is submitted to itself and how the results of
that submittal can be used to solve the problem at hand.
The code-behind class for an ASP.NET page is a class that derives
from System.Web.UI.Page
. This class encapsulates
all data and server-side functionality for the page. When a form is
submitted to itself, ASP.NET instantiates the page class with the
data the user has submitted—that is, ASP.NET uses the data from
the form submittal to repopulate the controls to their former state.
This object can then be passed to any page, object, or method that
has a need for the data.
In order to store the Page
class instance
representing the first page to session state and retrieve it in the
second page, nothing special needs to be done to the
.aspx
file that collects the form data. In the
following example, the form is a simple one that contains three text
fields to capture the user’s first name, last name,
and age, and a button to submit the data to the server.
In the code-behind of the page that collects the form data, the data
members that are to be accessible to the processing page must have
their accessibility set to public
. Normally, the
accessibility would be set to protected
; however,
protected members are accessible only by the object itself and other
objects that derive from the parent class. In this solution, the
second page instantiates a class of type
CH03SubmitToAnother_FirstPageVB1
(the class of the
first page) rather than deriving from it.
A publicly accessible constant,
SES_SUBMITTED_DATA
, is provided to name the
variable stored in session scope to avoid the problem with
“magic values” and improve the
maintainability of the code. Magic values are
literals that have special significance in a system. As a general
rule, you should replace magic values with named constants in order
to prevent inadvertently varying them somewhere in your code. And
when the same magic values appear in many different classes, they
should be treated as global constants in your application rather than
declaring them repeatedly in each class.
In the click event handler you write for the Submit button, the page
object is placed in session scope to provide access to the data by
the page that will process the data. This is done by calling the
Add
method of the Session
object and passing it the name of the session-scoped variable (which
is defined by the constant SES_SUBMITTED_DATA
) and
a reference to the current page (represented by the
Me
keyword in VB.NET and this
in C#).
Control is then transferred to the page that will process the form
data by calling the Transfer
method of the
Server
class and providing it the URL of the page
to which control is to be transferred.
The second parameter for the Server.Transfer
method must be set to False
in this example to
avoid sending the form data in the viewstate
to
the processing page. See the alternate example for an approach to
using the viewstate
to pass the information to the
processing form.
The .aspx
file for the page that processes the
form data is nothing special. In this example, it simply has three
labels used to display the data from the submitted form. We use
Label
controls in our example to demonstrate that
the two pages can be quite different.
The code-behind for the form data processing page is also quite
simple. First, the page object previously stored in session scope is
recovered. The session variable is then destroyed to recover the
system resources used to store the data by setting it equal to
Nothing
(in VB) or null
(in
C#).
The text for the labels on the form processing the data is then set to the values from the previous page. Our example simply displays the values. A real-world example might store the data in a database and would require additional code to handle the data access.
A drawback to the technique of placing the page object in session scope is that it can consume a significant amount of server resources if there are a large number of users on the system and/or the page object is large.
A second approach to submitting a form to another page for processing
is to pass the information in the forms collection from the page
where the data is entered to the processing page. The forms
collection contains all of the data entered on the first page along
with any hidden fields, including the viewstate
,
of the first page. This approach is similar to the classic ASP
approach of posting to another page and may be desirable if the
project requirements dictate that session scope will not be used for
any data storage.
The .aspx
file for the sending and receiving
forms must be modified to disable the “machine
authentication check” that is normally performed on
the viewstate
. By default, ASP.NET adds an
encrypted value to the viewstate
that is unique to
the machine serving the page and the page itself. This allows ASP.NET
to verify that the viewstate
has not been tampered
with by the client. The “machine authentication
check” is disabled by setting the
EnableViewStateMac
attribute in the Page directive
to false
, as shown here:
<%@ Page language="c#" AutoEventWireup="false"
Codebehind="FormSubmitToAnother_FirstPageCS2.aspx.cs"
Inherits="ASPNetCookbook.CSExamples.FormSubmitToAnother_FirstPageCS2"
EnableViewStateMac="False"
%>
There is a significant drawback to using this approach. By disabling the “machine authentication check,” the page submitted back to the server can be altered by the client, resulting in page errors or access to data the client is not authorized to view. You’ll have to gauge whether this drawback outweighs the drawback of the previous technique—neither approach is perfect.
In the code-behind for the page that collects the data, the code in
the click event for the button is changed to simply transfer control
to the processing page with the second parameter of the
Server.Transfer
method set to
True
. This preserves the form data from the first
page and passes it to the second page for processing.
Private Sub btnSubmit_Click(ByVal sender As Object, _ ByVal e As System.Web.UI.ImageClickEventArgs) _ Handles btnSubmit.ClickServer.Transfer("CH03SubmitToAnother_SecondPageVB2.aspx", True)
End Sub 'btnSubmit_Click private void btnSubmit_Click(Object sender, System.Web.UI.ImageClickEventArgs e) {Server.Transfer("CH03SubmitToAnother_SecondPageCS2.aspx", true);
} // btnSubmit_Click
The code-behind for the page that processes the form data needs no code for this example. The reason is that, in this example, we are just displaying the data on the page. In a real-world example, the values of the form fields can be accessed just as they would if the form were submitted to itself.
ASP.NET will populate the controls on the second page with the data
from the forms collection of the first page. Controls that match by
name and datatype
will be populated with data from
the first form. If the controls on the second form have different
names or datatype
s, they are not populated. This
requires very tight linkage between the pages and can be a
significant maintenance issue.
The alternate approach described here may not work correctly in Version 1.1 of the .NET Framework due to a bug in the implementation. The bug is described in Microsoft Knowledge Base article 821758 (http://support.microsoft.com/default.aspx?kbid=821758). To work correctly, the hotfix described in article 821156 (http://support.microsoft.com/default.aspx?kbid=821156) must be applied.
Example 3-7. Submitting a form to another page—first page (.aspx)
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="CH03SubmitToAnother_FirstPageVB1.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH03SubmitToAnother_FirstPageVB1" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Form Submission To Another Page</title> <link rel="stylesheet" href="css/ASPNetCookbook.css"> </head> <body leftmargin="0" marginheight="0" marginwidth="0" topmargin="0"> <form id="frmSubmitToAnother" method="post" runat="server"> <table width="100%" cellpadding="0" cellspacing="0" border="0"> <tr> <td align="center"> <img src="images/ASPNETCookbookHeading_blue.gif"> </td> </tr> <tr> <td class="dividerLine"> <img src="images/spacer.gif" height="6" border="0"></td> </tr> </table> <table width="90%" align="center" border="0"> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr> <td align="center" class="PageHeading"> Form Submission To Another Page - Approach 1 (VB) </td> </tr> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr> <td align="center"> <table border="0"> <tr> <td class="LabelText">First Name: </td> <td> <asp:TextBox ID="txtFirstName" Runat="server" Columns="30" CssClass="LabelText" /> </td> </tr> <tr> <td class="LabelText">Last Name: </td> <td> <asp:TextBox ID="txtLastName" Runat="server" Columns="30" CssClass="LabelText" /> </td> </tr> <tr> <td class="LabelText">Age: </td> <td> <asp:TextBox ID="txtAge" Runat="server" Columns="30" CssClass="LabelText" /> </td> </tr> <tr> <td align="center" colspan="2"> <br> <asp:ImageButton ID="btnSubmit" Runat="server" ImageUrl="images/buttons/button_submit.gif" /> </td> </tr> </table> </td> </tr> </table> </form> </body> </html>
Example 3-8. Submitting form to another page code-behind—first page (.vb)
Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: CH03SubmitToAnother_FirstPageVB1.aspx.vb ' ' Description: This module provides the code behind for ' CH03SubmitToAnother_FirstPageVB1.aspx ' '***************************************************************************** Namespace ASPNetCookbook.VBExamples Public Class CH03SubmitToAnother_FirstPageVB1 Inherits System.Web.UI.Page 'controls on form Public txtFirstName As System.Web.UI.WebControls.TextBox Public txtLastName As System.Web.UI.WebControls.TextBox Public txtAge As System.Web.UI.WebControls.TextBox Protected WithEvents btnSubmit As System.Web.UI.WebControls.ImageButton 'the following constant is used to define the variable in session scope 'used to pass the form dataPublic Const SES_SUBMITTED_DATA As String = "SubmittedData"
'************************************************************************* ' ' ROUTINE: Page_Load ' ' DESCRIPTION: This routine provides the event handler for the page load ' event. It is responsible for initializing the controls ' on the page. '------------------------------------------------------------------------- Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load If (Not Page.IsPostBack) Then 'Put user code to initialize the page here End If End Sub 'Page_Load '************************************************************************* ' ' ROUTINE: btnSubmit_Click ' ' DESCRIPTION: This routine provides the event handler for the submit ' button click event. It is responsible passing the form ' data to another form for processing. '-------------------------------------------------------------------------Private Sub btnSubmit_Click(ByVal sender As Object, _
ByVal e As System.Web.UI.ImageClickEventArgs) _
Handles btnSubmit.Click
'add this object to session scope
Session.Add(SES_SUBMITTED_DATA, Me)
'transfer control to the page that will process the form data
'NOTE: Second parameter for the transfer method must be set false to
' avoid transferring the viewstate to the next form. If the
' viewstate is transferred without disabling the machine
' authentication check, an exception will be thrown due to a
' corrupted viewstate (would fail MAC check)
Server.Transfer("CH03SubmitToAnother_SecondPageVB1.aspx", False)
End Sub 'btnSubmit_Click
End Class 'CH03SubmitToAnother_FirstPageVB1 End Namespace
Example 3-9. Submitting a form to another page code-behind—first page (.cs)
//---------------------------------------------------------------------------- // // Module Name: CH03SubmitToAnother_FirstPageCS1.aspx.cs // // Description: This module provides the code behind for // CH03SubmitToAnother_FirstPageCS1.aspx // //**************************************************************************** using System; using System.Web.UI; namespace ASPNetCookbook.CSExamples { public class CH03SubmitToAnother_FirstPageCS1 : System.Web.UI.Page { // controls on form public System.Web.UI.WebControls.TextBox txtFirstName; public System.Web.UI.WebControls.TextBox txtLastName; public System.Web.UI.WebControls.TextBox txtAge; protected System.Web.UI.WebControls.ImageButton btnSubmit; // the following constant is used to define the variable in session scope // used to pass the form datapublic const String SES_SUBMITTED_DATA = "SubmittedData";
//************************************************************************ // // ROUTINE: Page_Load // // DESCRIPTION: This routine provides the event handler for the page // load event. It is responsible for initializing the // controls on the page. //------------------------------------------------------------------------ private void Page_Load(object sender, System.EventArgs e) { // wire the submit button click event this.btnSubmit.Click += new ImageClickEventHandler(this.btnSubmit_Click); if (!Page.IsPostBack) { // Put user code to initialize the page here } } // Page_Load //************************************************************************ // // ROUTINE: btnSubmit_Click // // DESCRIPTION: This routine provides the event handler for the submit // button click event. It is responsible processing the // form data. // //------------------------------------------------------------------------private void btnSubmit_Click(Object sender,
System.Web.UI.ImageClickEventArgs e)
{
// 'add this object to session scope
Session.Add(SES_SUBMITTED_DATA, this);
// transfer control to the page that will process the form data
// NOTE: Second parameter for the transfer method must be set false to
// avoid transferring the viewstate to the next form. If the
// viewstate is transferred without disabling the machine
// authentication check, an exception will be thrown due to a
// corrupted viewstate (would fail MAC check)
Server.Transfer("CH03SubmitToAnother_SecondPageCS1.aspx", false);
} // btnSubmit_Click
} // CH03SubmitToAnother_FirstPageCS1 }
Example 3-10. Submitting form to another page—second page (.aspx)
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="CH03SubmitToAnother_SecondPageVB1.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH03SubmitToAnother_SecondPageVB1" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Form Submission To Another Page - Second Page</title> <link rel="stylesheet" href="css/ASPNetCookbook.css"> </head> <body leftmargin="0" marginheight="0" marginwidth="0" topmargin="0"> <form id="frmSubmitToAnother" method="post" runat="server"> <table width="100%" cellpadding="0" cellspacing="0" border="0"> <tr> <td align="center"> <img src="images/ASPNETCookbookHeading_blue.gif"> </td> </tr> <tr> <td class="dividerLine"> <img src="images/spacer.gif" height="6" border="0"></td> </tr> </table> <table width="90%" align="center" border="0"> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr> <td align="center" class="PageHeading"> Form Submission To Another Page - Approach 1 (VB) </td> </tr> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr> <td align="center"> <table border="0"> <tr> <td colspan="2" align="center" class="PageHeading"> Data Submitted From Previous Form </td> </tr> <tr> <td class="LabelText">First Name: </td> <td class="LabelText"> <asp:Label ID="lblFirstName" Runat="server" /> </td> </tr> <tr> <td class="LabelText">Last Name: </td> <td class="LabelText"> <asp:Label id="lblLastName" Runat="server" /> </td> </tr> <tr> <td class="LabelText">Age: </td> <td class="LabelText"> <asp:Label ID="lblAge" Runat="server" /> </td> </tr> </table> </td> </tr> </table> </form> </body> </html>
Example 3-11. Submitting a form to another page code-behind—second page (.vb)
Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: CH03SubmitToAnother_SecondPageVB1.aspx.vb ' ' Description: This module provides the code behind for ' CH03SubmitToAnother_SecondPageVB1.aspx ' '***************************************************************************** Namespace ASPNetCookbook.VBExamples Public Class CH03SubmitToAnother_SecondPageVB1 Inherits System.Web.UI.Page 'controls on form Protected lblFirstName As System.Web.UI.WebControls.Label Protected lblLastName As System.Web.UI.WebControls.Label Protected lblAge As System.Web.UI.WebControls.Label '************************************************************************* ' ' ROUTINE: Page_Load ' ' DESCRIPTION: This routine provides the event handler for the page load ' event. It is responsible for initializing the controls ' on the page. '-------------------------------------------------------------------------Private Sub Page_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Dim submittedPage As CH03SubmitToAnother_FirstPageVB1
'get the form data from session scope then remove the session variable
'to conserve resources
submittedPage = _
CType(Session.Item(CH03SubmitToAnother_FirstPageVB1.SES_SUBMITTED_DATA), _
CH03SubmitToAnother_FirstPageVB1)
Session.Item(CH03SubmitToAnother_FirstPageVB1.SES_SUBMITTED_DATA) = Nothing
'set the values on this form with the data from the submitted form
lblFirstName.Text = submittedPage.txtFirstName.Text
lblLastName.Text = submittedPage.txtLastName.Text
lblAge.Text = submittedPage.txtAge.Text
End Sub 'Page_Load
End Class 'CH03SubmitToAnother_SecondPageVB1 End Namespace
Example 3-12. Submitting a form to another page code-behind—second page (.cs)
//---------------------------------------------------------------------------- // // Module Name: CH03SubmitToAnother_SecondPageCS1.aspx.cs // // Description: This module provides the code behind for // CH03SubmitToAnother_SecondPageCS1.aspx // //**************************************************************************** using System; namespace ASPNetCookbook.CSExamples { public class CH03SubmitToAnother_SecondPageCS1 : System.Web.UI.Page { // controls on form protected System.Web.UI.WebControls.Label lblFirstName; protected System.Web.UI.WebControls.Label lblLastName; protected System.Web.UI.WebControls.Label lblAge; //************************************************************************ // // ROUTINE: Page_Load // // DESCRIPTION: This routine provides the event handler for the page // load event. It is responsible for initializing the // controls on the page. // //------------------------------------------------------------------------private void Page_Load(object sender, System.EventArgs e)
{
CH03SubmitToAnother_FirstPageCS1 submittedPage;
// get the form data from session scope then remove the session
// variable to conserve resources
submittedPage =
(CH03SubmitToAnother_FirstPageCS1)
(Session[CH03SubmitToAnother_FirstPageCS1.SES_SUBMITTED_DATA]);
Session[CH03SubmitToAnother_FirstPageCS1.SES_SUBMITTED_DATA] = null;
// set the values on this form with the data from the submitted form
lblFirstName.Text = submittedPage.txtFirstName.Text;
lblLastName.Text = submittedPage.txtLastName.Text;
lblAge.Text = submittedPage.txtAge.Text;
} // Page_Load
} // CH03SubmitToAnother_SecondPageCS1 }