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

El Libro del Día: Designing for Performance

El Libro del Día: 2015-02-06

Titulo: Designing for Performance
Autor: Lara Callender Hogan
Editorial: O'Reilly
Nro Paginas: 181

Capítulos:
Chapter 1 Performance Is User Experience
Chapter 2 The Basics of Page Speed
Chapter 3 Optimizing Images
Chapter 4 Optimizing Markup and Styles
Chapter 5 Responsive Web Design
Chapter 6 Measuring and Iterating on Performance
Chapter 7 Weighing Aesthetics and Performance
Chapter 8 Changing Culture at Your Organization

Descarga:
Designing_for_Performance

jueves, 5 de febrero de 2015

El Libro del Día: WebRTC Integrator's Guide

El Libro del Día: 2015-02-05

Titulo: WebRTC Integrator's Guide
Autor: Altanai
Editorial: Packt
Nro Paginas: 382

Capítulos:
Chapter 1: Running WebRTC with and without SIP
Chapter 2: Making a Standalone WebRTC Communication Client
Chapter 3: WebRTC with SIP and IMS
Chapter 4: WebRTC Integration with Intelligent Network
Chapter 5: WebRTC Integration with PSTN
Chapter 6: Basic Features of WebRTC over SIP
Chapter 7: WebRTC with Industry Standard Frameworks
Chapter 8: WebRTC and Rich Communication Services
Chapter 9: Native SIP Application and Interaction with WebRTC Clients
Chapter 10: Other WebRTC Use Cases

Descarga:
WebRTC_Integrator's_Guide

miércoles, 4 de febrero de 2015

El Libro del Día: Highcharts Cookbook

El Libro del Día: 2015-02-04

Titulo: Highcharts Cookbook
Autor: Nicholas Terwoord
Editorial: Packt
Nro Paginas: 332

Capítulos:
Chapter 1: Getting Started with Highcharts
Chapter 2: Processing Data
Chapter 3: Handling User Interaction
Chapter 4: Sharing Charts on the Web
Chapter 5: Integrating with ExtJS
Chapter 6: Integrating with jQuery
Chapter 7: Integrating with the Yii Framework
Chapter 8: Integrating with Other Frameworks
Chapter 9: Extending Highcharts
Chapter 10: Math and Statistics
Chapter 11: System Integration
Chapter 12: Other Inspirational Uses

Descarga:
Highcharts_Cookbook

martes, 3 de febrero de 2015

Entrenamiento - Capacitación en Desarrollo .NET a Empresas

Capacitación en Desarrollo .NET a Empresas

A las empresas interesadas en cursos cerrados para el personal de desarrollo de sistemas les comunico que estoy dictando capacitaciones para mejorar el desempeño del personal encargado de construir las aplicaciones y mejorar el tiempo y la calidad de los desarrollos en .NET.

Cursos

Los cursos ofrecidos son los siguientes:

- Desarrollo de Aplicaciones WinForms en .NET
- Desarrollo de Aplicaciones Web con ASP.NET WebForms
- Desarrollo de Aplicaciones Web con ASP.NET MVC
- Desarrollo de Aplicaciones Windows con WPF
- Desarrollo de Aplicaciones Web Móviles con ASP.NET
- Desarrollo de Aplicaciones Web con HTML5, CSS3 y JavaScript APIs

Beneficios

Los beneficios de la capacitación son los siguientes:

- Solo se ven Buenas Prácticas
- Orientado a la Performance
- 100% Práctico
- Ejemplos reales del día a día
- Abundante material (Manuales, Videos, etc)
- Syllabus a medida del requerimiento

Horarios

Todos los horarios son de 6 horas semanales, y hay diferentes tipos de horarios, entre ellos:
- Lunes, Miércoles y Viernes de 8:00 am a 10:00 am.
- Martes y Jueves de 7:30 am a 10:30 am.
- Lunes, Miércoles y Viernes de 8:00 pm a 10:00 pm.
- Domingos de 8:00 am a 2:00 pm.

Inversión

El costo por hora es de 100 soles y el costo total dependerá del número de horas que es variable de acuerdo al curso y la necesidad de la empresa.

Contacto

Los interesados enviarme un mail a Luis.duenash@gmail,com) con los siguientes datos:
- Asunto: Capacitación en Desarrollo .NET a Empresas
- Empresa: Nombre de la empresa
- Dirección: Dirección de la empresa (con alguna referencia)
- Contacto: Nombre del contacto
- Teléfono: Número de teléfono fijo de la empresa o celular del contacto
- Curso: Curso(s) por el que se interesa y el rango de horas aproximado que necesitan
- Participantes: Número de participantes del curso y el nivel actual (básico, intermedio, avanzado)

Syllabus

A los interesados que envíen su correo les estaré enviando el Syllabus personalizado de acuerdo a su requerimiento.

Acerca del Instructor

El instructor ha capacitado a más de 1000 personas en .NET desde el 2001, mayor información:

Comentario Final

Actualmente, hay muchos sistemas y aplicaciones web en nuestro país que son demasiado lentos y  no es un tema de hardware sino de software, específicamente de aplicación, debido a que los desarrolladores piensan hacerse la vida fácil en el momento de desarrollo sin importar el momento final (producción) cuando los usuarios usen las aplicaciones.

Se construye fácil, usando Frameworks de terceros, se usan estructuras de datos sin analizar sus características, y en general se piensa en patrones de diseño de desarrollo y No en patrones de performance que es lo que mas importa actualmente.

Una buena capacitación es la mejor inversión para aprender las buenas prácticas y nivelar al personal de sistemas. Las empresas que no piensan en esto como parte de su funcionamiento, siempre terminan perdiendo por creer que están ahorrando y confiar en que todos sus empleados "son investigadores".

El Libro del Día: Data Visualization For Dummies

El Libro del Día: 2015-02-03

Titulo: Data Visualization For Dummies
Autor: Mico Yuk, Stephanie Diamond
Editorial: Wiley
Nro Paginas: 258

Capítulos:
Part I: Getting Started with Data Visualization
Chapter 1: Introducing Data Visualization
Chapter 2: Exploring Common Types of Data Visualizations
Chapter 3: Knowing What You Must about Big Data
Part II: Mastering Basic Data Visualization Concepts
Chapter 4: Using Charts Effectively
Chapter 5: Adding a Little Context
Chapter 6: Paying Attention to Detail
Part III: Building Your First Data Visualization
Chapter 7: Defining an Easy-to-Follow Storyboard
Chapter 8: Developing a Clear Mock-Up
Chapter 9: Adding Effective Visuals to Your Mock-Up
Chapter 10: Adding Functionality and Applying Color
Chapter 11: Adding Some Finishing Touches
Chapter 12: Exploring User Adoption
Part IV: Putting Data Viz Techniques into Practice
Chapter 13: Evaluating Real Data Visualizations
Chapter 14: Recognizing Newbie Pitfalls
Part V: The Part of Tens
Chapter 15: Top Ten Data Visualization Resources
Chapter 16: Top Ten Fears of New Data-Viz Creators

Descarga:
Data_Visualization_For_Dummies

lunes, 2 de febrero de 2015

Entrenamiento - Fotos del Ultimo Seminario

Fotos del Ultimo Seminario

Antes que nada agradecer a todos los asistentes al último seminario de .NET: "Creación de Librería de Controles con .NET" y también agradecer a la empresa HTE por organizar el evento. Comparto algunas fotos para que las publiquen en sus Redes Sociales.







Mas fotos ver en el siguiente enlace:
Fotos Facebook HTE

Los Demos del seminario se los estoy enviando en unos momentos.