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

miércoles, 10 de febrero de 2016

Videos Curso ASP.NET MVC & JavaScript HTE (Parte 2)

Videos del Curso ASP.NET MVC & JavaScript HTE (Parte 2)

Comparto la segunda parte del curso de ASP.NET MVC & JavaScript realizado en la Empresa HTE donde todo se trabaja en forma desconectada usando en el Servidor mi Serializador de Cadenas y JavaScript en el Cliente, sobre todo Matrices.

1. Curso MVC & JavaScript HTE - Demo 09

En este video se muestra como ordenar columnas en forma desconectada, tanto ascendente como descendente usando matrices en JavaScript.

Para el ejemplo se crean enlaces en la cabecera de la tabla, los cuales permiten ordenar sobre cada campo de la tabla productos, el primer clic ordena en forma ascendente y el segundo ordena en forma descendente mostrándose el símbolo respectivo.


2. Curso MVC & JavaScript HTE - Demo 10

En este video se muestra como paginar registros usando matrices en el cliente usando JavaScript. La paginación consta de enlaces (links) solo con los números de páginas, es decir, una paginación simple.

En este ejemplo se usan botones para los números de paginas, los cuales se ven en forma de círculo, con fondo y color debido al estilo, el cual cambia de apariencia al pasar el mouse (hover).


3. Curso MVC & JavaScript HTE - Demo 11

En este video se muestra como paginar registros usando matrices en el cliente usando JavaScript. La paginación consta de enlaces (links) con los números de páginas y también con 4 botones a los extremos (2 en cada lado) que permiten trabajar por bloques.

Este tipo de paginación es necesaria cuando se tiene muchas páginas y debemos agruparlas por bloques, para poder ver mejor y navegar rápidamente al inicio o al final.


4. Curso MVC & JavaScript HTE - Demo 12

En este video se muestra como hacer todas las operaciones en forma conjunta, es decir, paginar registros, ordenar columnas, filtrar registros y exportar registros a texto y excel todo en el cliente (desconectado) usando matrices en JavaScript.

El ejemplo es con la lista de productos y los datos están paginados y por bloques, también las columnas se pueden ordenar en forma ascendente y descendente, también se puede filtrar por cada campo escribiendo sobre los textos de las cabeceras en cada columna de la tabla y finalmente los datos filtrados se pueden exportar en un archivo de texto o en un archivo de excel.


5. Curso MVC & JavaScript HTE - Demo 13 (Parte 1)

En este video se muestra la primera parte de como realizar un mantenimiento de una tabla, es decir, las operaciones de adicionar, actualizar y eliminar registros.

En el ejemplo se muestra una lista de productos paginada por bloques, la cual se puede ordenar, filtrar, exportar a texto y excel. Si queremos crear un nuevo producto o editar uno existente se mostrará un popup (div dentro de la misma pagina).

Después de adicionar o actualizar un registro se actualiza la lista de productos y se cierra el popup. Finalmente, se puede eliminar desde la lista un registro previa confirmación.


6. Curso MVC & JavaScript HTE - Demo 13 (Parte 2)

En este video se muestra la segunda parte de como realizar un mantenimiento de una tabla, es decir, las operaciones de adicionar, actualizar y eliminar registros.

En el ejemplo se muestra una lista de productos paginada por bloques, la cual se puede ordenar, filtrar, exportar a texto y excel. Si queremos crear un nuevo producto o editar uno existente se mostrará un popup (div dentro de la misma pagina).

Después de adicionar o actualizar un registro se actualiza la lista de productos y se cierra el popup. Finalmente, se puede eliminar desde la lista un registro previa confirmación.


7. Curso MVC & JavaScript HTE - Demo 14 (Parte 1)

En este video se muestra la primera parte de cómo crear una cabecera detalle para una orden usando una sola página, ya que se usa solo 2 divs: uno para la parte principal y otro para las búsquedas.

Los datos de los clientes, empleados y productos para las búsquedas se cargan desde un inicio y se presentan en el mismo popup (div) usando la misma funcionalidad para los 3: filtro, ordenación y paginación, permitiendo seleccionar dando un clic.

También se realiza la validación de la cabecera y los detalles usando JavaScript y mostrando los resultados en un span como viñetas y filas de colores si no están completos los datos del detalle.

Finalmente, se graba la orden usando una cadena que viaja hasta el Procedimiento Almacenado de SQL Server usando un solo comando y una sola conexión. Además se puede imprimir la orden creada usando JavaScript para crear el HTML respectivo.


8. Curso MVC & JavaScript HTE - Demo 14 (Parte 2)

En este video se muestra la segunda parte de cómo crear una cabecera detalle para una orden usando una sola página, ya que se usa solo 2 divs: uno para la parte principal y otro para las búsquedas.

Los datos de los clientes, empleados y productos para las búsquedas se cargan desde un inicio y se presentan en el mismo popup (div) usando la misma funcionalidad para los 3: filtro, ordenación y paginación, permitiendo seleccionar dando un clic.

También se realiza la validación de la cabecera y los detalles usando JavaScript y mostrando los resultados en un span como viñetas y filas de colores si no están completos los datos del detalle.

Finalmente, se graba la orden usando una cadena que viaja hasta el Procedimiento Almacenado de SQL Server usando un solo comando y una sola conexión. Además se puede imprimir la orden creada usando JavaScript para crear el HTML respectivo.


9. Curso MVC & JavaScript HTE - Demo 14 (Parte 3)

En este video se muestra la tercera parte de cómo crear una cabecera detalle para una orden usando una sola página, ya que se usa solo 2 divs: uno para la parte principal y otro para las búsquedas.

Los datos de los clientes, empleados y productos para las búsquedas se cargan desde un inicio y se presentan en el mismo popup (div) usando la misma funcionalidad para los 3: filtro, ordenación y paginación, permitiendo seleccionar dando un clic.

También se realiza la validación de la cabecera y los detalles usando JavaScript y mostrando los resultados en un span como viñetas y filas de colores si no están completos los datos del detalle.

Finalmente, se graba la orden usando una cadena que viaja hasta el Procedimiento Almacenado de SQL Server usando un solo comando y una sola conexión. Además se puede imprimir la orden creada usando JavaScript para crear el HTML respectivo.


miércoles, 3 de febrero de 2016

Entrenamiento - Vacantes Taller ASP.NET MVC & JavaScript Domingos

Vacantes Taller ASP.NET MVC & JavaScript Domingos

A los interesados en participar en el Taller de ASP.NET MVC & JavaScript, les comunico que pueden incorporarse para el inicio del tercer mes (Domingo 7 de Febrero de 8:00 am a 2:00 pm)

Los temas que ya se trataron fueron:
- Primer mes: Programando en el servidor web con C# (Creación de Librerías Genéricas y Serializadores)
- Segundo mes: Programando con JavaScript en el Cliente (Listar, Filtrar, Ordenar, Paginar, Exportar)

Los temas a tratar en este mes serán:
- Tercer mes: Mantenimiento, Cabecera Detalle, Reportes, Gráficos, Canvas, SVG, WebWorkers

La inversión del curso es de 200 soles y se paga en efectivo el primer día de clases y para participar hay que enviar un mail de confirmación a Luis.duenash@gmail.com indicando su nombre completo y se le enviará la dirección donde se realiza el curso que es en el cono norte.

Hay muchos que están en duda y quieren participar pero se les complica el horario o dicen que esta muy lejos, por lo cual esperan otra oportunidad. Solo les recuerdo que ya no habrá mas oportunidades porque ya no pienso seguir dictando talleres y las librerías de código y los ejemplos solo se las comparto a los alumnos de los talleres.

Los que han sido mis ex-alumnos y no se han enterado, ya no existe ISILTECH y la única forma de llevar un curso conmigo es participando en los Talleres de los Domingos. Finalmente, los temas que se ven son diferentes a los que dictaba hace mas de un año, todo es de lado del cliente (super veloz), es decir, mas del 80% de código es JavaScript y solo un 20% es .NET (C#).

jueves, 28 de enero de 2016

Videos Curso ASP.NET MVC & JavaScript Desysweb (Parte 1)

Videos del Curso ASP.NET MVC & JavaScript Desysweb (Parte 1)

Comparto la primera parte de otro curso de ASP.NET MVC & JavaScript realizado en la Empresa Desysweb donde todo se trabaja en forma desconectada usando en el Servidor mi Serializador de Cadenas y JavaScript en el Cliente, sobre todo Matrices.

1. Curso MVC & JavaScript Desysweb - Demo 01

En este video se muestra como trabajar con Listas de 2 formas:
- Consulta Síncrona usando el control MVC WebGrid que envia HTML
- Consulta Asíncrona usando XHR y enviando String que son usados para crear una Tabla por JavaScript en el Cliente.

Esta segunda forma es mas eficiente ya que solo se envía los datos (cadenas separadas por un caracter) y  en el cliente se construye el HTML usando JavaScript.


2. Curso MVC & JavaScript Desysweb - Demo 02

En este video se muestra como crear una Consulta Asíncrona usando XmlHttpRequest (XHR) enviando Cadenas (String) mediante un serializador personalizado y creando una tabla HTML en JavaScript.

Para el ejemplo se trabaja con una lista de Productos: Código, Nombre, Id del Proveedor, Nombre del Proveedor, Id de la Categoría, Nombre de la Categoría, Precio Unitario y Stock.


3. Curso MVC & JavaScript Desysweb - Demo 03

En este video se muestra como crear un filtro desconectado mediante un ComboBox y usando solo Cadenas en JavaScript.

Para el ejemplo se muestra en un ComboBox las Categorías y al seleccionar una se muestran los Productos por Categoría, pero todo es en el lado del cliente usando JavaScript.


4. Curso MVC & JavaScript Desysweb - Demo 04

En este video se enseña como crear un filtro desconectado mediante las cabeceras de la tabla usando controles de texto para filtrar cada campo, el filtro se hace solo usando Cadenas.

El ejemplo es con la tabla productos y se puede filtrar por código, nombre, proveedor, categoría, precio y stock mediante JavaScript.


5. Curso MVC & JavaScript Desysweb - Demo 05

En este video se enseña como crear un filtro desconectado mediante las cabeceras de la tabla usando controles de textos y combos para filtrar cada campo.

El ejemplo es con la tabla productos y se puede filtrar por código, nombre, proveedor, categoría, precio y stock mediante JavaScript.


6. Curso MVC & JavaScript Desysweb - Demo 06

En este video se muestra como trabajar con matrices y ocultar columnas en la presentación, para lo cual se listan los datos de los productos sin incluir el código del proveedor ni el código de la categoría.

Al seleccionar un producto se muestra un popup con su detalle donde si se muestran todos los datos, incluyendo los ocultos, el cual se realiza usando una búsqueda en la matriz.

Cabe resaltar que en este ejemplo no se usan dos páginas sino dos divs, uno para la lista y el otro para el detalle, lo que hace más rápido la visualización de los datos.


7. Curso MVC & JavaScript Desysweb - Demo 07

En este video se muestra como ordenar columnas en forma ascendente y descendente usando matrices en JavaScript.

Para el ejemplo se crean enlaces en la cabecera de la tabla, los cuales permiten ordenar sobre cada campo de la tabla productos, el primer clic ordena en forma ascendente y el segundo ordena en forma descendente mostrándose el símbolo respectivo.


8. Curso MVC & JavaScript Desysweb - Demo 08

En este video se muestra como paginar registros usando matrices en el cliente usando JavaScript. La paginación consta de enlaces (links) con los números de páginas y a los extremos los botones de avance por bloques, en el caso de que sean demasiadas páginas.

En este ejemplo se usan enlaces (a hrefs) para la paginación los cuales se encuentran sobre una lista de viñetas que se visualizan como botones gracias a la hija de estilos.


9. Curso MVC & JavaScript Desysweb - Demo 09

En este video se muestra como hacer las 3 operaciones en forma conjunta, es decir, paginar registros, ordenar columnas y filtrar registros en el cliente (desconectado) usando matrices en JavaScript.

El ejemplo es con la lista de productos y los datos están paginados y por bloques, también las columnas se pueden ordenar en forma ascendente y descendente y finalmente se puede filtrar por cada campo escribiendo sobre los textos de las cabeceras en cada columna de la tabla.


lunes, 23 de marzo de 2015

Videos - Curso de ASP.NET MVC Domingos (Parte 3)

Videos del Curso de ASP.NET MVC Domingos (Parte 3)

Comparto la tercera parte del Curso de ASP.NET MVC realizado los Domingos.

Curso de MVC Domingos - Demo 11 - Consulta Asíncrona en el Servidor con jQuery Ajax y JSON

En este video se muestra como realizar una consulta asíncrona de productos por categoría usando jQuery Ajax y devolviendo una lista de objetos como JSON para escribir el HTML en el cliente por Javascript.


Curso de MVC Domingos - Demo 12 - Consulta Asíncrona en el Servidor con XmlHttpRequest y Strings

En este video aprenderemos como realizar una consulta asíncrona de productos por categoría usando XmlHttpRequest (lo que usa internamente jQuery y Ajax) y devolviendo una cadena (String) para reducir el ancho de banda ya que es la octava parte de una vista parcial y la tercera parte de un JSON.


Curso de MVC Domingos - Demo 13 - Consulta Desconectada en el Cliente con jQuery Ajax y JSON

Este video muestra como realizar una consulta de productos por categoría totalmente desconectada del servidor web y de base de datos, recibiendo la primera vez todos los datos como JSON usando jQuery Ajax y filtrándolos en el cliente por JavaScript.


Curso de MVC Domingos - Demo 14 - Ordenación Desconectada en el Cliente con jQuery Ajax y JSON

En este video se muestra como ordenar los productos en el lado del cliente, totalmente desconectado del servidor web y de datos, para lo cual inicialmente hacemos una llamada asíncrona con jQuery Ajax y devolvemos un JSON con los productos los cuales se ordenan usando una función genérica de JavaScript.



miércoles, 21 de enero de 2015

Entrenamiento - Cuarto Seminario de .NET (Creación de Controles en ASP.NET)

Cuarto Seminario de .NET: Creación de Librerías de Controles en ASP.NET

Un saludo cordial a todos los visitantes del Blog y anunciar que este Miércoles 28 de Enero tendremos el cuarto y último seminario de .NET, que en esta ocasión tratará sobre como crear Librerías de Controles en ASP.NET, tanto en WebForms como en MVC.

Muchos desarrolladores de WebForms solo crean Controles de Usuario (User Controls) que solo son reusables dentro de una aplicación web, pero si lo quieren usar en otras aplicaciones tienen que copiar el código fuente y eso no es reusabilidad, lo correcto es crear su Librería de Controles Web que contengan clases WebControls o CompositeControls.

Lo mismo sucede en MVC, si queremos trabajar con grillas, gráficos, reportes, Microsoft solo ofrece los controles HTML Helpers que son limitados, por lo cual, muchos programadores recurren a librerías de terceros como jQueryUI, KendoUI, Infragistics, DevExpress, etc. También veremos como podemos aumentar controles a los HTML Helpers de MVC.

Les dejo la propaganda a los interesados:



También publico una foto de la tercera charla sobre Reflection y Creación de Controles WinForms.



No se olviden confirmar su asistencia mediante un mail a Luis.duenash@gmail.com e informes@hte.com.pe.

Los dejo con una frase para que reflexionen: "La grandeza de un país se ve por la cantidad de gente culta que en el habita". Si queremos ser un país desarrollado o del primer mundo, necesitamos invertir mas tiempo en nuestra profesión y cultura; no solo hay que ir a trabajar y cumplir con lo que uno puede, sino, hay que hacer el mejor trabajo (como nadie lo haría) y para eso hay que invertir en nuestro conocimiento.

jueves, 15 de enero de 2015

Entrenamiento - Cuarto y Ultimo Seminario .NET (Creación de Controles en ASP.NET)

Cuarto y Ultimo Seminario .NET: Creación de Controles en ASP.NET (Web Forms y MVC)

Quiero invitar a los interesados al ultimo seminario que tratara sobre "Creación de Controles en ASP.NET", en donde se verá como crear librerías de Controles Web Forms y MVC, habra ejemplos sobre controles Grillas Paginadas, Ordenadas, con Filtros en cabeceras, Exportaciones de Datos, etc.
También crearemos controles de gráficos, de búsqueda y el control ubigeo que por falta de tiempo no se pudo hacer ayer.

Gracias a todos los que asistieron a los seminarios anteriores, sobre a todo los que me vienen acompañando hace muchos años y a los que hicieron el esfuerzo por estar en los 3 seminarios anteriores, con ellos seguiremos trabajando, como lo hacíamos antes.

La idea era compartir con mas personas temas avanzados y temas del futuro en desarrollo de software, tales como "El Internet de las Cosas", "Impresion 3D" y sus implementaciones en "HTML5" y "JavaScripts APIs".

Sobre HTML5 se verían temas como:
- Formularios y Nuevos Controles
- Canvas
- SVG
- Audio y Video

Sobre JavaScripts APIs se verían temas como:
- Web Workers
- Geolocation
- FileStorage (SessionStorage, LocalStorage e IndexedDB)
- Web Audio API
- File System API
- WebGL
- WebSockets
- WebRTC
- Speech API, etc

Pero parece que muchos desean aprender temas actuales para su trabajo, es decir, como Crear Aplicaciones Web, Reportes, Impresiones, etc, temas que se ven en cualquier curso de capacitación. La idea era no repetir con los mismos temas sino prepararnos para el futuro. Pero estamos en un país donde la gente solo ve el día a día y no prevee el mañana.

Particularmente, seguiré trabajando en estos temas y este año lanzare varias librerías y generadores como les había comentado en las charlas. En realidad mi objetivo con el Blog y los Seminarios era no avanzar solo sino que muchas personas se sumaran a esto, pero cada uno establece en su vida prioridades y algunos no toman en serio la necesidad de mejorar cada día ni "entender como funcionan la cosas".

Saludos a todos y que la suerte los acompañe (la van a necesitar)

PD: Los que asistieron al seminario de ayer, hoy les enviare un mail con todos los Demos + Practicas que hice y también estaré publicando en el Blog un ejemplo de un Control Combo con Múltiples Columnas e Imágenes.

lunes, 5 de enero de 2015

Entrenamiento - Talleres de .NET HTE para Enero

Talleres de .NET HTE para Enero

En este mes de Enero tenemos programado 2 Talleres de .NET sobre Creación de Controles.

A todos los programadores y desarrolladores de .NET que quieran crear Librerías de Controles para facilitar y agilizar el desarrollo mediante la reusabilidad se les recomienda asistir a ambas charlas donde usaremos intensivamente Reflection para crear Controles Enlazados a Datos.

Aquí les dejo la información del evento y ya se pueden ir inscribiendo de cualquiera de las 2 formas:
- Reservación por Correo
- Pago y Confirmación por Correo (envío del voucher)

El correo sigue siendo Luis.duenash@gmail.com e informes@hte.com.pe

La inversión por cada charla de 3 horas es de 40 soles y el que desea Certificado serán 20 soles adicionales.

Nota: El Certificado es opcional, solo para los que lo soliciten.

El horario es de 7:00 a 10:00 pm, pero el ingreso para el registro es a partir de las 6:30 pm.




Los esperamos.

viernes, 21 de noviembre de 2014

El Demo del Día: Filtrar y Ordenar en Cabeceras del DataGridView en WinForms

Filtrar y Ordenar en Cabeceras del DataGridView en WinForms

En este post aprenderemos como cambiar las cabeceras del control DataGridView de Windows Forms para incluir TextBoxs que permitan filtrar registros, así como etiquetas que permitan ordenar ascendente y descendentemente los datos de cada columna.

De repente muchos preguntaran porque tantos demos con Grillas y Cabeceras sobre estas, tanto en Windows y Web Forms y la respuesta es que este es el control mas usado y estas funcionalidades son muy usadas pero no es tan simple como configurar una propiedad y muchos eligen usar un control de terceros, pero con la ayuda de estos post aprenderás tu mismo a crearlos.

Requerimiento

Se necesita crear una consulta en Windows con los siguientes requerimientos:

- La consulta debe ser desconectada y no ir a a cada momento al servidor de datos.

- Los filtros deben ser por todos los campos mostrados y deben ocupar poco espacio.

- Se debe poder ordenar cada columna en forma ascendente y descendente.

- Se debe mostrar el símbolo de ordenación: "▲" para ascendente y "▼" para descendente.

Solución

- Usaremos el control DataGridView y crearemos columnas personalizadas.

- Crearemos una clase DataGridViewTextBoxHeaderColumn para cada columna de tipo TextBox.

- Crearemos una clase DataGridViewTextBoxHeaderCell para la cabecera de cada columna, la cual contendrá un LinkLabel para ordenar y un TextBox para filtrar.

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

Create Procedure uspProductsListar
As
Select ProductID,ProductName,SupplierID,CategoryID,UnitPrice,UnitsInStock
From Products

Crear una Aplicación Windows Forms en C#

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

Arrastrar un DataGridView llamado "dgvProducto" y acoplarlo en todo el formulario, el diseño se mostrará similar a la siguiente figura:


Crear la Clase Entidad del Negocio

Crear la clase beProducto escribiendo el siguiente código:

using System;
namespace DataGridView_FiltroOrdenCabecera
{
    public class beProducto
    {
        public beProducto()
        {
            IdProducto = -1;
            IdProveedor = -1;
            IdCategoria = -1;
            PrecioUnitario = -1;
            Stock = -1;
        }
        public int IdProducto { get; set; }
        public string Nombre { get; set; }
        public int IdProveedor { get; set; }
        public int IdCategoria { get; set; }
        public decimal PrecioUnitario { get; set; }
        public short Stock { get; set; }
    }
}

Nota: Se ha creado un constructor para iniciar las propiedades numéricas en -1 y asi poder permitir la búsqueda del cero (0) en los filtros.

Crear la Clase de Acceso a Datos

Crear la clase daProducto escribiendo el siguiente código:

using System;
using System.Data; //CommadType
using System.Data.SqlClient; //SqlConnection, SqlCommand, SqlDataReader
using System.Collections.Generic; //List
namespace DataGridView_FiltroOrdenCabecera
{
    public class daProducto
    {
        public List<beProducto> listar(SqlConnection con)
        {
            List<beProducto> lbeProducto = null;
            SqlCommand cmd = new SqlCommand("uspProductsListar", con);
            cmd.CommandType = CommandType.StoredProcedure;
            SqlDataReader drd = cmd.ExecuteReader(CommandBehavior.SingleResult);
            if (drd != null)
            {
                lbeProducto = new List<beProducto>();
                int posIdProducto = drd.GetOrdinal("ProductID");
                int posNombre = drd.GetOrdinal("ProductName");
                int posIdProveedor = drd.GetOrdinal("SupplierID");
                int posIdCategoria = drd.GetOrdinal("CategoryID");
                int posPrecioUnitario = drd.GetOrdinal("UnitPrice");
                int posStock = drd.GetOrdinal("UnitsInStock");
                beProducto obeProducto;
                while (drd.Read())
                {
                    obeProducto = new beProducto();
                    obeProducto.IdProducto = drd.GetInt32(posIdProducto);
                    obeProducto.Nombre = drd.GetString(posNombre);
                    obeProducto.IdProveedor = drd.GetInt32(posIdProveedor);
                    obeProducto.IdCategoria = drd.GetInt32(posIdCategoria);
                    obeProducto.PrecioUnitario = drd.GetDecimal(posPrecioUnitario);
                    obeProducto.Stock = drd.GetInt16(posStock);
                    lbeProducto.Add(obeProducto);
                }
                drd.Close();
            }
            return (lbeProducto);
        }
    }
}

Nota: Tiene que haberse creado el Procedimiento Almacenado "uspProductsListar" 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; //List
using System.Data.SqlClient; //SqlConnection
using System.Configuration; //ConfigurationManager
namespace DataGridView_FiltroOrdenCabecera
{
    public class brProducto
    {
        public List<beProducto> listar()
        {
            List<beProducto> lbeProducto = null;
            string conexion = ConfigurationManager.ConnectionStrings["conNW"].ConnectionString;
            using (SqlConnection con = new SqlConnection(conexion))
            {
                try
                {
                    con.Open();
                    daProducto odaProducto = new daProducto();
                    lbeProducto = odaProducto.listar(con);
                }
                catch (SqlException ex)
                {                
                    foreach (SqlError err in ex.Errors)
                    {
                        //Capturar cada error y grabar un Log
                    }
                }
            } //con.Close(); con.Dispose(); con = null;
            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 las Cabeceras de las Columnas del DataGridView

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

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

namespace DataGridView_FiltroOrdenCabecera
{
    public class DataGridViewTextBoxHeaderCell : DataGridViewColumnHeaderCell
    {
        public TextBox txt;
        public LinkLabel lbl;
        public event EventHandler OrdenaCampo;
        public event EventHandler CambiaTexto;

        public DataGridViewTextBoxHeaderCell()
        {
            lbl = new LinkLabel();
            lbl.Click += new EventHandler(ordenaCampo);
            txt = new TextBox();          
            txt.TextChanged += new EventHandler(cambiaTexto);
        }

        protected override void Paint(Graphics graphics, Rectangle clipBounds,Rectangle cellBounds,
            int rowIndex, DataGridViewElementStates dataGridViewElementState,
            object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle,
            DataGridViewAdvancedBorderStyle advancedBorderStyle,
            DataGridViewPaintParts paintParts)
        {
            base.Paint(graphics, clipBounds, cellBounds, rowIndex, dataGridViewElementState,
            value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
            lbl.Location = new Point(cellBounds.X + 2, cellBounds.Y+5);
            lbl.Size = new Size(cellBounds.Size.Width - 4, 13);
            lbl.BackColor = Color.Transparent;
            lbl.Font=new Font("Arial",8,FontStyle.Bold);
            lbl.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
            txt.Location = new Point(cellBounds.X + 2,cellBounds.Y + 25);
            txt.Size = new Size(cellBounds.Size.Width-4,20);
        }

        public string ValorEtiqueta
        {
            get
            {
                return (lbl.Text);
            }
            set
            {
                lbl.Text = value;
            }
        }

        private void ordenaCampo(object sender, EventArgs e)
        {
            if (OrdenaCampo != null)
            {
                OrdenaCampo(this, e);
            }
        }

        public string ValorTexto
        {
            get
            {
                return (txt.Text);
            }
            set
            {
                txt.Text = value;
            }
        }

        private void cambiaTexto(object sender, EventArgs e)
        {
            if (CambiaTexto != null)
            {
                CambiaTexto(this, e);
            }
        }
    }
}

Nota: La clase creada hereda de "DataGridViewColumnHeaderCell" y crea 2 controles: un LinkLabel "lbl" para ordenar y un TextBox "txt" para filtrar. Al "lbl" se le crea el evento "OrdenaCampo" y al "txt" se le crea el evento "CambiaTexto" para filtrar.
Además se crean 2 propiedades para exponer los valores de las etiquetas de las cabeceras: "ValorEtiqueta" y el valor de los textos: "ValorTexto".

Crear una Clase para las Columnas del DataGridView

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

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

namespace DataGridView_FiltroOrdenCabecera
{
    public class DataGridViewTextBoxHeaderColumn: DataGridViewTextBoxColumn
    {
        DataGridViewTextBoxHeaderCell textoCabecera = new DataGridViewTextBoxHeaderCell();      
        public DataGridViewTextBoxHeaderColumn()
        {
            this.CellTemplate = new DataGridViewTextBoxCell();
            this.HeaderCell = textoCabecera;          
        }

         protected override void OnDataGridViewChanged()
        {
            if (this.DataGridView != null)
            {
                this.DataGridView.Controls.Add(textoCabecera.lbl);
                this.DataGridView.Controls.Add(textoCabecera.txt);
            }
        }

        public DataGridViewTextBoxHeaderCell Texto
        {
            get
            {
                return this.textoCabecera;
            }
        }
    }
}

Nota: La clase creada hereda de "DataGridViewTextBoxColumn" y crea un objeto "textoCabecera" de tipo "DataGridViewTextBoxHeaderCell" que se configura como propiedad "HeaderCell" de la columna. Este se expone como una propiedad llamada "Texto".
Además en el evento "OnDataGridViewChanged" se agrega los controles creados en la cabecera de la celda: el LinkLabel "lbl" y el TextBox "txt".

Usar las Clases creadas en el DataGridView del Formulario

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

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

namespace DataGridView_FiltroOrdenCabecera
{
    public partial class frmConsultaProductos : Form
    {
        private List<beProducto> lbeProducto;
        private List<beProducto> lbeFiltro;
        DataGridViewTextBoxHeaderColumn col1,col2,col3,col4,col5,col6;

        public frmConsultaProductos()
        {
            InitializeComponent();
        }

        private void cargarProductos(object sender, EventArgs e)
        {
            brProducto obrProducto = new brProducto();
            lbeProducto = obrProducto.listar();
            dgvProducto.AutoGenerateColumns = false;
            dgvProducto.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
            dgvProducto.ColumnHeadersHeight = 50;
            dgvProducto.ColumnHeadersHeightSizeMode =
            DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
            lbeFiltro = lbeProducto;
            dgvProducto.DataSource = lbeFiltro;
            crearColumnasCabecera();
        }

        private void crearColumnasCabecera()
        {
            col1 = new DataGridViewTextBoxHeaderColumn();
            col1.DataPropertyName = "IdProducto";          
            col1.Width = 70;
            col1.Texto.ValorEtiqueta = "Código";
            col1.Texto.OrdenaCampo += new EventHandler(ordenarProductos);
            col1.Texto.CambiaTexto += new EventHandler(filtrarProductos);
            dgvProducto.Columns.Add(col1);
         
            col2 = new DataGridViewTextBoxHeaderColumn();
            col2.DataPropertyName = "Nombre";
            col2.Width = 300;
            col2.Texto.ValorEtiqueta = "Descripción del Producto";
            col2.Texto.OrdenaCampo += new EventHandler(ordenarProductos);
            col2.Texto.CambiaTexto += new EventHandler(filtrarProductos);
            dgvProducto.Columns.Add(col2);
         
            col3 = new DataGridViewTextBoxHeaderColumn();
            col3.DataPropertyName = "IdProveedor";
            col3.Width = 70;
            col3.Texto.ValorEtiqueta = "Id Prov";
            col3.Texto.OrdenaCampo += new EventHandler(ordenarProductos);
            col3.Texto.CambiaTexto += new EventHandler(filtrarProductos);
            dgvProducto.Columns.Add(col3);
         
            col4 = new DataGridViewTextBoxHeaderColumn();
            col4.DataPropertyName = "IdCategoria";
            col4.Width = 70;
            col4.Texto.ValorEtiqueta = "Id Cat";
            col4.Texto.OrdenaCampo += new EventHandler(ordenarProductos);
            col4.Texto.CambiaTexto += new EventHandler(filtrarProductos);
            dgvProducto.Columns.Add(col4);
         
            col5 = new DataGridViewTextBoxHeaderColumn();
            col5.DataPropertyName = "PrecioUnitario";
            col5.Width = 70;
            col5.Texto.ValorEtiqueta = "Pre Unit";
            col5.Texto.OrdenaCampo += new EventHandler(ordenarProductos);
            col5.Texto.CambiaTexto += new EventHandler(filtrarProductos);
            dgvProducto.Columns.Add(col5);
         
            col6 = new DataGridViewTextBoxHeaderColumn();
            col6.DataPropertyName = "Stock";
            col6.Width = 70;
            col6.Texto.ValorEtiqueta = "Stock";
            col6.Texto.OrdenaCampo += new EventHandler(ordenarProductos);
            col6.Texto.CambiaTexto += new EventHandler(filtrarProductos);
            dgvProducto.Columns.Add(col6);
        }

        private bool buscarProductos(beProducto obeProducto)
        {
            bool exitoIdProducto = true;
            bool exitoNombre = true;
            bool exitoIdProveedor = true;
            bool exitoIdCategoria = true;
            bool exitoPrecioUnitario = true;
            bool exitoStock = true;
            if (!col1.Texto.Equals("")) exitoIdProducto =
            (obeProducto.IdProducto.ToString().Contains(col1.Texto.ValorTexto));
            if (!col2.Texto.Equals("")) exitoNombre =
            (obeProducto.Nombre.ToLower().Contains(col2.Texto.ValorTexto.ToLower()));
            if (!col3.Texto.Equals("")) exitoIdProveedor =
            (obeProducto.IdProveedor.ToString().Contains(col3.Texto.ValorTexto));
            if (!col4.Texto.Equals("")) exitoIdCategoria =
            (obeProducto.IdCategoria.ToString().Contains(col4.Texto.ValorTexto));
            if (!col5.Texto.Equals("")) exitoPrecioUnitario =
            (obeProducto.PrecioUnitario.ToString().Contains(col5.Texto.ValorTexto));
            if (!col6.Texto.Equals("")) exitoStock =
            (obeProducto.Stock.ToString().Contains(col6.Texto.ValorTexto));
            return (exitoIdProducto && exitoNombre && exitoIdProveedor && exitoIdCategoria
            && exitoPrecioUnitario && exitoStock);
        }

        protected void filtrarProductos(object sender, EventArgs e)
        {
            Predicate<beProducto> pred = new Predicate<beProducto>(buscarProductos);
            lbeFiltro = lbeProducto.FindAll(pred);
            dgvProducto.DataSource = lbeFiltro;
        }

        protected void ordenarProductos(object sender, EventArgs e)
        {
            DataGridViewTextBoxHeaderCell cabecera = (DataGridViewTextBoxHeaderCell)sender;
            DataGridViewTextBoxHeaderColumn columna =
            (DataGridViewTextBoxHeaderColumn )cabecera.OwningColumn;
            string campo = columna.DataPropertyName;
            string etiqueta = cabecera.ValorEtiqueta.Replace("▲","").Replace("▼","").Trim();
            int n = 0;
            string simbolo = "▲";
            if (columna.Tag != null)
            {
                if (columna.Tag.Equals(0))
                {
                    simbolo = "▼";
                    n = 1;
                }
            }
            columna.Tag = n;
            cabecera.ValorEtiqueta = String.Format("{0} {1}", etiqueta, simbolo);
            if(n.Equals(0)) lbeFiltro = lbeFiltro.OrderBy(x=>x.GetType().
                 GetProperty(campo).GetValue(x,null)).ToList();
            else lbeFiltro = lbeFiltro.OrderByDescending(x => x.GetType().
                 GetProperty(campo).GetValue(x, null)).ToList();
            dgvProducto.DataSource = lbeFiltro;
        }
    }
}

Nota: En el evento "load" del formulario se llama a la función controladora "cargarProductos" que obtiene los datos y configura el DataGridView "dgvProducto" sobre todo creando las columnas personalizadas en la función "crearColumnasCabecera".

En el evento "OrdenaCampo" de la cabecera de cada columna se asocia la función "ordenarProductos" y en el evento "CambiaTexto" se asocia a la función "filtrarProductos".

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:


En las cabeceras de cada columna aparecen unos LinkLabels y TextBoxs, probamos la ordenación dando clic a los LinkLabel y la primera vez se ordenará en forma ascendente y la siguiente en forma descendente apareciendo su respectivo símbolo, tal como se muestra en la siguiente figura:


Escribir en la cabecera del código el "1" y en la del nombre "ma" y cada vez que se escribe se realiza el filtro desconectado, tal como se muestra a continuación:


Comentario Final

En este post hemos aprendido como sobre escribir las clases "DataGridViewColumnHeaderCell" y "DataGridViewTextBoxColumn" para crear columnas con cabeceras personalizadas que contengan controles: LinkLabels para ordenar y TextBox para filtrar.

En Windows Forms crear cabeceras en el DataGridView es un poco mas trabajoso que en ASP.NET WebForms y MVC que usan plantillas y que en WPF que usa estilos y es mas fácil personalizar.

Pero de todas formas no es imposible y lo mejor es que puedes hacer los cambios que deseas ya que tienes el fuente del código. Espero les sirva a todos los visitantes del Blog.

Creo que después de este buen Demo algunos se animarán a ir al Seminario de Buenas Prácticas en .NET, sobre todo los que nunca han llevado cursos conmigo.

Descarga
Demo26_DataGridView_FiltroOrdenCabecera

jueves, 23 de octubre de 2014

El Libro del Día: La Biblia de Visual Basic .NET (Cap 3)

El Libro del Día: 2014-10-23

Titulo: La Biblia de Visual Basic .NET (Capítulo 3)
Autor: Luis Dueñas Huaroto
Editorial: Coredise
Nro Paginas: 152

Capítulo 3: Accediendo a Datos con ADO .NET
1. Trabajando en Forma Conectada
1.1. Introducción al Acceso a Datos con ADO .NET
1.2. Conectarse a un Origen de Datos
1.3. Ejecutando Comandos de Selección
1.4. Realizando un Mantenimiento Conectado
2. Trabajando en Forma Desconectada con DataSet
2.1. Trabajando con Tablas y Relaciones
2.2. Trabajando con Vistas
2.3. Mantenimiento Desconectado con DataSet
3. Trabajando en Forma Desconectada con Listas de Objetos
3.1. Llenando una Lista de Objetos
3.2. Filtrando Datos en una Lista de Objetos
3.3. Ordenando y Buscando datos en una Lista de Objetos
3.4. Mantenimiento con Objetos
4. LINQ
4.1. LINQ a DataSet
4.2. LINQ a SQL
4.3. LINQ a Entidades

Descarga:
Biblia_VisualBasicNET_Capitulo3

miércoles, 6 de agosto de 2014

El Demo del Día: Paginar, Ordenar y Filtrar en el Control Web DataList

Paginar, Ordenar y Filtrar en el Control Web DataList

Requerimientos

Se desea crear una Aplicación Web que permita listar los datos de los empleados que cumpla con lo siguiente:
- Presente en forma de ficha la información del código, apellido, nombre y fecha de nacimiento del empleado.
- Muestre en la ficha la foto del empleado la cual se encuentra en un carpeta "Empleados" donde el nombre del archivo esta compuesto por el código del empleado y la extensión jpg. Para los archivos que no se encuentren se usará el archivo No.jpg.
- Las fichas deben tener fondo gris claro, pero al pasar el mouse el estilo de la ficha cambiara a fondo amarillo.
- La información debe estar paginada (por defecto mostrar 6 registros de empleados), mediante botones de desplazamiento y enlaces con números de paginas.
- Se debe permitir ordenar los registros por cualquier campo en forma ascendente y descendente.
- Se debe permitir filtrar los registros por cualquier campo ingresando el dato buscado.

Solución

Usaremos el control DataList con Plantilla de Datos para poder presentar en forma de ficha la información del empleado y mostrarla en varias columnas. Ademas agregaremos botones y enlaces para paginar, combos para seleccionar los criterios de ordenación y filtro de registros.

Nota: En este caso si podemos deshabilitar el ViewState del DataList ya que no hay ningun control dentro de la plantilla que haga PostBack, es decir enviaremos el minimo HTML al navegador en el cliente.

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

Usaremos la tabla Empleados (Employees) de Northwind:

Create Procedure [dbo].[uspEmployeesListar]
As
Select EmployeeID,LastName,FirstName,
IsNull(BirthDate,'1/1/1900') As BirthDate
From Employees Order By 1

Crear una Librería de Clases con las Entidades del Negocio

Crear un proyecto de tipo Librería de Clases en C# llamado: "Northwind.Librerias.EntidadesNegocio" y modificar el código de la clase como sigue:

using System;
namespace Northwind.Librerias.EntidadesNegocio
{
    public class beEmpleado
    {
        public int IdEmpleado { get; set; }
        public string Apellido { get; set; }
        public string Nombre { get; set; }
        public string NombreCompleto
        {
            get
            {
                return (String.Format("{0} {1}",Nombre,Apellido));
            }
        }
        public DateTime FechaNacimiento { get; set; }
    }
}

Crear una Librería de Acceso a Datos

Crear un proyecto de tipo Librería de Clases en C# llamado: "Northwind.Librerias.AccesoDatos" y modificar el código de la clase como sigue:

using System;
using System.Data; //CommandType
using System.Data.SqlClient; //SqlConnection, SqlCommand, SqlDataReader
using System.Collections.Generic; //List
using Northwind.Librerias.EntidadesNegocio; //beEmpleado
namespace Northwind.Librerias.AccesoDatos
{
    public class daEmpleado
    {
        public List<beEmpleado> listar(SqlConnection con)
        {
            List<beEmpleado> lbeEmpleado = null;
            SqlCommand cmd = new SqlCommand("uspEmployeesListar", con);
            cmd.CommandType = CommandType.StoredProcedure;
            SqlDataReader drd = cmd.ExecuteReader(CommandBehavior.SingleResult);
            if (drd != null)
            {
                lbeEmpleado = new List<beEmpleado>();
                int posIdEmpleado = drd.GetOrdinal("EmployeeID");
                int posApellido = drd.GetOrdinal("LastName");
                int posNombre = drd.GetOrdinal("FirstName");
                int posFechaNacimiento = drd.GetOrdinal("BirthDate");
                beEmpleado obeEmpleado;
                while (drd.Read())
                {
                    obeEmpleado = new beEmpleado();
                    obeEmpleado.IdEmpleado = drd.GetInt32(posIdEmpleado);
                    obeEmpleado.Apellido = drd.GetString(posApellido);
                    obeEmpleado.Nombre = drd.GetString(posNombre);
                    obeEmpleado.FechaNacimiento = drd.GetDateTime(posFechaNacimiento);
                    lbeEmpleado.Add(obeEmpleado);
                }
                drd.Close();
            }
            return (lbeEmpleado);
        }
    }
}

Nota: Hay que hacer una referencia a la Librería de Entidades del Negocio.

Crear una Librería de Reglas del Negocio

Crear un proyecto de tipo Librería de Clases en C# llamado: "Northwind.Librerias.ReglasNegocio" y modificar el código de la clase como sigue:

using System;
using System.Configuration; //ConfigurationManager
namespace Northwind.Librerias.ReglasNegocio
{
    public class brGeneral
    {
        //Propiedad
        public string Conexion { get; set; }

        //Constructor
        public brGeneral()
        {
            Conexion = ConfigurationManager.ConnectionStrings["conNW"].ConnectionString;
        }
    }
}

Nota: Hay que hacer una referencia a la Librería de Entidades del Negocio, a la Librería de Acceso a Datos y a System.Configuration.

Agregar otra clase llamada: "brEmpleado" y escribir el siguiente código:

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using Northwind.Librerias.EntidadesNegocio;
using Northwind.Librerias.AccesoDatos;

namespace Northwind.Librerias.ReglasNegocio
{
    public class brEmpleado:brGeneral //Herencia
    {
        public List<beEmpleado> listar()
        {
            List<beEmpleado> lbeEmpleado = null;
            using (SqlConnection con = new SqlConnection(Conexion))
            {
                try
                {
                    con.Open();
                    daEmpleado odaEmpleado = new daEmpleado();
                    lbeEmpleado = odaEmpleado.listar(con);
                }
                catch (SqlException ex)
                {
                    //Capturar el error y grabar un Log
                }
            } //con.Close(); con.Dispose(); con = null;
            return (lbeEmpleado);
        }
    }
}

Nota: La clase "brEmpleado" hereda de la clase "brGeneral" la cadena de conexión para no pasarla como parámetro en cada método de la clase o en el constructor de cada clase se hace una sola vez.

Crear una Librería de Código de Usuario para Web

Crear un proyecto de tipo Librería de Clases en C# llamado: "General.Librerias.CodigoUsuarioWeb" y modificar el código de la clase como sigue:

using System;
using System.Collections.Generic;
using System.IO; //File
using System.Web; //HttpContext

namespace General.Librerias.CodigoUsuarioWeb
{
    public class Imagen
    {
        public static string obtenerUrl(int id, string carpeta)
        {
            string archivo = HttpContext.Current.Server.MapPath
            (String.Format("../Imagenes/{0}/{1}.jpg", carpeta, id));
            string url = String.Format("../Imagenes/{0}/{1}.jpg", carpeta, id);
            if (!File.Exists(archivo)) url = String.Format("../Imagenes/{0}/No.jpg", carpeta);
            return (url);
        }
    }
}

Nota: Como esta librería va a ser usada por paginas web hay que hacer referencia a: "System.Web"

Crear la Aplicación Web en ASP .NET Web Form

Crear un "Nuevo Sitio web vacío de ASP .NET" en C# llamado "Demo14" y crear las siguientes carpetas:
- Estilos: Para el archivo de hoja de estilo (CSS).
- Imagenes. Dentro crear otra carpeta llamada "Empleados" y adjuntar los archivos con las fotos de los empleados: 1.jpg, 2.jpg, 3.jpg, ... y No.jpg.
- Paginas: Para contener la pagina de lista de empleados.

Crear el Archivo de Hoja de Estilo (CSS)

Seleccionar la carpeta "Estilos" y agregar un archivo de hoja de estilos con el nombre de: "ACME.css" y modificar el código como sigue:

body {
    background-color:aqua;
}
.Titulo {
    background-color:black;
    color:white;
    text-transform:uppercase;
    font-size:xx-large;
    font-weight:bold;
}
.Subtitulo {
    background-color:white;
    color:blue;
    text-transform:capitalize;
    font-size:x-large;
    font-weight:bold;
}
.MarcarTabla {
    background-color: lightgray;
    cursor:default;
    border:solid;
    border-color:lightgray;
}
.MarcarTabla:hover {
    background-color: yellow;
    cursor:pointer;
    border:solid;
    border-color:black;
}

Crear la Pagina ASP .NET como un Formulario Web Form

Seleccionar la carpeta "Paginas" y agregar un Formulario Web Form llamado: "ListaEmpleados.aspx", para empezar a diseñar la pagina hay que hacer referencia a las Librerías de Negocio (que copia todas sus dependencias) y la Librería de Código de Usuario Web.

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ListaEmpleados.aspx.cs" Inherits="Paginas_ListaEmpleados" %>
<%@ Import Namespace="Northwind.Librerias.EntidadesNegocio" %>
<%@ Import Namespace="General.Librerias.CodigoUsuarioWeb" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
    <link href="../Estilos/ACME.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <table style="width:100%">
            <tr>
                <td class="Titulo" colspan="5">Paginar, Ordenar y Filtrar en el DataList</td>
            </tr>
            <tr>
                <td class="Subtitulo" colspan="5">Lista de Empleados</td>
            </tr>
            <tr>
                <td style="width:10%">Ordenar Por: </td>
                <td style="width:20%">
                    <asp:DropDownList ID="ddlCampo" runat="server" />
                </td>
                <td style="width:10%">Tipo Orden: </td>
                <td style="width:20%">
                    <asp:DropDownList ID="ddlOrden" runat="server">
                        <asp:ListItem Value="0">Ascendente</asp:ListItem>
                        <asp:ListItem Value="1">Descendente</asp:ListItem>
                    </asp:DropDownList>
                </td>
                <td style="width:40%">
                    <asp:Button ID="btnOrdenar" Text="Ordenar" 
                      OnClick="ordenarCampo" runat="server"/>
                </td>
            </tr>
            <tr>
                <td style="width:10%">Filtrar Por: </td>
                <td style="width:20%">
                    <asp:DropDownList ID="ddlCriterio" runat="server" AutoPostBack="True" 
                      OnSelectedIndexChanged="cambiarControlBusqueda" />
                </td>
                <td style="width:10%">Dato Filtrar: </td>
                <td style="width:20%">
                    <asp:TextBox ID="txtDato" Width="130" runat="server" />
                </td>
                <td style="width:40%">
                    <asp:Button ID="btnFiltrar" Text="Filtrar" OnClick="filtrarCriterio" runat="server"/>
                </td>
            </tr>
            <tr>
                <td colspan="5" class="Subtitulo">Registros encontrados: 
                    <asp:Label ID="lblRegistros" Text="0" runat="server"/>
                </td>
            </tr>
            <tr>
                <td colspan="5">
                    <asp:DataList ID="dlsEmpleado" runat="server" EnableViewState="False" 
                        RepeatColumns="3" CellPadding="15" CellSpacing="15"
                        RepeatDirection="Horizontal">
                        <ItemTemplate>
                            <table class="MarcarTabla">
                                <tr>
                                    <td>
                                        <table>
                                            <tr>
                                                <td style="width:80px">Código :</td>
                                                <td style="width:100px">
                                                    <%#((beEmpleado)Container.DataItem).IdEmpleado%>
                                                </td>
                                            </tr>
                                            <tr>
                                                <td style="width:80px">Apellido :</td>
                                                <td style="width:100px">
                                                    <%#((beEmpleado)Container.DataItem).Apellido%>
                                                </td>
                                            </tr>
                                            <tr>
                                                <td style="width:80px">Nombre :</td>
                                                <td style="width:100px">
                                                    <%#((beEmpleado)Container.DataItem).Nombre%>
                                                </td>
                                            </tr>
                                            <tr>
                                                <td style="width:80px">Fecha Nac :</td>
                                                <td style="width:100px">
                                                    <%#((beEmpleado)Container.DataItem).FechaNacimiento.
                                                       ToShortDateString()%>
                                                </td>
                                            </tr>
                                        </table>
                                    </td>
                                    <td>
                                        <img src='<%#Imagen.obtenerUrl(((beEmpleado)Container.DataItem).
                                          IdEmpleado,"Empleados")%>' width="120" height="80" alt="Foto" 
                                          title="<%#((beEmpleado)Container.DataItem).NombreCompleto%>"/>
                                    </td>
                                </tr>
                            </table>
                        </ItemTemplate>
                   </asp:DataList>
                </td>
            </tr>
            <tr>
                <td id="celdaBotones" style="text-align:center" colspan="5" runat="server">
                    <asp:Button ID="btnPrimero" Text="<<" Width="50" 
                      runat="server" OnClick="btnPrimero_Click" />
                    <asp:Button ID="btnAnterior" Text="<" Width="50" 
                      runat="server" OnClick="btnAnterior_Click" />
                    <asp:TextBox ID="txtPosicion" Width="100" ReadOnly="true" runat="server" />
                    <asp:Button ID="btnSiguiente" Text=">" Width="50" 
                      runat="server" OnClick="btnSiguiente_Click" />
                    <asp:Button ID="btnUltimo" Text=">>" Width="50" 
                      runat="server" OnClick="btnUltimo_Click" />
                </td>
            </tr>
            <tr>
                <td id="celdaPaginas" style="text-align:center" colspan="5" runat="server">
                </td>
            </tr>
        </table>
    </div>
    </form>
</body>
</html>

El diseño de la pagina se mostrará como en la siguiente figura:


Escribir el siguiente código C# en la página:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Northwind.Librerias.EntidadesNegocio; //beEmpleado
using Northwind.Librerias.ReglasNegocio; //brEmpleado
using System.Reflection; //PropertyInfo

public partial class Paginas_ListaEmpleados : System.Web.UI.Page
{
    private List<beEmpleado> lbeEmpleado;
    private int registrosPagina = 6;
    private int indicePaginaActual;
    private int indiceUltimaPagina;

    protected void Page_Load(object sender, EventArgs e)
    {
        txtPosicion.Attributes.Add("style", "text-align:center");
        if (!Page.IsPostBack)
        {
            brEmpleado obrEmpleado = new brEmpleado();
            lbeEmpleado = obrEmpleado.listar();
            Session["Empleados"] = lbeEmpleado;
            Session["Todo"] = lbeEmpleado;
            calcularPaginacion();
            enlazarDataList();
            cargarListaCampos();
        }
        else
        {
            indicePaginaActual = (int)ViewState["indicePaginaActual"];
            indiceUltimaPagina = (int)ViewState["indiceUltimaPagina"];
        }
        crearEnlacesPaginas();
    }

    private void calcularPaginacion()
    {
        indiceUltimaPagina = (lbeEmpleado.Count / registrosPagina);
        if (lbeEmpleado.Count % registrosPagina == 0)
            indiceUltimaPagina--;
        ViewState["indicePaginaActual"] = indicePaginaActual;
        ViewState["indiceUltimaPagina"] = indiceUltimaPagina;
    }

    private void cargarListaCampos()
    {
        ddlCriterio.Items.Add(new ListItem("Todo", ""));
        PropertyInfo[] propiedades = lbeEmpleado[0].
            GetType().GetProperties();
        foreach (PropertyInfo propiedad in propiedades)
        {
            ddlCampo.Items.Add(new ListItem
                (propiedad.Name, propiedad.Name));
            ddlCriterio.Items.Add(new ListItem
                (propiedad.Name, propiedad.Name));
        }
    }

    private void crearEnlacesPaginas()
    {
        celdaPaginas.Controls.Clear();
        LinkButton lbn;
        for (int i = 0; i <= indiceUltimaPagina; i++)
        {
            lbn = new LinkButton();
            lbn.Text = (i + 1).ToString();
            lbn.Click += new EventHandler(paginar);
            celdaPaginas.Controls.Add(lbn);
            celdaPaginas.Controls.Add(new LiteralControl("  "));
        }
    }

    private void paginar(object sender, EventArgs e)
    {
        LinkButton lbn = (LinkButton)sender;
        indicePaginaActual = int.Parse(lbn.Text) - 1;
        ViewState["indicePaginaActual"] = indicePaginaActual;
        enlazarDataList();
    }

    private void enlazarDataList()
    {
        indicePaginaActual = (int)ViewState["indicePaginaActual"];
        indiceUltimaPagina = (int)ViewState["indiceUltimaPagina"];

        lbeEmpleado = (List<beEmpleado>)Session["Empleados"];
        List<beEmpleado> lbeFiltro = new List<beEmpleado>();
        int inicio = indicePaginaActual * registrosPagina;
        int fin = (indicePaginaActual * registrosPagina) + registrosPagina;
        for (int i = inicio; i < fin; i++)
        {
            if (i < lbeEmpleado.Count) lbeFiltro.Add(lbeEmpleado[i]);
            else break;
        }
        dlsEmpleado.DataSource = lbeFiltro;
        dlsEmpleado.DataBind();
        lblRegistros.Text = lbeEmpleado.Count.ToString();
        txtPosicion.Text = String.Format("{0} de {1}", indicePaginaActual + 1,
            indiceUltimaPagina + 1);
    }

    protected void ordenarCampo(object sender, EventArgs e)
    {
        string campo = ddlCampo.SelectedValue;
        int orden = int.Parse(ddlOrden.SelectedValue);
        lbeEmpleado = (List<beEmpleado>)Session["Empleados"];
        if (orden.Equals(0)) lbeEmpleado = lbeEmpleado.OrderBy
            (x => x.GetType().GetProperty(campo).GetValue(x, null)).ToList<beEmpleado>();
        else lbeEmpleado = lbeEmpleado.OrderByDescending
            (x => x.GetType().GetProperty(campo).GetValue(x, null)).ToList<beEmpleado>();
        Session["Empleados"] = lbeEmpleado;
        enlazarDataList();
    }

    protected void filtrarCriterio(object sender, EventArgs e)
    {
        string campo = ddlCriterio.SelectedValue;
        lbeEmpleado = (List<beEmpleado>)Session["Todo"];
        if (!campo.Equals(""))
        {
            string tipo = lbeEmpleado[0].GetType().GetProperty(campo).
                PropertyType.ToString().ToLower();
            if (tipo.Contains("datetime"))
            {
                string[] fechaNac = txtDato.Text.Split('-');
                txtDato.Text = fechaNac[2] + "/" + fechaNac[1] + "/" + fechaNac[0];
            }
            lbeEmpleado = lbeEmpleado.FindAll(x => x.GetType().GetProperty(campo).
               GetValue(x, null).ToString().ToLower().Contains(txtDato.Text.ToLower()));
        }
        celdaBotones.Visible = (lbeEmpleado.Count > 0);
        Session["Empleados"] = lbeEmpleado;
        indicePaginaActual = 0;
        ViewState["indicePaginaActual"] = indicePaginaActual;
        calcularPaginacion();
        enlazarDataList();
        crearEnlacesPaginas();
    }

    protected void btnPrimero_Click(object sender, EventArgs e)
    {
        indicePaginaActual = 0;
        ViewState["indicePaginaActual"] = indicePaginaActual;
        enlazarDataList();
    }

    protected void btnAnterior_Click(object sender, EventArgs e)
    {
        indicePaginaActual = (int)ViewState["indicePaginaActual"];
        if (indicePaginaActual > 0) indicePaginaActual--;
        ViewState["indicePaginaActual"] = indicePaginaActual;
        enlazarDataList();
    }

    protected void btnSiguiente_Click(object sender, EventArgs e)
    {
        indicePaginaActual = (int)ViewState["indicePaginaActual"];
        indiceUltimaPagina = (int)ViewState["indiceUltimaPagina"];
        if (indicePaginaActual < indiceUltimaPagina) indicePaginaActual++;
        ViewState["indicePaginaActual"] = indicePaginaActual;
        enlazarDataList();
    }

    protected void btnUltimo_Click(object sender, EventArgs e)
    {
        indiceUltimaPagina = (int)ViewState["indiceUltimaPagina"];
        indicePaginaActual = indiceUltimaPagina;
        ViewState["indicePaginaActual"] = indicePaginaActual;
        enlazarDataList();
    }

    protected void cambiarControlBusqueda(object sender, EventArgs e)
    {
        string campo = ddlCriterio.SelectedValue;
        txtDato.Text = "";
        if (campo.Equals("")) txtDato.TextMode = TextBoxMode.SingleLine;
        else
        {
            lbeEmpleado = (List<beEmpleado>)Session["Empleados"];
            string tipo = lbeEmpleado[0].GetType().GetProperty(campo).
                PropertyType.ToString().ToLower();
            if (tipo.Contains("int")) txtDato.TextMode = TextBoxMode.Number;
            else
            {
                if (tipo.Contains("datetime")) txtDato.TextMode = TextBoxMode.Date;
                else txtDato.TextMode = TextBoxMode.SingleLine;
            }
        }
        enlazarDataList();
    }
}

Nota: Por defecto los registrosPorPagina está configurado a 6 y usamos Reflection para cargar dinamicamente los combos con los nombres de las propiedades del objeto empleado.

Modificar el Archivo Web.Config para especificar la cadena de conexión a Northwind

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="conNW" providerName="SQLServer" connectionString="uid=UsuarioNW;pwd=123456;
      data source=DSOFT\Sqlexpress; database=Northwind"/>
  </connectionStrings>
    <system.web>
      <compilation debug="true" targetFramework="4.5" />
      <httpRuntime targetFramework="4.5" />
    </system.web>
</configuration>

Probar la Pagina Web

Guardar el Sitio Web, clic derecho a la Pagina "ListaEmpleados.aspx" y seleccionar "Ver en el explorador".


Probar ir a cualquier pagina usando los Links con los números de paginas o con los botones, por ejemplo si estamos en la ultima ya no podemos avanzar a la siguiente o si estamos en la primera ya no podemos retroceder.

Seleccionar un criterio del combo "Ordenar Por", luego seleccionar el "Tipo de Orden" (ascendente o descendente) y clic al botón "Ordenar", ver como los registros se ordenan por el campo seleccionado.

Seleccionar un criterio del combo "Filtrar Por", luego ingresar un dato en "Dato Filtrar" y clic al botón "Filtrar", ver como solo aparecen los registros que cumplen con el criterio, se muestra la cantidad de registros encontrados y como varia los números de paginas.

Comentario Final

En esta demostración hemos visto como podemos paginar, ordenar y filtrar usando el control DataList y sin usar ViewState para dicho control para minimizar el HTML enviado al navegador. Mas bien hemos usado el ViewState (y no Session) para almacenar y recuperar el "indicePaginaActual" y el "indiceUltimaPagina" ya que al hacer PostBack se pierden los valores de las variables.

Descarga:
Demo14_Paginar_Ordenar_Filtrar_DataList