martes, 29 de mayo de 2012

[ASP.NET] - GridView sumar columnas con jquery

 

Introducción

Cuando se trabaja integrando código cliente (javascript) con asp.net seguramente en algún punto se necesite realizar operaciones con tablas, siendo el control GridView  usado en la mayoría de estos casos, es por eso se intentara mostrar las diferentes formas en que se puede realizar operaciones con código cliente usando jquery para trabajar con tablas

Para el ejemplo se listara productos con sus respectivos precios, el usuario completara la cantidad que se solicita de ese producto, realizando el calculo del precio final que deberá abonarse.

 

Calculo del precio por producto

La primer operación que debe programarse corresponde al calculo a nivel de fila, o sea de cada producto. La implementación completa podrá verse en el código dentro de al carpeta “Ej1”.

Como se observa en la imagen la columna del precio unitario es fija, el usuario ingresa una cantidad y al quitar el foco del textbox automáticamente se calcula el precio.

Para lograrlo será necesario adjuntar un evento de cambio a cada textbox del gridview, recordemos que el control termina renderizado como una tabla html, por lo tanto, se puede trabajar en el cliente con el html resultante.

<script language="javascript" type="text/javascript">

    $(document).ready(function() {

        $("#<%=GridView1.ClientID%> [id*='txtCantidad']").change(function() {
        
            var tr = $(this).parent().parent();
            var precio = $("td:eq(1)", tr).html();

            $("td:eq(3) span", tr).html($(this).val() * precio);

        });

    });
    
</script>

Ante un evento se recupera el textbox que lanza la acción, es por eso que se utiliza $(this), el this en este caso es el textbox, se sube dos niveles en la estructura del html, o sea a nivel del <tr> que contiene a ese textbox, para luego aplicar la selección a otra columna, en este caso la del precio, por eso se utiliza td:eq(1) seria la segunda columna (recordar que los índices comienzan en cero)

El ultimo paso será asignar el resultado, se aplica la misma técnica, se selecciona la columna y por tratarse de un label de asp.net el cual renderiza a <span> se usa este selector en jquery.

Si bien en la implementación puede observarse solo una única forma para asignar el evento a los textbox contenido en la columna de gridview pueden ser usados otro selectores de jquery dependiendo la necesitad:

 

Usando el atributo class

En este caso el selector hace uso de un atributo del control para poder localizarlo, todos los textbox que el gridview genere tendrán el mismo atributo “class”

$('#<%=GridView1.ClientID%> .cantidad').change(function() { ….

Es necesario definir este atributo en el control del témplate del grid

Usando un doble selector

En este caso se aplicara una doble selección, primero se selecciona lo definido a la derecha y sobre esto aplica lo definido en al izquierda.

$('td:eq(2) :text', '#<%=GridView1.ClientID%> tr').change(function() {

La parte derecha selecciona todas las filas del grid, mientras que la izquierda toma solo la tercer columna (Cantidad Pedido), el uso de “:text” define que solo los textbox de la celda serán incluidos

 

Usando el operador like *=

Por medio del operador de selección mediante búsqueda aproximada, se puede tomar todos aquellos controles que tengan un misma patrón en su id

$("#<%=GridView1.ClientID%> [id*='txtCantidad']").change(function() {

En el caso del gridview creara un control por cada dato que se le asigne, repitiendo el textbox N veces, pero todos tendrán la palabra txtCantidad en alguna parte de su id

 

Calculo del precio Total

Calcular el precio total, implica sumar una columna completa para tener el precio total que se sumara en el footer del gridview. La implementación completa podrá verse en el código dentro de al carpeta “Ej2”.

El añadido que se realiza a la suma de la fila del grid solo implica la llamada a una funciona para recalcular el total.

Un punto conflictivo cuando se suma una columna es el hecho de paginar la grid, vamos a ver en este caso como se lograría una implementación con y sin paginado

 

Calculo total sin paginado

Al no paginar se puede reconocer el footer como ultima fila de la tabla pudiendo aplicar selectores que tengan en cuenta esta situación.

function CalcularTotal() {

    var total = 0;
    $('#<%=GridView1.ClientID%> tr:not(:last)').each(function() {
    
        var coltotal = parseFloat($("td:eq(3) span", this).html());
        
        if (!isNaN(coltotal)) {
            total += coltotal;
        }

    });

    $('#<%=GridView1.ClientID%> tr:last td:eq(3) span').html(total);
}

Se puede hacer uso de “:last” para agregar o descartar esta ultima fila del grid donde se presentara el total.

Se recorre todas las filas del gridview, dejando fuera el footer, por eso es “:not(:last)”, se esta descartando el ultimo <tr>. Pero si se toma exactamente la ultima fila cuando se quiere asignar el total resultante, por eso se usa el “tr:last”

Nota: por supuesto hay que quitar la propiedad AllowPaging="True" del gridview para probarlo sin paginado

 

Calculo total con paginado

Al tener paginado la ultima final ya no será el footer, por lo tanto usar el :last no funciona, se requiere otras técnicas de selección

function CalcularTotal() {

    var total = 0;
    $("#<%=GridView1.ClientID%> [id*='lblPrecio']").each(function() {

        var coltotal = parseFloat($(this).html());

        if (!isNaN(coltotal)) {
            total += coltotal;
        }

    });

    $("#<%=GridView1.ClientID%> [id*='lblTotal']").html(total);

}

 

Se decide ir directamente a los controles que contienen la información.

Al tomar el valor del <span> (que representa el lblPrecio), puede que este no sea un valor numérico valido, es por eso que se parsea a float, si devuelve NaN es porque no es un numero que sumar.

 

Código

Se ha usado Visual Studio 2008, es necesario SP1 del VS, como base de datos Sql Compact 3.5

 

jueves, 17 de mayo de 2012

[Winforms] TreeView– Cambiar Estado Node con CheckBox

 

Introducción

Muchas veces es necesario cambiar el estado de ítems predecesores o hijo según ciertos criterios, el control TreeView por posee una estructura jerárquica en sus datos es un caso de esta situación.

 

Validar estado de los nodos


En la implementación de código que se puede observar aplica conceptos simples como de validación de estados de los nodos del treeview.

Básicamente consiste en dos parte

  •    validar si el nodo tiene un nodo padre, para lo cual será necesario verificar si todos los hijos están marcados
  •    cambiar el estado a todos los hijos según la marca de este nodo

Estas acciones se realizan sobre el mismo evento ya que implica de forma contextual al nodo que interviene en la acción

 

private void tvCategoryproducts_AfterCheck(object sender, TreeViewEventArgs e)
{
    //
    // Se remueve el evento para evitar que se ejecute nuevamente por accion de cambio de estado 
    // en esta operacion
    //
    tvCategoryproducts.AfterCheck -= tvCategoryproducts_AfterCheck;

    //
    // Se valida si el nodo marcado tiene presedente
    // en caso de tenerlo se debe evaluar los nodos al mismo nivel para determinar si todos estan marcados, 
    // si lo estan se marca tambien el nodo padre
    //
    if (e.Node.Parent != null)
    {
        bool result = true;
        foreach (TreeNode node in e.Node.Parent.Nodes)
        {
            if (!node.Checked)
            {
                result = false;
                break;
            }
        }

        e.Node.Parent.Checked = result;

    }

    //
    // Se valida si el nodo tiene hijos
    // si los tiene se recorren y asignan el estado del nodo que se esta evaluando
    //
    if (e.Node.Nodes.Count > 0)
    {
        foreach (TreeNode node in e.Node.Nodes)
        {
            node.Checked = e.Node.Checked;
        }
    }


    tvCategoryproducts.AfterCheck += tvCategoryproducts_AfterCheck;

}

 

Optimización usando Linq

Linq nos puede evitar tener que escribir un montón de código, sino lo creen solo basta con dar una mirada a la implementación alternativa que evalúa los nodos marcados

En este caso hice uso de los método de extensión ayudado con lambda, pero se podría haber usado la notación linq sin problemas

 

private void tvCategoryproducts_AfterCheck_UsingLinq(object sender, TreeViewEventArgs e)
{
    //
    // Se remueve el evento para evitar que se ejecute nuevamente por accion de cambio de estado 
    // en esta operacion
    //
    tvCategoryproducts.AfterCheck -= tvCategoryproducts_AfterCheck_UsingLinq;

    //
    // Se valida si el nodo marcado tiene presedente
    // en caso de tenerlo se debe evaluar los nodos al mismo nivel para determinar si todos estan marcados, 
    // si lo estan se marca tambien el nodo padre
    //
    if (e.Node.Parent != null)
    {
        e.Node.Parent.Checked = !e.Node.Parent.Nodes.Cast<TreeNode>().Any(x => !x.Checked);
    }

    //
    // Se valida si el nodo tiene hijos
    // si los tiene se recorren y asignan el estado del nodo que se esta evaluando
    //
    if (e.Node.Nodes.Count > 0)
    {
        e.Node.Nodes.Cast<TreeNode>().ToList().ForEach(x => x.Checked = e.Node.Checked);
    }


    tvCategoryproducts.AfterCheck += tvCategoryproducts_AfterCheck_UsingLinq;

}

Código


Se utilizo Visual Studio 2010 y Sql Compect 3.5

  [C# SkyDrive]