lunes, 30 de abril de 2012

[ASP.NET] PopUp Filtro – usando window.open()

 

Introducción

El objetivo del artículo apunta a demostrar como implementar la búsqueda de un determinado ítem haciendo uso de ventanas que se abran en popup.

Para la implementación se utilizara la funcionalidad de window.open() provista por javascript.

El ejemplo consiste en un listado de producto, pudiendo ser filtrados mediante una ventana popup que lista los proveedores.

El código fue implementado de dos formas distintas, si bien son muy parecidas tienen sutiles cambios, estas dos formas las denomine:

  • Selección con evento
  • Selección sin evento

Imagino se preguntaran del porque de esta diferencia del uso de un evento o no, básicamente difieren en el uso de controles asp.net (con evento) o tag de html (sin evento), cada alternativa cambia la forma en que se implementa la solución, en concreto la forma en que se toma el ítem elegido.

Empezaremos analizando la parte de la implementación que es común en ambos casos para luego ir a los detalles que afectan el usar un control de asp.net o uno de html

Abrir ventana de búsqueda

La definición del botón de búsqueda se logra adjuntando el evento click de jquery al tag <input>, como se refleja en la imagen

Esta acción lanzara la ventana de búsqueda que se ha implementado mediante el uso de window.open()

 

Filtro de productos

Una vez seleccionado el proveedor en la ventana de búsqueda esta acciona, o ejecuta, el botón de filtro, el cual se ha implementado, en en la pagina original, es decir la ventana hija invoca un boton en la ventana padre.

 

protected void btnFiltrar_Click(object sender, EventArgs e)
{
    LoadGridProducts();

    //
    // muestro la descripcion del proveedor
    //
    Suppliers supplier = NorthwindData.GetSupplierById(txtId.Text);

    string CompanyName = "";
    if (supplier != null)
    {
        CompanyName = supplier.CompanyName.Encode();
    }

    //
    // asigna el nombre al label que esta fuera del updatepanel
    //
    string script = @"$('#{0}').text('{1}');";
    script = string.Format(script, lblSupplierName.ClientID, CompanyName);

    ScriptManager.RegisterStartupScript(this, typeof(Page), "filterinfo", script, true);

}
private void LoadGridProducts()
{
    int? SupplierId = null;

    int id = 0;
    if (Int32.TryParse(txtId.Text, out id))
    {
        SupplierId = id;
    }
    

    gvProducts.DataSource = NorthwindData.GetProductsBySupplier(SupplierId);
    gvProducts.DataBind();
}

Algo que quizás no parezca tener sentido al principio es porque el nombre del proveedor seleccionado es asignado mediante código javascript (o mejor dicho jquery), en lugar de hacerlo directamente, bueno esto se deba al uso del control UpdatePanel, el label donde se asigna el nombre del proveedor seleccionado esta por fuera del panel lo cual requiere que al asignar un valor sea realice de forma indirecta.

 

Selección sin evento (usando html <img>)

Empezaremos analizando como realizar la selección usando un control simple de html, en este caso en el grid de proveedores se usa el tag <img> dentro del item template

Pro medio de la ayuda de jquery se asocia el evento click, usando el atributo class del <img> como selector

<script type="text/javascript">

    $(function() {

        $('#<%=gvSuppliers.ClientID %> .imgSelection').click(function() {

            var supplierID = $(this).attr('SupplierID');
            
            var tr = $(this).parent().parent();
            var companyName = $('td:eq(2)', tr).text();

            window.opener.$("[id*='txtId']").val(supplierID);
            window.opener.$("[id*='lblSupplierName']").text(companyName);

            window.opener.$("[id*='btnFiltrar']").click();

            window.close();
        })

    });

</script>

Como el evento lo lanza el propio tag <img> se puede acceder a este mediante $(this) tomando el atributo, pero para recuperar información adicional debe subirse hasta el tag <tr> de la tabla que genera el gridview para así poder acceder a los datos de las celdas, el td:eq(2), hace referencia a la segunda columna del grid

La integración de jquery con javascript nos permite realizar una búsqueda en la página padre usando window.opener, y mediante el *= sobre el id, una búsqueda aproximada, similar a usar el like.

en las ultimas dos líneas se lanza el evento click del boton de filtro y se cierra la ventana.

 

Selección con evento (uso control asp.net ImageButton)

A diferencia del caso sin evento, en este se hace uso de un control de asp.net para seleccionar el proveedor del grid, como se puede observar en la imagen

Este simple cambio impacta en la forma en que se implementa el resto

<script type="text/javascript">

    function SupplierSelected(supplierID, companyName) {

        window.opener.$("[id*='txtId']").val(supplierID);
        window.opener.$("[id*='lblSupplierName']").text(companyName);

        window.opener.$("[id*='btnFiltrar']").click();

        window.close();
    }

</script>

El javascript usado se reduce, ya no se implementa la búsqueda en al tabla del html que genera el gridview, pues se dispone acceso directo al control desde código .net

protected void gvSuppliers_SelectedIndexChanging(object sender, GridViewSelectEventArgs e)
{
    
    int supplierId = Convert.ToInt32(gvSuppliers.DataKeys[e.NewSelectedIndex].Value);

    string companyName = gvSuppliers.Rows[e.NewSelectedIndex].Cells[2].Text;


    string script = @"$(function(){{
                        SupplierSelected('{0}','{1}');
                    }});"; //se define {{ para poder usar el string.Format()

    script = string.Format(script, supplierId, companyName.Encode());

    ScriptManager.RegisterStartupScript(Page, typeof(Page), "popupclose", script, true);

}

La asignación del valor de retorno no es tan directa, sino que debe ser escrita en el retorno del postback del evento, esto es necesario ya que el window.opener solo puede usarse desde código cliente.

 

Código
[C#]
 

[ASP.NET] PopUp Filtro – Usando jquery UI Dialog

 

Introducción

El objetivo del artículo apunta a demostrar como implementar la búsqueda de un determinado ítem haciendo uso de ventanas que se abran en popup.

Para implementarlo se hará uso de la librería jquery UI, la cual incluye funcionalidad para crear cuadros de dialog modales.

El código fue implementado de dos formas distintas, si bien son muy parecidas tienen sutiles cambios, estas dos formas las denomine:

  • Selección con evento (usando un ImageButton)
  • Selección sin evento  (usando el tag html <img>)

Imagino se preguntaran del porque de esta diferencia del uso de un evento o no, básicamente difieren en el uso de controles asp.net (con evento) o tag de html (sin evento), cada alternativa cambia la forma en que se implementa la solución, en concreto la forma en que se toma el ítem elegido.

Empezaremos analizando la parte de la implementación que es común en ambos casos para luego ir a los detalles que afectan el usar un control de asp.net o uno de html

 

Abrir ventana de filtro

La definición de la ventana por medio de jquery UI solo implica asociar al <div> que representara el dialog

 

$(function() {

    $('#btnOpenSupplierSearch').click(function() {

        registerGridEvent();
        
        $('#dialog').dialog('open');

    });

    $('#dialog').dialog({
        autoOpen: false,
        modal: true,
        width: 700,
        heigth: 250,
        title: 'Buscar Proveedor',
        buttons: {
            "Cancel": function() {
                $(this).dialog("close");
            }
        }
    });

});

En este caso será por medio de una acción del botón de búsqueda que se abrirá la ventana.

 

Selección sin evento (usando html <img>)

Es muy importante en este punto notar como se asocia el <img> que representa la selección de cada row del grid con su correspondiente acción en javascript.

En esta acción desde javascript no solo se toma la información del html, sino que además se cierra el dialog y se lanza el evento de búsqueda accionado el botón que tiene esta funcionalidad.

 

function registerGridEvent() {

    $('#<%=gvSuppliers.ClientID %> .imgSelection').click(function() {

        var supplierID = $(this).attr('SupplierID');

        var tr = $(this).parent().parent();
        var companyName = $('td:eq(2)', tr).text();

        //asigno datos a los controles
        $("[id*='txtId']").val(supplierID);
        $("[id*='lblSupplierName']").text(companyName);

        //cierro el popup de seleccion
        $('#dialog').dialog("close");
        
        //aplico el filtro
        $("[id*='btnFiltrar']").click();
    })

}

Re-asignación de evento jquery

Existe un punto de conflicto entre los eventos de asp.net y jquery, espacialmente cuando se usa el UpdatePanel para conservar los eventos y se mezclan acción de jquery.

Estos dos mundos no se llevan muy bien, un claro ejemplo se podría apreciar con el paginado del gridview, si bien al ir cambiando de pagina no se nota el refresco ya que el UpdatePanel lo hace simple, por debajo se genera una invocación al servidor para ejecutar del evento asociado al grid.

 

protected void gvSuppliers_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
    gvSuppliers.PageIndex = e.NewPageIndex;

    LoadGridSuppliers();

    //
    // registro el evento click del boton, ya que al pagina el postback 
    // quita la asociacion del evento que jquery
    //
    string script = "registerGridEvent();";

    ScriptManager.RegisterClientScriptBlock(this, typeof(Page), "registerevent", script, true);

}

Jquery se ve afectado si es que en ese bloque se tenia eventos asociados en algún tag, en este caso concreto el tag <img> esta asociado al evento click() de jquery, por lo tanto hay que volver a re-asociar para que se revalide la acción.

Es por eso que en el evento de paginado en el server se lanza la acción en el cliente por medio del

ScriptManager.RegisterClientScriptBlock()

Esto es necesario para que a la vuelta del postback se ejecute la function javascript y asigne nuevamente el evento click() de jquery al selector definido.

 

Selección con evento (uso control asp.net ImageButton)

En este caso el camino tiene algunas vueltas entres server y cliente:

De la acción del ImageButton se ingresa al evento SelectedIndexChanged en el código .net, quien luego de tomar la información del gridview armara el script que asignara la info, cerrando el dialogo y por ultimo lanzando la acción de botón para filtrar los productos.

 

protected void gvSuppliers_SelectedIndexChanging(object sender, GridViewSelectEventArgs e)
{

    int supplierId = Convert.ToInt32(gvSuppliers.DataKeys[e.NewSelectedIndex].Value);

    string companyName = gvSuppliers.Rows[e.NewSelectedIndex].Cells[2].Text;


    string script = @"$(function(){{
                        SupplierSelected('{0}','{1}');
                    }});"; //se define {{ para poder usar el string.Format()

    script = string.Format(script, supplierId, companyName.Encode());

    ScriptManager.RegisterStartupScript(Page, typeof(Page), "popupclose", script, true);

}

 

function SupplierSelected(supplierID, companyName) {

    $("[id*='txtId']").val(supplierID);
    $("[id*='lblSupplierName']").text(companyName);

    //cierro el popup de seleccion
    $('#dialog').dialog("close");

    //aplico el filtro
    $("[id*='btnFiltrar']").click();
    
}

Un punto que no hay que pasar por alto que afecta al evento del control dentro del dialog, jquery reasigna las acciones del dialogo a nivel del body y no del form, por eso línea cambia esa acción de esta forma los eventos de asp.net se habilitan nuevamente.

 

Código
[C#]
 

domingo, 29 de abril de 2012

[ASP.NET] PopUp Filtro – Usando Ajax Toolkit ModalPopupExtender

 

Introducción

En esta oportunidad analizaremos como implementar un filtro usando los controles de ajax tookit, mas concretamente el control ModalPopupExtender

Se hará uso de una lista de productos la cual será filtrada por proveedores, desplegados en el popup.

Algo que notaran en el ejemplo será dos formas de implementar la selección del proveedor:

- en una se contara con al ayuda de ToolkitScriptManager para registrar la información seleccionada, será necesario un .js (ListProducts.js), encargado de tomar la info y asignarla a los controles

- la otra será una asignación directa usando jquery (esta ultima seria la recomendable, porque requiere menos objetos involucrados en su implementación)

 

Abrir ventana de búsqueda

La apertura de la ventana de filtro será realizada por medio de un evento asp.net, pero para poder hacerlo es necesario deshabilitar el javascript que asocia el botón con el ModalPopupExtender.

Para lograrlo es que se asigna la propiedad TargetControlID a un objeto hidden, como muestra la imagen

Esto permite que desde el botón se pueda cargar los ítems en el gridview que formara parte del popup de filtro, si se hubiera asignado directo el botón al ModalPopupExtender, este deshabilita los evento de asp.net, por lo que la carga del grid no se hubiera podido llevar a cabo.

 protected void btnOpenSupplierSearch_Click(object sender, EventArgs e)
 {
     LoadGridSuppliers();

     ModalPopupExtender1.Show();

 }

private void LoadGridSuppliers()
 {
     gvSuppliers.DataSource = NorthwindData.GetAllSuppliers();
     gvSuppliers.DataBind();
 }

El uso del Show() sobre el ModalPopupExtender abrirá la ventana, luego de cargar el grid que contiene.

 

Selección ítem en el popup

La selección del proveedor será implementada por medio de un botón de imagen de asp.net, el cual lanzara el evento del gridview, es por eso que se asocia el CommandName=”Select”

La acción de este evento podría ser implementada de dos formas:

Usando al funcionalidad de ToolkitScriptManager

En la implementación de esta opción puede implicar varios puntos a tener en cuenta, el primero involucra hacer uso del ToolkitScriptManager para asignar la información que se quiere devolver como resultado de la selección

protected void gvSuppliers_SelectedIndexChanging(object sender, GridViewSelectEventArgs e)
{
    string id = Convert.ToString(gvSuppliers.DataKeys[e.NewSelectedIndex].Value);
    string name = gvSuppliers.Rows[e.NewSelectedIndex].Cells[2].Text;

    //
    // asigno la informacion a los controles que estan fuera de updatepanel
    //
    ToolkitScriptManager1.RegisterDataItem(txtId, id);
    ToolkitScriptManager1.RegisterDataItem(lblSupplierName, name);


    //
    // Cargo grid de productos con el dato seleccionado
    //
    LoadGridProducts(id);

    //
    // cierro el popup
    //
    ModalPopupExtender1.Hide();
}

 

Pero además implica hacer uso de JavaScript para poder tomar la información enviada en el RegisterDataItem(), es por eso que se asocia un .js que cumplirá esta función

<asp:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server">
    <Scripts>
        <asp:ScriptReference Path="~/ListProducts.js" />
    </Scripts>
</asp:ToolkitScriptManager>

El contenido del .js implementa el siguiente código

Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(SupplierFilterHandler);

function SupplierFilterHandler(sender, args) {
    var datos = args.get_dataItems();

    //obtenermos el id geenrado por asp.net, o sea el ClientID
    var txtId = $("[id*='txtId']").attr('id');
    var lblSupplierName = $("[id*='lblSupplierName']").attr('id');
    

    if (datos[txtId] == undefined)
        return;

    $("[id*='txtId']").val(datos[txtId]);
    $("[id*='lblSupplierName']").text(datos[lblSupplierName]);

}

La idea es poder tomar la información enviada desde el código .net desde el cliente y asignarla a los controles todo ello desde código cliente, sin mezclar parte de .net con parte javascript, separando cada responsabilidad

Se usa el *= ya que el id del control es generado por asp.net (el cual puede cambiar del asignado desde el diseñador), por eso se ubica el nombre real generado, ya que no se disponer del ClientID para tomarlo desde el .js

 

Usando directamente jquery

Este seria el camino directo para asignar la información del filtro en la pagina principal

protected void gvSuppliers_SelectedIndexChanging(object sender, GridViewSelectEventArgs e)
{
    string id = Convert.ToString(gvSuppliers.DataKeys[e.NewSelectedIndex].Value);
    string name = gvSuppliers.Rows[e.NewSelectedIndex].Cells[2].Text;

    //
    // asigno la informacion a los controles que estan fuera de updatepanel
    //
    string script = @" $(""[id*='txtId']"").val('{0}');
                       $(""[id*='lblSupplierName']"").text('{1}');";

    script = string.Format(script, id, name.Encode());

    ScriptManager.RegisterStartupScript(this, typeof(Page), "filterinfo", script, true);

    //
    // Cargo grid de productos con el dato seleccionado
    //
    LoadGridProducts(id);

    //
    // cierro el popup
    //
    ModalPopupExtender1.Hide();
}

 

En esta implementación solo es necesario definir el script que se encargara de asignar los datos a los controles y enviarlo al cliente por medio del ScriptManager.RegisterStartupScript().

Esto es mucho mas directo y sin vueltas que el paso anterior, aunque requiere generar código javascript (ayudado con jquery) inserto en medio del con código .net en el servidor, a veces puede gustar, a veces no, mezclar este tipo de código, pero si hay que reconocer que es el camino directo en su implementación.

 

Código
[C#]
 

lunes, 2 de abril de 2012

ASP.NET–GridView anidados (maestro-detalle) (GridView nested)

 

Introducción

El objetivo en el articulo será el demostrar como armar un grid con jerarquía, en este caso mostrando las ordenes de compra de un cliente

Además se pretende demostrar como se puede armar una entidad compleja que retorne los datos en una estructura anidada de listas

 

Detectar despliegue detalle

Uno de los puntos mas importantes en la implementación esta relacionado con la forma en que se detecta el expandir\colapsar del detalle

En la imagen se remarcan dos atributos creados con este propósito, la idea es poder relacionar la imagen con el tag <tr> que permite ocultar o mostrar el grid del detalle

La idea detrás de esto es ayudar luego a jquery para poder determinar que elementos intervienen en la acción

con jquery se extrae el id de la orden, para luego usarlo de selector del <tr> que posee ese mismo id

el toggle() solo permite switchear entre visible\invisible, y detectando el estado mostrar una imagen u otra

 

Estructura de datos jerárquica

La estructura usada para el modelo relaciona tres clases

La idea es mostrar como al cargar los datos, es analizar como los métodos se invocan unos a otros para recuperar la info enlazada en al jerarquía

 

Código

El codigo fue confeccionado con Visual Studio 2008 y base de datos Sql Compact 3.5

[C#]
[C# SkyDrive]