Programming Microsoft ASP.NET 4 - Dino Esposito [199]
dynamic previousPage = PreviousPage;
var txt = previousPage.Keyword; // Keyword is inaccessible due to its protection level
if (txt == null)
{
...
}
To fix this code, you need to define a public property on the posting page class that exposes as a public member whatever element you want to retrieve from within the posted page. It doesn’t have to be the control reference; it is recommended that you expose just data. Here’s an example:
public partial class Crosspage : System.Web.UI.Page
{
public String SelectedKeywords
{
get { return Keyword.Text; }
}
}
With this change, the following call will work:
dynamic previousPage = PreviousPage;
var keywords = previousPage.SelectedKeywords;
The dynamic type, though, involves falling down to the Dynamic Language Runtime (DLR) engine and should be used only when you really need dynamically resolved code. In this case, you can get an even more effective (and strongly typed) solution by resorting to a page directive.
The @PreviousPageType Directive
Let’s say it up front. To retrieve values on the posting page, FindControl is your only safe option if you don’t know in advance which page will be invoking your target. However, when you’re using cross-page posting in the context of an application, chances are good that you know exactly who will be calling the page and how. In this case, you can take advantage of the @PreviousPageType directive to cause the target page’s PreviousPage property to be typed to the source page class.
In the target page, you add the following directive:
<%@ PreviousPageType VirtualPath="crosspage.aspx" %>
The 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. The directive just shown makes the PreviousPage property on the target page class be of the same type as the page at the given path (or the specified type). This fact alone, though, is not sufficient to let you access input controls directly. Each page class contains protected members that represent child controls; unfortunately, you can’t call a protected member of a class from an external class. (Only derived classes can access protected members of the parent class.)
To work around the issue, in the caller page you must add public properties that expose any information you want posted pages to access. For example, imagine that crosspostpage.aspx contains a TextBox named Keyword. To make it accessible from within a target page, you add the following code to the code-behind class:
public TextBox KeywordControl
{
get { return Keyword; }
}
The new KeywordControl property on the page class wraps and exposes the internal text-box control. In light of this code, the target page can now execute the following code:
Response.Write(PreviousPage.KeywordControl.Text);
Although you can directly expose a control reference, it is preferable that you expose just the data the posted page needs to consume. This approach is based on the Law of Demeter, which essentially states that internal details of components should not be made public unless strictly required. Another way of looking at this is in light of the “Tell, don’t ask principle”: your posted page gets what it needs instead of asking for a property on a control.
Detecting Cross-Page Postings
Being the potential target of a cross-page call doesn’t automatically make a target page a different kind of page all of a sudden. There’s always the possibility that the target page is invoked on its own—for example, via hyperlinking. When this happens, the PreviousPage property returns null and other postback-related properties, such as IsPostBack, assume the usual values.
If you have such a dual page, you should insert some extra code to discern the page behavior. The following example shows a page that allows only cross-page access:
if (PreviousPage == null)
{
Response.Write("Sorry, that's