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

Escrito por un humano, no por una IA