Hay ocasiones en las que tenemos una página muy poblada de controles que se deben mostrar en pasos sucesivos. Desde una simple recogida de datos que se haya complicado un poco, hasta varios pasos de un asistente, por ejemplo.

Existen muchas maneras de solucionar esta situación y hacer que las páginas estén un poco más despejadas. Por ejemplo, podemos usar un control Wizard nativo de ASP.NET Web Forms. Sin embargo una forma mucho más natural de conseguirlo es recibir los resultados de la primera página en una nueva que los procesa y continúa el flujo de la aplicación. De hecho, en los paradigmas de programación convencionales (o sea, en todo lo que no sea Web Forms, incluyendo ASP.NET MVC pero también PHP, etc...) realmente es como se ha hecho siempre: unas páginas pasan datos a otras para procesarlos.

Transferencia en el lado del servidor

En ASP.NET disponemos de la instrucción Server.Transfer que nos permite derivar la ejecución actual de una página hacia otra, como si se ejecutasen en serie pero sin pasar por el lado cliente como ocurre con Response.Redirect. Además, durante la ejecución de la segunda página tenemos acceso al contexto de la llamada inicial y también a los controles de la página original y sus valores. Esta instrucción está disponible desde siempre en todas las versiones de ASP.NET ya que en realidad venía heredada de ASP 3.0 clásico, donde ya se utilizaba.

El principal inconveniente de la transferencia en el lado servidor es que los usuarios no son conscientes jamás de que este proceso ha tenido lugar, es decir, no ven reflejado en la barra de direcciones del navegador que en realidad se ha procesado otra página.

Si nos fijamos en la página de ejemplo que podrás descargar al final de este post, la página original es "Default.aspx":

PagEjemplo

Al ejecutar una transferencia en el servidor pulsando el correspondiente botón, aunque la página que se ha ejecutado es la que en el ejemplo he llamado "Transfer.aspx" en realidad en el navegador esto no se ve reflejado:

EjemploTransfer

Esto, además de despistar a los desarrolladores más noveles, puede tener inconvenientes dependiendo de cómo esté hecha la página cuando se pulsa el botón de volver en el navegador, por ejemplo.

Otro inconveniente menos frecuente de este método -pero que yo he tenido ya que sufrir en alguna ocasión- es la generación de excepciones de tipo ThreadAbortException debido a que se interrumpe la ejecución de un hilo. Ello es debido a que, internamente, Transfer llama siempre a Response.End (como se observa en la captura siguiente del código fuente), interrumpiendo la ejecución del hilo actual, lo cual puede dar lugar a este tipo de problemas:

ServerTransfer_Codigo
Pulsa para aumentar

Es un comportamiento documentado en la Knowledge Base de Microsoft, y se recomienda sustituir la llamada a transfer por otra a Server.Execute.

El método Transfer tiene un parámetro adicional aparte de la página que se quiere ejecutar que sirve para indicar si queremos que la página a la que transferimos tenga acceso a las colecciones Form y QueryString del objeto Response de la página original:

   1: Server.Transfer("~/MiOtraPagina.aspx", true);

Con esto podremos acceder desde la página transferida a los datos enviados a la página original desde un formulario o a través de la URL.

Cross Page Posting

Con ASP.NET 2.0 se introdujo una nueva forma de hacer la transferencia entre páginas llamada Cross Page Posting. Ésta se parece a la transferencia o la ejecución de páginas pero en realidad lo que hace es enviar la página directamente desde el cliente con un PostBack normal, sólo que en lugar de dirigir éste a la página original lo hace a una nueva página. Es, básicamente, provocar el envío del formulario a una nueva página en lugar de hacerlo a la actual que es lo habitual.

Lo que conseguimos es que se ejecute la página de la manera normal pero enviando los datos a un nuevo Web Form.

Se parece mucho a la transferencia de la página pero tiene dos diferencias fundamentales:

  1. El cambio de flujo se realiza desde el lado cliente, no desde el servidor.
  2. El usuario ve en su navegador la URL de la nueva página: no se queda en la misma página.

Si vemos el resultado de ejecutar el ejemplo adjunto con el botón de hacer "Cross page posting", observamos que la página resultante es la esperada y no la original:

EjemploCrossPage

Implementar Cross page Posting es muy sencillo: basta con asignar en el control que queremos que lo provoque, la propiedad PostbackUrl a la página a la que queremos enviar los datos:

PostBackUrlProp

Al hacerlo con un botón por ejemplo, se modifica el código de lado cliente para el evento "click" del botón HTMl resultante, de modo que envía los datos a la nueva página:

CodigoHTMLCrossPost
Pulsa para aumentar

Una cuestión adicional: aunque los datos se envían finalmente a otra página diferente a la original, el evento de servidor del botón se ejecuta igualmente. Los cambios que se hagan desde dicho evento a los controles de la página original no se verán reflejados al acceder a éstos en la página final, pero sí se pueden ejecutar otro tipo de tareas, como escribir a una base de datos, por ejemplo. Basta con poner un punto de interrupción en Visual Studio para ver que es así.

¿Cómo accedemos a los datos de la página original?

Tenemos diversas formas de acceder a los datos de los controles de la página original, tanto al hacer un Transfer como un Cross Page Posting.

La manera más fácil es obtener una referencia a la página original usando la propiedad PreviousPage de la página actual. Una vez obtenida la referencia es fácil encontrar cualquier control usando el método FindControl:

   1: if (Page.PreviousPage != null)
   2: {
   3:     TextBox txtPrev = (TextBox)Page.PreviousPage.FindControl("TextBox1");
   4:     if (txtPrev != null)
   5:         lblMsg2.Text = txtPrev.Text;
   6: }
   7: else
   8: {
   9:     lblMsg2.Text = "No es Cross page ni transferencia";
  10: }

Como vemos se verifica si hay una página anterior o no, en cuyo caso se busca el control TextBox original y se muestra su contenido en la página actual (todo esto dentro del evento Load de la página).

Sin embargo acceder a los controles usando FindControl es un método muy frágil y propenso a errores. Basta con que el control se cambie de ubicación en la página para introducirlo dentro de un contenedor, por ejemplo, para que ya no se encuentre. Eso por no mencionar la posibilidad de que le cambiemos el ID sin darnos cuenta de esta dependencia y que de repente todo deje de funcionar.

Por ello un método mucho más recomendable es definir una o más propiedades de sólo lectura en la página original para tener acceso a los controles subyacentes. en nuestro ejemplo podemos definir una página NombreAsaludar en la página Default.aspx, de este modo:

   1: //Definimos una propiedad para facilitar la lectura de los controles
   2: public string NombreASaludar {
   3:     get
   4:     {
   5:         return TextBox1.Text;
   6:     }
   7: }

Ahora podemos acceder en la página secundaria a la que hemos transferido o hecho CrossPosting al valor del control usando un código un poco más seguro:

   1: if (Page.PreviousPage != null)
   2: {
   3:     Default pagPrev = (Default)Page.PreviousPage;
   4:     lblMsg2.Text = pagPrev.NombreASaludar;
   5: }
   6: else
   7: {
   8:     lblMsg2.Text = "No es Cross page";
   9: }

Como vemos ahora en lugar de usar FindControl convertimos la PreviousPage al tipo correcto de la página original y ya podemos llamar a su propiedad NombreASaludar directamente.

Sin embargo aún existe una forma mejor de hacerlo y es incluir en la página secundaria una directiva @PreviousPageType, así:

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

Con esto le estamos indicando a la infraestructura de ASP.NET el tipo de la página original, por lo que la propiedad PreviousPage tendrá ya automáticamente el tipo correcto y nos ahorraremos incluso la conversión:

   1: if (Page.PreviousPage != null)
   2: {
   3:     lblMsg2.Text = this.PreviousPage.NombreASaludar;
   4: }
   5: else
   6: {
   7:     lblMsg2.Text = "No es Cross page";
   8: }

¡Ahora sí que es directo!

¿Cómo se distingue entre una transferencia y Cross Page Postback?

Todo lo anterior funciona tanto en transferencias como en envíos de datos cruzados entre páginas. Sin embargo en ocasiones será necesario que podamos distinguir entre unas y otras. ¿Cómo podemos hacerlo?

Las páginas tienen una propiedad llamada IsCrossPagePostBack que nos permite saber si se ha producido o no este tipo de acción. Esta propiedad será verdadera para la página anterior en el caso de un Cross page postback pero no en el caso de una transferencia, así que basta con poner esto para distinguirlas:

   1: if (Page.PreviousPage != null && Page.PreviousPage.IsCrossPagePostBack)

Fíjate que no hay peligro de que rompa la comparación cuando no es ni una cosa ni la otra ya que el operador lógico "AND" (&&) está cortocircuitado, es decir, si la primera condición no se cumple ya no se comprueba la segunda, así que aunque PreviousPage sea nulo la segunda comparación no provoca una excepción.

Dejo un ejemplo con todas las posibilidades implementadas para que lo puedas estudiar y ver las pequeñas diferencias entre unos casos y otros: CrossPagePostingYTransfer.zip (14,3 KB)

¡Espero que te sea útil!

¿Te ha gustado este post? - Aprende .NET con los cursos on-line tutelados de campusMVP:
   ·
Preparación del examen 70-515: Desarrollo Web con .NET 4.0 (Tutelado por mi)
   · Desarrollo Web con ASP.NET 4.0 Web Forms (Tutelado por mi)
   · ASP.NET 4.0 Web Forms desde cero (Tutelado por mi)
   · Desarrollo Web con ASP.NET MVC 3 
   · Silverlight 4.0 - Aplicaciones Ricas para Internet (RIA)
   · jQuery paso a paso para programadores ASP.NET
   · Visual Studio 2010 desde cero

💪🏻 ¿Este post te ha ayudado?, ¿has aprendido algo nuevo?
Pues NO te pido que me invites a un café... Te pido algo más fácil y mucho mejor