1. Original Entry + Comments2. Write a Comment3. Preview Comment
New comments for this entry are disabled.


June 09, 2004  |  Cross-page posting in Whidbey, Part 2  |  19713 hit(s)

I have a couple more details on ASP.NET Whidbey cross-page posting to add to the original entry. What I've covered is how to specify a cross-page post (set the PostBackUrl property of a button) and how on the target page to get the value(s) of control(s) from the source page.

For the latter, you use FindControl. A point I didn't make last time was that FindControl is scoped to a naming container. You can do this:
PreviousPage.FindControl(controlId)
to find a control on the page, but if the control you're looking for is in a template someplace, you'll need to first use FindControl to get a reference to the template's owner and then a second FindControl on the result to get the actual control. Could be tedious.

Another way to get access to the source page, as I had alluded to and Colt nailed, is to configure strong access to the source page. To review, in the target page, the PreviousPage property gets a reference to the source page, but it's typed simply to Page. However, in the target page you can include a PreviousPageType directive like this:
<%@ PreviousPageType VirtualPath="~/SourcePage.aspx" %>
(Alternatively, instead of VirtualPath you can specify a Type attribute and specify the actual type you want.) Including this directive causes the target page to automatically cast the PreviousPage property to the type of the page you specify.

Great, but what does it get you? Well, if the source page has public properties, you can get strongly typed access to them, like this (in the target):
Label1.Text = PreviousPage.City
where City is a public property on the source page. Obviously, this is going to be most useful when you have built the source page with the idea in mind of doing precisely what's illustrated here. A good use, perhaps, would be to use public properties on the source page to expose the control properties you are interested in. That gives you typed access to those properties without having to invoke FindControl. Note that strongly typing the source page doesn't give you any better access to the source page controls than untyped access; controls are protected, so you just can't get to them without the FindControl rigamarole or wrapping the interesting stuff in public properties.

Public properties on the source page might as well be read-only. It's not a technical requirement, and you can even set the properties from the target page, but it's not good for anything -- any values you set aren't persisted.

I should probably note that any access to the source page whatsover is predicated on the source and target pages being in the same application. You can cross-post to any arbitrary URL, and it will work. But if the source page is in some other application, PreviousPage won't get you any reference, because there's no way to get an instance of that page.

Cross-post or postback or Server.Transfer?

You might design your pages so that SourcePage.aspx cross-posts to TargetPage.aspx, but it's always possible that TargetPage.aspx is requested on its own. You should therefore always test PreviousPage to be sure it contains the source page reference you think it does:
If Not PreviousPage Is Nothing Then
' Do your PreviousPage thing ...
Else
' Not a cross-post target (this time, anyway ...)
End If
In the target of a cross-posted page, normal postback semantics apply. The first time the page runs (as a result of the cross-post), IsPostBack is false. If the target page has posted to itself, IsPostBack is true, as expected, and PreviousPage is uninitialized. Coz by then it's a normal page in normal mode, so to speak.

Something not immediately obvious is that everything that applies to cross-posting also applies to Server.Transfer. This includes a page reference in PreviousPage and strong typing via the PreviousPageType directive. If you've used Server.Transfer in the past and used Context.Handler to get a reference to the source page, well, you won't have to any more in Whidbey -- just use PreviousPage as described here.

However, that does raise a slight problem, namely, when it's useful to do so, how do you know whether the current page was invoked via cross-page posting versus Server.Transfer? Anticipating this question, they added a property IsCrossPagePostBack to the Page class. In the target page, you test that property for the PreviousPage reference. Basically, if PreviousPage.IsCrossPagePostBack is true, you got to the target via a cross-post; if it's false, you got there via Server.Transfer.


And I think that's about it for cross-page posting. I don't know of anything else there might be to say, but if you think of anything, let me know, coz I'll need to put in in the docs. :-)


IsCrossPagePostBack    (Dino Esposito's WebLog)




Colt   11 Jun 04 - 12:36 AM

Hi Mike,

Basically, Dave have a similar description and code snippet as yours in his "First Look" book - http://www.amazon.com/exec/obidos/tg/detail/-/0321228960/qid=1066759398/sr=8-1/ref=sr_8_1/102-6179109-5504166?v=glance&s=books&n=507846 in the chapter of Cross Page posting.

I remembered ScottGu showed a demo (in PDC?) in this subject, where he showed a TextBox and a Button for searching some text, and this text box maybe placed inside a user control/master page, such that the TextBox and its associated button can post to the Search.aspx for real searching without affecting the original logic of the container page itself.

BTW, I think this kind of cross page posting is quite useful in some shopping cart like web sites, at least, the webmaster / visitors can click a button for an item and post to an external site for transaction/verification processing. :)


 
Hi Mike   18 Dec 04 - 11:16 AM

I have the following two pages. I am wondering how can I return back.
When I click on the back link to go to page one I want the values to be
saved or in the textboxes. What is the appropriate way in visual studio
2005?

CrossPage1.aspx
===============
<%@ Page Language="C#" %>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div align="center">
<table>
<tr>
<td>Enter some Text:</td>
<td><asp:TextBox ID="TextBox1"
Runat="server"></asp:TextBox></td>
</tr>
<tr>
<td>Enter more Text:</td>
<td><asp:TextBox ID="TextBox2"
Runat="server"></asp:TextBox></td>
</tr>
<tr>
<td align="center" colspan="2">
<asp:LinkButton ID="LinkButton1" Runat="server"
PostBackUrl="CrossPage2.aspx">Submit Page</asp:LinkButton>
</td>
</tr>
</table>
</div>
</form>
</body>
</html>



 
Hi Mike, second page   18 Dec 04 - 11:18 AM


CrossPage2.aspx
===============
<%@ Page Language="C#" %>
<%@ PreviousPageType VirtualPath="CrossPage1.aspx" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<script runat="server">
void Page_Load(object sender, System.EventArgs e)
{
// If posted from CrossPage1.aspx to CrossPage2.aspx...

if (PreviousPage == null)
{
Response.Write("Invoke me only through cross-page posting.");
Response.End();
return;
}
else
{
// Retrieve the textbox values from FindControl
TextBox TextBox1;
TextBox TextBox2;
TextBox1 = (TextBox)PreviousPage.FindControl("TextBox1");
TextBox2 = (TextBox)PreviousPage.FindControl("TextBox2");
Response.Write(TextBox1.Text+"<br>"+TextBox2.Text+"<br>");
}


}
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Page2</title>
</head>
<body>
<form id="form1" runat="server">
<div align="center">
<asp:Label ID="Label1" Runat="server"
EnableViewState="False"></asp:Label>&nbsp;<br />
<br />
<asp:HyperLink ID="HyperLink1" Runat="server"
NavigateUrl="crosspage1.aspx">Back</asp:HyperLink>
</div>
</form>
</body>


 
jgreer   09 Feb 05 - 3:38 PM

Hi Hi Mike,
i was wondering the exact same thing.
of course in 1.x i was wondering how to detect cross page transfers from any page and all the cool stuff they added to 2.0 that wasn't possible i soon found out.

my situation is i've managed to create very cleanly constructed /loaded pages with properties,
like a pageable list of items thats massive, then it goes into a details view page, then many post backs later of editing i want to return to my fully customized list of items. exposing all the properties that load there state into properties. as such

public int PageNumber
{
get { return (int)( Session[Page.ID + "PageNumber"] == null ? _pageNumber : Session[Page.ID + "PageNumber"]); }
set { Session[Page.ID + "PageNumber"] = value; }
}

then using it as such during databinding sutations

EventCollection ec = DTOProvider.Instance().GetPagedEvents(PageNumber, PageSize, SearchString, Month, Day, Year, OrderByField, IsOrderAscending, true);

gvEvents.DataSource = ec;
gvEvents.DataBind();

i've been able to obviously return the page back to its state pretty cleanly but over sessions and i could obviously extend this to store in the viewstate or profile or what not but i was wondering if anyone has looked at a slick way of generically serializing or persisting the important parts of any PreviousPage and returning to that particular page from data reconstructed from viewstate?

maybe even down to not even worring about name or location of the page when going previous, just calling a param'less method.
kind of on that same note handling browser back button reposts and transfering back to the previous pages post


 
Kevin Dente   12 May 05 - 7:00 AM

Mike,
I blogged some related info about cross-page postbacks and PreviousPage here:


http://weblogs.asp.net/kdente/archive/2005/04/09/397846.aspx



 
Shane Shepherd   14 Sep 06 - 9:44 AM

If I have multiple buttons on the source page, is it possible to determine which button was clicked on the target page?

For example, if I had a Gridview with buttons. The buttons postpackurl is set to target.aspx. On target.aspx, can I get the DataKey for the row clicked?


 
mike   14 Sep 06 - 6:09 PM

You should be able to use FindControl to get a reference to the GridView control on the source page and get any of its property values. Essentially, the control retains its state during cross-posting sufficiently that if you get a reference to it, you can work with it the same as if it were on the current page.

 
Shane Shepherd   15 Sep 06 - 6:17 AM

Mike,

Thanks for your reply! That's what I thought...however, I'm getting this error:
CS0117: 'System.EventArgs' does not contain a definition for 'Row'

For this code in the target page:
protected void Page_Load(object sender, EventArgs e)    
{
if (PreviousPage != null)
{
string _employeeId = ((GridView)PreviousPage.FindControl("gvLookup")).DataKeys[e.Row.RowIndex].Value.ToString();
}
}

I know why I'm getting the error, I just don't know what to do about it.

Regards,

- Shane


 
mike   15 Sep 06 - 1:57 PM

Hmmm. This is working for me:
    Sub Page_Load()
Dim sourcePage As Page = CType(Page.PreviousPage, Page)
If sourcePage IsNot Nothing Then
Dim sourceGridView As GridView
sourceGridView = sourcePage.FindControl("GridView1")
If sourceGridView IsNot Nothing Then
Response.Write("<br/>Dk = " & sourceGridView.SelectedDataKey.Value.ToString())
End If
Else
Response.Write("No source page.")
End If
End Sub

Couple of thots:
  • Just to confirm, you're doing the cross-post by setting the PostbackUrl property of a button.
  • The source and target pages are in the same application.
I would unpack your big ol' statement and see at what point it's failing -- is it in FindControl? In getting the key? Etc.