Sunday, January 6, 2008

Using the PreviousPage Property with Multiple Previous Pages

Scenario

Many of you may be familiar with the PreviousPage property of the Page object.  It returns the Page object that transferred control to the current page.  You may also be familiar with the PreviousPageType directive which provides a way to get strong typing against the previous page.

   1: <%@ PreviousPageType VirtualPath="~/YourSourcePage.aspx" %>

In most cases you need nothing more on the target page...  unless the target page has multiple previous pages from which it will be expecting data.

Solution

Enter the Reference directive.  Only one PreviousPageType directive can exist per page but any number of Reference directives can be used.  Most people have used this directive to reference user controls with the following syntax:

   1: <%@ Reference Control="YourControl.ascx" %>

 

The reference directive also has Page and virtualPath attributes, either of which can be used to reference other pages as follows:

   1: <%@ Reference Page="~/YourSourcePage.aspx" %>

 

There is, however, a down side to using the Reference directive over the PreviousPageType directive.  The PreviousPage property will have to be cast to the actual previous page type before use.  For example, if your previous page's class name was SourcePage and it had a property called Message, it would have to be accessed as follows:

   1: string message = ((SourcePage)PreviousPage).Message;

 

This also means that you'll have to do a check before you can cast it to the correct page like so:

   1: string message = string.Empty;
   2: if (PreviousPage is FirstSourcePage)
   3: {
   4:     message = ((FirstSourcePage)PreviousPage).Message;
   5: }
   6: else if (PreviousPage is SecondSourcePage)
   7: {
   8:     message = ((SecondSourcePage)PreviousPage).Message;
   9: }
  10: else
  11: {
  12:     message = "The previous page is unknown.";
  13: }

 

Personally I think this is a small price to pay if you only have a few possible previous pages.  Otherwise, there are always other options - query string, form collection, session state, etc.

Working Example

For those interested in quickly trying this out, I've included all the code you need below.  One scenario has a single source and single target page.  The other has multiple sources pointing to the same target page.

Code behind for the single source page used with the single target page:

   1: using System;
   2: using System.Data;
   3: using System.Configuration;
   4: using System.Collections;
   5: using System.Web;
   6: using System.Web.Security;
   7: using System.Web.UI;
   8: using System.Web.UI.WebControls;
   9: using System.Web.UI.WebControls.WebParts;
  10: using System.Web.UI.HtmlControls;
  11:  
  12: public partial class SingleSourcePage : System.Web.UI.Page
  13: {
  14:     protected void Page_Load(object sender, EventArgs e) { }
  15:  
  16:     public string Message
  17:     {
  18:         get { return "This is a message from SingleSourcePage.aspx"; }
  19:     }
  20: }

 

Markup for the single source page used with the single target page:

   1: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="SingleSourcePage.aspx.cs" Inherits="SingleSourcePage" %>
   2: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   3: <html xmlns="http://www.w3.org/1999/xhtml" >
   4:     <head runat="server">
   5:         <title>Single Source Page</title>
   6:     </head>
   7:     <body>
   8:         <form id="form1" runat="server">
   9:             <h1>SingleSourcePage.aspx</h1>
  10:             <div>
  11:                 <ul>
  12:                     <li>
  13:                         <a href="SingleSourcePage.aspx">SingleSourcePage.aspx</a>
  14:                     </li>
  15:                     <li>
  16:                         <a href="TargetPageSingleSource.aspx">TargetPageSingleSource.aspx</a>
  17:                     </li>
  18:                     <li>
  19:                         <a href="FirstSourcePage.aspx">FirstSourcePage.aspx</a>
  20:                     </li>
  21:                     <li>
  22:                         <a href="SecondSourcePage.aspx">SecondSourcePage.aspx</a>
  23:                     </li>
  24:                     <li>
  25:                         <a href="TargetPageMultipleSources.aspx">TargetPageMultipleSources.aspx</a>
  26:                     </li>
  27:                 </ul>
  28:             </div>
  29:             <div>
  30:                 <asp:Button ID="btnPostToTargetPage" runat="server" Text="Post to TargetPageSingleSource.aspx" PostBackUrl="~/TargetPageSingleSource.aspx" />
  31:             </div>
  32:         </form>
  33:     </body>
  34: </html>

 

Code behind for the target page with one previous page:

   1: using System;
   2: using System.Data;
   3: using System.Configuration;
   4: using System.Collections;
   5: using System.Web;
   6: using System.Web.Security;
   7: using System.Web.UI;
   8: using System.Web.UI.WebControls;
   9: using System.Web.UI.WebControls.WebParts;
  10: using System.Web.UI.HtmlControls;
  11:  
  12: public partial class TargetPageSingleSource : System.Web.UI.Page
  13: {
  14:     protected void Page_Load(object sender, EventArgs e)
  15:     {
  16:         if (PreviousPage != null)
  17:         {
  18:             lblMessage.Text = PreviousPage.Message;
  19:         }
  20:         else
  21:         {
  22:             lblMessage.Text = "No previous page was detected.";
  23:         }
  24:     }
  25: }

 

Markup for the target page with one previous page:

   1: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="TargetPageSingleSource.aspx.cs" Inherits="TargetPageSingleSource" %>
   2: <%@ PreviousPageType VirtualPath="~/SingleSourcePage.aspx" %>
   3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
   4: <html xmlns="http://www.w3.org/1999/xhtml">
   5:     <head runat="server">
   6:         <title>Target Page from a Single Source</title>
   7:     </head>
   8:     <body>
   9:         <form id="frmMain" runat="server">
  10:             <h1>TargetPageSingleSource.aspx</h1>
  11:             <div>
  12:                 <ul>
  13:                     <li>
  14:                         <a href="SingleSourcePage.aspx">SingleSourcePage.aspx</a>
  15:                     </li>
  16:                     <li>
  17:                         <a href="TargetPageSingleSource.aspx">TargetPageSingleSource.aspx</a>
  18:                     </li>
  19:                     <li>
  20:                         <a href="FirstSourcePage.aspx">FirstSourcePage.aspx</a>
  21:                     </li>
  22:                     <li>
  23:                         <a href="SecondSourcePage.aspx">SecondSourcePage.aspx</a>
  24:                     </li>
  25:                     <li>
  26:                         <a href="TargetPageMultipleSources.aspx">TargetPageMultipleSources.aspx</a>
  27:                     </li>
  28:                 </ul>
  29:             </div>
  30:             <div>
  31:                 <asp:Label ID="lblMessage" runat="server" />
  32:             </div>
  33:         </form>
  34:     </body>
  35: </html>

 

Code behind for source page one of two for use with the target page accepting multiple previous pages:

   1: using System;
   2: using System.Data;
   3: using System.Configuration;
   4: using System.Collections;
   5: using System.Web;
   6: using System.Web.Security;
   7: using System.Web.UI;
   8: using System.Web.UI.WebControls;
   9: using System.Web.UI.WebControls.WebParts;
  10: using System.Web.UI.HtmlControls;
  11:  
  12: public partial class FirstSourcePage : System.Web.UI.Page
  13: {
  14:     protected void Page_Load(object sender, EventArgs e) { }
  15:  
  16:     public string Message
  17:     {
  18:         get { return "This is a message from FirstSourcePage.aspx"; }
  19:     }
  20: }

 

Markup for source page one of two for use with the target page accepting multiple previous pages:

   1: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="FirstSourcePage.aspx.cs" Inherits="FirstSourcePage" %>
   2: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
   3: <html xmlns="http://www.w3.org/1999/xhtml">
   4:     <head runat="server">
   5:         <title>First Source Page</title>
   6:     </head>
   7:     <body>
   8:         <form id="frmMain" runat="server">
   9:             <h1>FirstSourcePage.aspx</h1>
  10:             <div>
  11:                 <ul>
  12:                     <li>
  13:                         <a href="SingleSourcePage.aspx">SingleSourcePage.aspx</a>
  14:                     </li>
  15:                     <li>
  16:                         <a href="TargetPageSingleSource.aspx">TargetPageSingleSource.aspx</a>
  17:                     </li>
  18:                     <li>
  19:                         <a href="FirstSourcePage.aspx">FirstSourcePage.aspx</a>
  20:                     </li>
  21:                     <li>
  22:                         <a href="SecondSourcePage.aspx">SecondSourcePage.aspx</a>
  23:                     </li>
  24:                     <li>
  25:                         <a href="TargetPageMultipleSources.aspx">TargetPageMultipleSources.aspx</a>
  26:                     </li>
  27:                 </ul>
  28:             </div>
  29:             <div>
  30:                 <asp:Button ID="btnPostToTargetPage" runat="server" Text="Post to TargetPageMultipleSources.aspx" PostBackUrl="~/TargetPageMultipleSources.aspx" />
  31:             </div>
  32:         </form>
  33:     </body>
  34: </html>

 

Code behind for source page two of two for use with the target page accepting multiple previous pages:

   1: using System;
   2: using System.Data;
   3: using System.Configuration;
   4: using System.Collections;
   5: using System.Web;
   6: using System.Web.Security;
   7: using System.Web.UI;
   8: using System.Web.UI.WebControls;
   9: using System.Web.UI.WebControls.WebParts;
  10: using System.Web.UI.HtmlControls;
  11:  
  12: public partial class SecondSourcePage : System.Web.UI.Page
  13: {
  14:     protected void Page_Load(object sender, EventArgs e) { }
  15:  
  16:     public string Message
  17:     {
  18:         get { return "This is a message from SecondSourcePage.aspx"; }
  19:     }
  20: }

 

Markup for source page two of two for use with the target page accepting multiple previous pages:

   1: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="SecondSourcePage.aspx.cs" Inherits="SecondSourcePage" %>
   2: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
   3: <html xmlns="http://www.w3.org/1999/xhtml" >
   4:     <head runat="server">
   5:         <title>Second Source Page</title>
   6:     </head>
   7:     <body>
   8:         <form id="frmMain" runat="server">
   9:             <h1>SecondSourcePage.aspx</h1>
  10:             <div>
  11:                 <ul>
  12:                     <li>
  13:                         <a href="SingleSourcePage.aspx">SingleSourcePage.aspx</a>
  14:                     </li>
  15:                     <li>
  16:                         <a href="TargetPageSingleSource.aspx">TargetPageSingleSource.aspx</a>
  17:                     </li>
  18:                     <li>
  19:                         <a href="FirstSourcePage.aspx">FirstSourcePage.aspx</a>
  20:                     </li>
  21:                     <li>
  22:                         <a href="SecondSourcePage.aspx">SecondSourcePage.aspx</a>
  23:                     </li>
  24:                     <li>
  25:                         <a href="TargetPageMultipleSources.aspx">TargetPageMultipleSources.aspx</a>
  26:                     </li>
  27:                 </ul>
  28:             </div>
  29:             <div>
  30:                 <asp:Button ID="btnPostToTargetPage" runat="server" Text="Post to TargetPageMultipleSources.aspx" PostBackUrl="~/TargetPageMultipleSources.aspx" />
  31:             </div>
  32:         </form>
  33:     </body>
  34: </html>

 

Code behind for target page accepting multiple previous pages:

   1: using System;
   2: using System.Data;
   3: using System.Configuration;
   4: using System.Collections;
   5: using System.Web;
   6: using System.Web.Security;
   7: using System.Web.UI;
   8: using System.Web.UI.WebControls;
   9: using System.Web.UI.WebControls.WebParts;
  10: using System.Web.UI.HtmlControls;
  11:  
  12: public partial class TargetPageMultipleSources : System.Web.UI.Page
  13: {
  14:     protected void Page_Load(object sender, EventArgs e)
  15:     {
  16:         if (PreviousPage != null)
  17:         {
  18:             if (PreviousPage is FirstSourcePage)
  19:             {
  20:                 lblMessage.Text = ((FirstSourcePage)PreviousPage).Message;
  21:             }
  22:             else if (PreviousPage is SecondSourcePage)
  23:             {
  24:                 lblMessage.Text = ((SecondSourcePage)PreviousPage).Message;
  25:             }
  26:             else
  27:             {
  28:                 lblMessage.Text = "The previous page is unknown.";
  29:             }
  30:         }
  31:         else
  32:         {
  33:             lblMessage.Text = "No previous page was detected.";
  34:         }
  35:     }
  36: }

 

Markup for target page accepting multiple previous pages:

   1: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="TargetPageMultipleSources.aspx.cs" Inherits="TargetPageMultipleSources" %>
   2: <%@ Reference Page="~/FirstSourcePage.aspx" %>
   3: <%@ Reference Page="~/SecondSourcePage.aspx" %>
   4: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
   5: <html xmlns="http://www.w3.org/1999/xhtml" >
   6:     <head runat="server">
   7:         <title>Target Page from Multiple Sources</title>
   8:     </head>
   9:     <body>
  10:         <form id="frmMain" runat="server">
  11:             <h1>TargetPageMultipleSources.aspx</h1>
  12:             <div>
  13:                 <ul>
  14:                     <li>
  15:                         <a href="SingleSourcePage.aspx">SingleSourcePage.aspx</a>
  16:                     </li>
  17:                     <li>
  18:                         <a href="TargetPageSingleSource.aspx">TargetPageSingleSource.aspx</a>
  19:                     </li>
  20:                     <li>
  21:                         <a href="FirstSourcePage.aspx">FirstSourcePage.aspx</a>
  22:                     </li>
  23:                     <li>
  24:                         <a href="SecondSourcePage.aspx">SecondSourcePage.aspx</a>
  25:                     </li>
  26:                     <li>
  27:                         <a href="TargetPageMultipleSources.aspx">TargetPageMultipleSources.aspx</a>
  28:                     </li>
  29:                 </ul>
  30:             </div>
  31:             <div>
  32:                 <asp:Label ID="lblMessage" runat="server" />
  33:             </div>
  34:         </form>
  35:     </body>
  36: </html>

 

Conclusion

I've used this method several times with different websites I've worked on and it seems to do the trick for me.  The comments are open if anyone else has another way they prefer to do this.

9 comments:

Rey said...
This comment has been removed by the author.
amonik said...

Your Article is the most powerful and the most easiest to implement which i ever seen for many other issues. THANKS A LOT. You need to report Microsoft to remove the Previous page directive and use as many Reference as want.

Mahesh said...

Your article is very good

Anonymous said...

i liked ur article but its not working. when i debug the application page is property is not triggered. ANY HELP PLZ??????

Anonymous said...

and even i dont think that it should work coz is against oops concept. u r trying to implement multiple inheritance without using an interface.

Christian Guzmán said...

I did this but applying Interfaces to each caller page, having the properties i required.

Didnt even try without interfaces, but I can assure you it works if you use one.

Anonymous said...

Thanks for the info, you really saved me some time and a headache!

Victor Dwi said...

Wow.. after googling for hours, your article is the best explained solution of all. Thanks a lot!! Keep writing :)

Tarık Guner said...

Perfectly worked on my custom test.
I personally use switch statement like:

if(!IsPostBack)
{
if(PreviousPage !=null)
{
switch(PreviousPage.Page.Form.ID)
{
case "form1":
{
Label1.Text = ((MultipleCrossPage.Client1)(PreviousPage)).message;
break;
}

case "form2":
{
Label1.Text = ((MultipleCrossPage.Client2)(PreviousPage)).message;
break;
}

default:
{
Label1.Text = "Unknown";
break;
}

}
}
}

Beware that, this operation is not a postback on host page.