Mostrando las entradas con la etiqueta Agrupar. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Agrupar. Mostrar todas las entradas

miércoles, 18 de mayo de 2016

Videos - El Reto o Desafío JavaScript

Videos - El Reto o Desafío JavaScript

Estimados visitantes del Blog, hace un buen tiempo que no escribo un post. Esta vez lo hago para informarles que he creado una Lista de Reproducción en mi Canal de YouTube llamado "Reto JavaScript" que consiste en crear código solo con JavaScript sin usar ningún Framework ni librería cliente, solo JavaScript puro (no jQuery, Angujar, Ember, etc).

Con esto quiero lograr 2 cosas:

1. Demostrar que todo lo que ven que hacen las librerías mas populares se puede conseguir con poco código y mejor (sobre todo si esta totalmente desconectado).

2. Fomentar la creatividad, la investigación, el talento y el esfuerzo de los desarrolladores para que estén preparados para crear cualquier tipo de requerimiento sin depender de terceros.

El tiempo propuesto para realizar cada reto es de una semana y la solución, es decir el código JavaScript será publicado en este medio.

RetoJS 00 - Presentación e Introducción al RetoJS

En este primer video hago una presentación personal de mi experiencia y los objetivos de querer fomentar la creatividad de los desarrolladores a través del Reto JavaScript, el cual consiste en el plazo de una semana crear una aplicación con un solo archivo JavaScript que realice la funcionalidad solicitada.

La idea es que se olviden poco a poco de los Frameworks y Librerías de JavaScript que son negativos para la Performance sobre todo si se abusa de ellos y que sean reemplazados por la lógica y la creatividad de tal forma que les permita programar en forma nativa cualquier cosa.


RetoJS 01 - Mover Columnas en una Tabla de Datos 

En este video se muestra el primer Reto JavaScript que consiste en realizar una aplicación que permita mover el orden de las columnas de una tabla en el lado del cliente, solo con JavaScript.

En el ejemplo se muestra la tabla de productos con 6 columnas, las cuales se pueden arrastrar en cualquier dirección, ya sea de izquierda a derecha o de derecha a izquierda, cambiando el orden de la presentación.


RetoJS 02 - Agrupar Celdas en una Tabla de Datos

En este video se muestra el segundo Reto JavaScript que consiste en realizar una aplicación que permita agrupar las celdas por una o varias columnas de una tabla en el lado del cliente, solo con JavaScript.

En el ejemplo se muestra la tabla de productos con 6 columnas, las cuales se pueden arrastrar a la parte superior que indica la agrupación, para lo cual primero se ordena y luego se combina las celdas. También hay un botón que permite eliminar la agrupación y regresar a la vista normal (no agrupada).


RetoJS 03 - Mover Columnas y Agrupar Celdas en una Tabla

En este video se muestra el tercer Reto JavaScript que consiste en realizar una aplicación que permita mover el orden de las columnas y agrupar celdas de una tabla en el lado del cliente, solo con JavaScript.

En el ejemplo se muestra la tabla de productos con 6 columnas, las cuales se pueden arrastrar en cualquier dirección, ya sea de izquierda a derecha o de derecha a izquierda, cambiando el orden de la presentación.

Además, debe poderse arrastrar las cabeceras a la parte superior que indica agrupación, para lo cual primero se ordena y luego se combina las celdas. También hay un botón que permite eliminar la agrupación y regresar a la vista normal (no agrupada).


Comentario Final

Estas tareas propuestas son comunes en el trabajo con datos en las aplicaciones web, es por eso, que en este último taller vamos a incluir todos los retos propuestos y lo haremos desde cero.

Que se diviertan, y demuestren todo su potencial.

viernes, 6 de febrero de 2015

El Demo del Día: Agrupación de Celdas del DataGridView en WinForms

Agrupación de Celdas del DataGridView en WinForms

Requerimiento

Se necesita agrupar los valores repetidos de ciertas columnas del DataGridView en una Aplicación Windows en .NET, por ejemplo para ver los productos por categorías o por proveedores sin tener que repetir varias veces el mismo valor.

Solución

Crearemos una clase personalizada llamada Grilla que herede del DataGridView y que sobre escriba los eventos "OnCellPainting" y "OnCellFormatting".
En el primer evento (CellPainting) colocaremos los bordes solo cuando el valor de la columna sea diferente y en el segundo evento (CellFormatting) borraremos los valores repetidos de cada columna.
En ambos casos solo se considera si es una columna a combinar, para lo cual crearemos una propiedad llamada "GroupColumns".

Crear el Procedimiento Almacenado en la Base de Datos de SQL Server

Para el ejemplo usaremos la Base de Datos Northwind de SQL Server, en la cual crearemos el siguiente Procedimiento almacenado:

Create Procedure uspProductsListarStock
As
Select p.ProductName,p.CategoryID,c.CategoryName,UnitsInStock
From Products p
Inner Join Categories c On c.CategoryID=p.CategoryID
Order By 2,3,1

Crear una Aplicación Windows Forms en C#

En Visual Studio crear un proyecto Windows Forms en C# con el nombre de "DataGridView_Agrupado", luego cambiar el nombre del formulario a "frmListaProductos".

Crear la Clase Entidad del Negocio

Crear la clase beProducto escribiendo el siguiente código:

namespace DataGridView_Agrupado
{
    public class beProducto
    {
        public int IdCategoria { get; set; }
        public string NombreCategoria { get; set; }
        public string NombreProducto { get; set; }
        public short Stock { get; set; }
    }
}

Crear la Clase de Acceso a Datos

Crear la clase daProducto escribiendo el siguiente código:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;

namespace DataGridView_Agrupado
{
    public class daProducto
    {
        public List<beProducto> listarStock(SqlConnection con)
        {
            List<beProducto> lbeProducto = null;
            SqlCommand cmd = new SqlCommand("uspProductsListarStock", con);
            cmd.CommandType = CommandType.StoredProcedure;
            SqlDataReader drd = cmd.ExecuteReader(CommandBehavior.SingleResult);
            if (drd != null)
            {
                lbeProducto = new List<beProducto>();
                int posNombreProducto = drd.GetOrdinal("ProductName");
                int posIdCategoria = drd.GetOrdinal("CategoryID");
                int posNombreCategoria = drd.GetOrdinal("CategoryName");
                int posStock = drd.GetOrdinal("UnitsInStock");
                beProducto obeProducto;
                while (drd.Read())
                {
                    obeProducto = new beProducto();
                    obeProducto.NombreProducto = drd.GetString(posNombreProducto);
                    obeProducto.IdCategoria = drd.GetInt32(posIdCategoria);
                    obeProducto.NombreCategoria = drd.GetString(posNombreCategoria);
                    obeProducto.Stock = drd.GetInt16(posStock);
                    lbeProducto.Add(obeProducto);
                }
                drd.Close();
            }
            return (lbeProducto);
        }
    }
}

Nota: Tiene que haberse creado el Procedimiento Almacenado "uspProductsListarStock" en la BD Northwind.

Crear la Clase de Reglas del Negocio

Crear la clase brProducto escribiendo el siguiente código:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;

namespace DataGridView_Agrupado
{
    public class brProducto
    {
        public List<beProducto> listarStock()
        {
            List<beProducto> lbeProducto = null;
            string CadenaConexion = ConfigurationManager.ConnectionStrings 
                                                      ["conNW"].ConnectionString;
            using (SqlConnection con = new SqlConnection(CadenaConexion))
            {
                try
                {
                    con.Open();
                    daProducto odaProducto = new daProducto();
                    lbeProducto = odaProducto.listarStock(con);
                }
                catch (Exception ex)
                {
                    //grabarLog(ex);
                }
            }
            return (lbeProducto);
        }
    }
}

Nota: Hay que hacer referencia a la librería "System.Configuration.dll" para usar la clase ConfigurationManager para leer el archivo de configuración.

Crear una Clase para Combinar las Celdas del DataGridView

Crear la clase "Grilla" escribiendo el siguiente código:

using System;
using System.Drawing;
using System.Windows.Forms;

namespace DataGridView_Agrupado
{
    public class Grilla : DataGridView
    {
        public string[] GroupColumns { get; set; }

        private bool esRepetidoValorCelda(int fila, int columna)
        {
            bool exito = false;
            int c = fila - 1;
            dynamic valorCelda = this.Rows[fila].Cells[columna].Value;
            dynamic valorComparar;
            while (c > -1)
            {
                valorComparar = this.Rows[c].Cells[columna].Value;
                if (valorCelda.Equals(valorComparar))
                {
                    exito = true;
                    break;
                }
                c--;
            }
            return (exito);
        }

        private bool esColumnaCombinar(string nombreColumna)
        {
            bool exito = false;
            foreach (string columna in GroupColumns)
            {
                if (columna.Equals(nombreColumna))
                {
                    exito = true;
                    break;
                }
            }
            return (exito);
        }

        protected override void OnCellPainting(DataGridViewCellPaintingEventArgs e)
        {
            base.OnCellPainting(e);
            e.AdvancedBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.None;
            if (e.RowIndex < 1 || e.ColumnIndex < 0) return;
            string nombreColumna = this.Columns[e.ColumnIndex].Name;
            bool esCombinada = (GroupColumns!=null && esColumnaCombinar(nombreColumna));
            if ((esCombinada && !esRepetidoValorCelda(e.RowIndex, e.ColumnIndex)) 
            || !esCombinada) e.AdvancedBorderStyle.Top = AdvancedCellBorderStyle.Top;
            else e.AdvancedBorderStyle.Top = DataGridViewAdvancedCellBorderStyle.None;
        }

        protected override void OnCellFormatting(DataGridViewCellFormattingEventArgs e)
        {
            base.OnCellFormatting(e);
            bool esNumero = (e.Value is Int16||e.Value is Int32||e.Value is Int64||e.Value is int
                                          ||e.Value is decimal);
            if (esNumero)
            {
                e.CellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
                bool esDecimal = (e.Value is decimal);
                if (esDecimal) e.CellStyle.Format = "n2";
            }
            string nombreColumna = this.Columns[e.ColumnIndex].Name;
            bool esCombinada = (GroupColumns != null && esColumnaCombinar(nombreColumna));
            if (esCombinada&&esRepetidoValorCelda(e.RowIndex, e.ColumnIndex))
            {
                e.Value = string.Empty;
                e.FormattingApplied = true;
            }
        }
    }
}

Nota: La clase Grilla hereda del DataGridView y sobre escribe los eventos "CellPainting" y "CellFormatting". Ademas se crea una propiedad llamada "GroupColumns" para especificar las columnas que se desean combinar o agrupar sus valores repetidos, para eso creamos 2 funciones: "esRepetidoValorCelda" y "esColumnaCombinar".

Usar la Clase Grilla en el Formulario

Regresar al formulario "frmListaProductos" y escribir el siguiente código:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace DataGridView_Agrupado
{
    public partial class frmListaProductos : Form
    {
        public frmGraficoProductos()
        {
            InitializeComponent();
        }

        private void listarProductos(object sender, EventArgs e)
        {
            brProducto obrProducto = new brProducto();
            List<beProducto> lbeProducto = obrProducto.listarStock();
            Grilla dgvProducto = new Grilla();
            dgvProducto.Dock = DockStyle.Fill;
            dgvProducto.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
            //dgvProducto.GroupColumns = new string[] { "IdCategoria", "NombreCategoria"};
            dgvProducto.DataSource = lbeProducto;
            this.Controls.Add(dgvProducto);
        }
    }
}

Nota: En el evento "load" del formulario se llama a la función "listarProductos" que obtiene los datos de los productos y lo almacena en una lista de objetos, luego esta se enlaza al control Grilla creado.
Inicialmente la línea de código donde se configura la propiedad GroupColumns esta comentada.

Modificar el archivo App.Config para incluir la Cadena de Conexión

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <connectionStrings>
      <add name="conNW" providerName="System.Data.SqlClient" connectionString="uid=UsuarioNW;pwd=123456;data source=DSOFT\Sqlexpress;initial catalog=Northwind"/>
    </connectionStrings>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
</configuration>

Probar la Aplicación Windows Forms

Grabar la aplicación y ejecutarla con F5. Se mostrará la siguiente ventana:


Observar que los valores de las columnas IdCategoria y NombreCategoria salen repetidos, que es la forma normal como funciona el DataGridView.

Detener la ejecución de la aplicación, descomentar la línea comentada:
dgvProducto.GroupColumns = new string[] { "IdCategoria", "NombreCategoria"};

Probar nuevamente y se mostrará una ventana similar a la siguiente figura:


Observar que las columnas IdCategoria y NombreCategoria no se repite los valores ya que se agrupo mediante la propiedad "GroupColumns".

Detener nuevamente la ejecución de la aplicación y eliminar el campo "NombreCategoria" de la propiedad GroupColumns, tal como sigue:
dgvProducto.GroupColumns = new string[] { "IdCategoria" };

Probar nuevamente y se mostrará una ventana similar a la siguiente figura:


Observar que solo la primera columna esta agrupada. Si deseas puedes aumentar nuevos productos con nombres iguales y aumentar a la propiedad "GroupColumns" el campo "NombreProducto".

Comentario Final

En este post hemos visto como agrupar celdas repetidas en una o varias columnas del DataGridView de Windows Forms para lo cual sobre escribimos los eventos CellPainting y CellFormatting que son los 2 principales eventos de dicho control.

En realidad la agrupación de valores repetidos No ha combinado las celdas sino solo ha borrado los valores repetidos y ha puesto el borde superior. Si deseas combinar las celdas se tendría que programar la escritura en el evento Paint causando demora al renderizar, es decir, la técnica que estamos usando es la mas eficiente aunque no se vea fusionada la celda.

Descarga