PopUp o Ventanas Modales en ASP.NET

ENERO 2016
PROBALO EN MVC CAMPEON!

Muchas veces porque queda muy agradable a la vista, es conveniente abrir una ventana (popup) con información adicional sobre un ítem deseado. Otro caso (particularmente el que me tocó desarrollar) puede ser hacer una especie de Wizard donde las ventanas cumplan una suerte de varios pasos donde se pueda ir para atrás,adelante y/o finalizar, al mejor estilo instalación de cualquier soft (siguiente,siguiente,siguiente,instalar…finalizar).

Lo que voy a mostrar parte de una página Principal (Página Madre) que es la que va a abrir el popup y acá se pueden usar 2 maneras, una ventana modal o una ventana modeless.

ventana modal: Este tipo de ventanas no permite la interacción con la ventana anterior mientras este abierta, de hecho interrumpe cualquier ejecución que se este haciendo por atrás. Por ejemplo si nosotros hacemos un postback y en el servidor abrimos una ventana con el método de JS window.showModalDialog puede ocurrir que la página madre quede en blanco o a medio cargar.

ventana modeless: Estas ventanas si permiten la interacción con su ventana madre, de hecho es posible hacer click en la ventana de atrás y lo va a permitir. Por lo comentado arriba este tipo modal puede ser útil. Pero si por ejemplo vamos a abrir una modal de una fila en un gridview que tiene un enlace a eliminarlo, si lo hace mientras esta la modeles abierta se lo va a permitir.

Este ejemplo esta diseñado para IE 6.0 en adelante y voy a usar VS2005. Básicamente voy a hacer esto:

Voy a usar una sola página como Wizard pero pueden ser las que se necesiten, al fin y al cabo todas terminan en un página de Fin.

La página principal va a contar con script porque va a mantener una variable para poder tomarla al final:

<script>
var Guardar;
function DeseaGuardar()
{
window.document.Form1.txtOculto.value=Guardar;
__doPostBack('lnkGuardar','');
}
function Ejecutar()
{
//Agregamos el JS para pasar el parametro</span>
var param='';
param='valor=' + window.document.Form1.TextBox1.value;
WizardGeneral(200,550,'Default2.aspx',param);
}
</script>

La función WizardGeneral está en un JS que adjunto con todo el proyecto al final y es la que arma la URL para abrir el Wizard de una manera u otra (Modal/Modeless) . Yo lo hago con una función en el evento onclick llamando a Ejecutar pero por ejemplo si queremos hacer popups de una grilla agregándole a todas las rows solo vamos a necesitar el ID de cada row para levantar los datos en el popup con lo cual se puede hacer todo en la 1era carga de la página agregando el id en el evento RowDatabound. Yo a modo de ejemplo solo modifico un textbox.

El HTML de la página sería algo así:

<HTML>
<HEAD>
<title>Prueba</title>
<meta content="Microsoft Visual Studio .NET 7.1" name="GENERATOR">
<meta content="Visual Basic .NET 7.1" name="CODE_LANGUAGE">
<meta content="JavaScript" name="vs_defaultClientScript">
<script language="javascript" src="JS/ScriptJS.js"></script>
</HEAD>
<body style="text-align: left">

<form id="Form1" method="post" runat="server">



<table width="100%">


<tr>


<td style="text-align: center;" colspan="3">
<asp:Label ID="lblerror" runat="server" Width="100%"></asp:Label></td>


</tr>




<tr>


<td style="width: 24px">
<asp:Label ID="Label1" runat="server" Text="Valor"></asp:Label></td>




<td style="width: 60px">
<asp:TextBox ID="TextBox1" runat="server" Width="279px"></asp:TextBox>
</td>




<td style="width: 100px">
</td>


</tr>




<tr>


<td style="width: 24px">
</td>




<td style="width: 60px; text-align: center;">
<input id="btnBoton" type="button" value="Popup" runat =server  /></td>




<td style="width: 100px">
</td>


</tr>


</table>



<asp:LinkButton ID="lnkGuardar" runat="server"></asp:LinkButton>amp;nbsp;
<input id="txtOculto" style="width: 54px" runat =server  type="hidden" />
</form>

</body>
</HTML>

Al final de la página hay 2 elementos importantes un Link, que es el que va a causar el postback de la página madre cuando termine el wizard y un hidden que es el que va a tener el valor de retorno del wizard. El código de servidor es mas o menos este:

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Introducir aquí el código de usuario para inicializar la página
If Not Page.IsPostBack Then
Try
txtOculto.Value = ""
btnBoton.Attributes.Add("onclick", "javascript:Ejecutar()")
Catch ex As Exception
lblerror.Text = ex.Message
End Try
End If
End Sub

Protected Sub lnkGuardar_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles lnkGuardar.Click
'txtOculto tiene el valor modificado que me devuelve el Wizard
If Not String.IsNullOrEmpty(txtOculto.Value) Then TextBox1.Text = txtOculto.Value
End Sub

Cuando la página de fin se cierre “cargar” de nuevo esta página ósea va a pasar por el page_load por eso hay que manejar bien el page.ispostback para no pisar valores y luego va a entrar por el click del Link que nosotros le formamos a hacer con la instrucción de Javascript __doPostBack. La Página del Wizard tiene este HTML:

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Página del Wizard</title>
<base target="_self">
</head>
<body >
<script language="javascript">
          var sData = dialogArguments;

          sData.Guardar= '';
         sData.DeseaGuardar();
       function sourceCode()  //Para ver el codigo fuente
         {
          d=window.open();
          d.document.open('text/plain').write(document.documentElement.outerHTML);
          }

</script>
<form id="form1" runat="server" method="post">
<table>
<tr>
<td style="width: 100px">
<asp:Label ID="Label1″ runat="server" Text="Valor del Wizard" Width="118px"></asp:Label></td>
<td style="width: 100px">
<asp:TextBox ID="TextBox1″ runat="server"></asp:TextBox></td>
</tr>
<tr>
<td style="width: 100px"></td>
<td style="width: 100px">
<asp:Button ID="Button1″ runat="server" Text="Ejecutar" /></td>
</tr>
<tr>
<td colspan="2″>
<asp:Label ID="Label2″ runat="server" ForeColor="#0000C0″ Text="Al cambiar esta valor se modifica el textbox de la página principal"></asp:Label></td>
</tr>
</table>
</form>

</body>
</html>

Tiene algunas particularidades la primera es que tiene le atributo target= self esto es para cuando hacemos el response.redirect a la página de fin no nos abra una página sino que siga en la misma. Después la función Javascript toma el dialogArguments que son las variables o funciones de la página madre. Y por ultimo algo que nos puede ser muy útil para depurar es una función para ver el HTML de la página cuando se esta ejecutando, porque las modales no dejan hacer click derecho para ver el código. Simplemente se agrega el nombre (sourceCode) en el evento onload del Form y listo apenas carga la página tenemos una nueva ventanita con el HTML :D.

Este es el código de servidor:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Try
If Not Page.IsPostBack Then
TextBox1.Text = Request.Params("valor")
Else
Dim strpage As String = "General_Fin_W.aspx"
Dim paramFinal As String = "modo=modificacionamp;datos=".ToString + TextBox1.Text
strpage += "?".ToString + paramFinal.ToString 'General_Fin_W.aspx?modo=modificacionamp;datos=
Response.Redirect(strpage)
End If
Catch ex As Exception
Response.Write(ex.Message)
End Try
End Sub

y por ultimo la página de Fin HTML y código de Servidor:

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title></title>
<script language="JavaScript" type="text/javascript">

       function CerrarWizard()
       {
        var sData = dialogArguments;
           //var datos= document.Form1.HiddenField1.value; // Esto vale lo que
          //termina concatenando en el wizard
          var datos= document.Form1.HiddenField1.value;
          sData.Guardar=datos;
          sData.DeseaGuardar();
          window.close();
          }

         function sourceCode()  //Para ver el codigo fuente
           {
           d=window.open();
           d.document.open('text/plain').write(document.documentElement.outerHTML);
           }

</script> </head> <body class="BodyNormal" onactivate ="CerrarWizard();">
<form id="Form1" runat="server" >
<div style="text-align: center" > <asp:Label ID="lblMensajes" runat="server" CssClass="labelmensaje" Width="98%">Cargando ...</asp:Label>
 <asp:HiddenField ID="HiddenField1" runat="server" /></div>
</form>

 </body> </html> 

Tiene una simple pantalla blanca con un mensaje cargando…. (pero la idea es que prácticamente no aparezca) porque como se ve en la funcion hace en onload le asigna el valor a sData.Guardar (osea el valor oculto de la página madre) y despues hace un window.close.


Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Select Case Request.Params("modo")
Case "modificacion"
If Not String.IsNullOrEmpty(Request.Params("datos")) Then
HiddenField1.Value = Request.Params("datos")
End If
End Select
End Sub

La idea es hacer una sola página de fin en caso de que necesitamos varios Wizard por ejemplo para cargar Personas o Productos, etc. Todas devuelven 1 dato por ejemplo si son Wizard de Alta podrían devolver el ID a la página Madre para poder levantarlo desde ahí.

Lo subí a medifire aqui.

ACTUALIZADO 18/1/2016:
GoogleDrive

Saludos.