El Libro del Día: 2014-06-30
Titulo: jQuery Mobile Web Development Essentials
Autor: Raymond Camden, Andy Matthews
Editorial: Packt
Nro Paginas: 243
Capítulos:
Chapter 1: Preparing Your First jQuery Mobile Project
Chapter 2: Working with jQuery Mobile Pages
Chapter 3: Enhancing Pages with Headers, Footers, and Toolbars
Chapter 4: Working with Lists
Chapter 5: Getting Practical – Building a Simple
Chapter 6: Working with Forms and jQuery Mobile
Chapter 7: Creating Modal Dialogs and Widgets
Chapter 8: Moving Further with the Notekeeper Mobile Application
Chapter 9: jQuery Mobile Configuration, Utilities, and JavaScript Methods
Chapter 10: Working with Events
Chapter 11: Enhancing jQuery Mobile
Chapter 12: Creating Native Applications
Chapter 13: Becoming an Expert – Building an RSS Reader Application
Descarga:
jQueryMobile_WebDevelopment_Essentials
Blog de Luis Dueñas dedicado a la difusión del Desarrollo en Microsoft .NET, Visual Studio, WinForms, WebForms, MVC, ASP .NET, jQuery, AJAX, HTML5, JavaScript, Móviles, etc. Encontrarás Libros, Demos, Artículos Técnicos, Entrenamiento.
lunes, 30 de junio de 2014
viernes, 27 de junio de 2014
El Demo del Día: Comparación al Validar un Formulario con Javascript (Obstrusivo Vs No Obstrusivo)
Comparación al Validar un Formulario con Javascript (Obstrusivo Vs No Obstrusivo)
Cuando queremos enviar datos hacia el servidor a través de un formulario HTML, por ejemplo en ASP .NET Web Forms, necesitamos validar en el cliente los datos para lo cual debemos usar Javascript.
El método tradicional de Javascript usado era asociar una función a un evento de un control dentro del cuerpo del formulario, por ejemplo en el evento onclick de un botón. A este tipo de Javascript se le conoce como "Javascript Obstrusivo" y lo malo es que mezcla el diseño con el código.
Con la aparición de jQuery se habla mucho de "Javascript No Obstrusivo" que consiste en asociar una función a un evento de un control por código sin interferir en el HTML, pero esta técnica nace gracias a DOM Nivel 2 que incorpora los métodos addEventListener, que permiten asociar a un evento de un control una función Javascript explicita (con nombre) o anónima (sin nombre).
Para los que No conocen que es DOM, significa el Modelo de Objetos del Documento, por ejemplo, el DOM Nivel 1 tiene los objetos Document, Window, Browser y el DOM Nivel 2 el método "addEventListener()" que usaremos para asociar eventos en tiempo de ejecución, junto con el método "preventDefault()" para cancelar el envío del formulario en el click del botón.
Validación del Formulario WebForm con Javascript Obstrusivo
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Obstrusiva.aspx.cs" Inherits="Validacion.Obstrusiva" %>
<!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>
<script>
function validarDatos() {
if (validarCampo("txtNombre", "Ingresa el Nombre") == false) return false;
if (validarCampo("cboCurso", "Seleccione el Curso") == false) return false;
return true;
}
function validarCampo(idControl, mensaje) {
var campo = document.getElementById(idControl);
if (campo != null) {
if (campo.value == "") {
alert(mensaje);
campo.focus();
return false;
}
}
else return false;
return true;
}
</script>
</head>
<body style="background-color:aqua">
<form id="form1" runat="server">
<div>
<h2>Validación Obstrusiva con Javascript</h2>
<h3>Registro de Alumnos</h3>
Nombre:
<asp:TextBox ID="txtNombre" runat="server" /><p />
Curso: <asp:DropDownList id="cboCurso" runat="server">
<asp:ListItem value="">Seleccione</asp:ListItem>
<asp:ListItem value="NET">NET</asp:ListItem>
<asp:ListItem value="Java">Java</asp:ListItem>
<asp:ListItem value="PHP">PHP</asp:ListItem>
</asp:DropDownList><p />
<asp:Button ID="btnEnviar" OnClientClick="return validarDatos();" Text="Enviar" runat="server" />
</div>
</form>
</body>
</html>
Nota: En el control Web "Button" se está asociando a la propiedad "OnClientClick" la función Javascript: "validarDatos()". Al devolver false la función no irá al servidor.
Validación del Formulario WebForm con Javascript No Obstrusivo
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="NoObstrusiva.aspx.cs" Inherits="Validacion.NoObstrusivo" %>
<!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>
<script type="text/javascript">
window.addEventListener("load", crearValidacion, false);
function crearValidacion() {
var btn = document.getElementById("btnEnviar");
btn.addEventListener("click", validarDatos, false);
function validarDatos(e) {
if (validarCampo("txtNombre", "Ingresa el Nombre",e) == false) return false;
if (validarCampo("cboCurso", "Seleccione el Curso",e) == false) return false;
return true;
}
function validarCampo(idControl,mensaje,e) {
var campo = document.getElementById(idControl);
if (campo != null) {
if (campo.value == "") {
alert(mensaje);
campo.focus();
e.preventDefault();
return false;
}
}
else return false;
return true;
}
}
</script>
</head>
<body style="background-color:aqua">
<form id="form1" runat="server">
<div>
<h2>Validación No Obstrusiva con Javascript</h2>
<h3>Registro de Alumnos</h3>
Nombre:
<asp:TextBox ID="txtNombre" runat="server" /><p />
Curso: <asp:DropDownList id="cboCurso" runat="server">
<asp:ListItem value="">Seleccione</asp:ListItem>
<asp:ListItem value="NET">NET</asp:ListItem>
<asp:ListItem value="Java">Java</asp:ListItem>
<asp:ListItem value="PHP">PHP</asp:ListItem>
</asp:DropDownList><p />
<asp:Button ID="btnEnviar" Text="Enviar" runat="server" />
</div>
</form>
</body>
</html>
Nota: En el código Javascript se asocia al evento "load" de la ventana (window) la función "crearValidacion()", la cual captura el botón y asocia a su evento "click" la función "validarDatos()". Para evitar enviar el formulario al servidor al estar vacío un campo se usa el método: "preventDefault()".
Comentario Final
En lo posible hay que usar Javascript No Obstrusivo para evitar mezclar el diseño con el código, ya sea usando jQuery o Javascript nativo. Actualmente, todos los navegadores soportan DOM Nivel 2 así es que el método "addEventListener" correrá sin problemas.
Descarga:
Validacion_WebForm_Javascript
Cuando queremos enviar datos hacia el servidor a través de un formulario HTML, por ejemplo en ASP .NET Web Forms, necesitamos validar en el cliente los datos para lo cual debemos usar Javascript.
El método tradicional de Javascript usado era asociar una función a un evento de un control dentro del cuerpo del formulario, por ejemplo en el evento onclick de un botón. A este tipo de Javascript se le conoce como "Javascript Obstrusivo" y lo malo es que mezcla el diseño con el código.
Con la aparición de jQuery se habla mucho de "Javascript No Obstrusivo" que consiste en asociar una función a un evento de un control por código sin interferir en el HTML, pero esta técnica nace gracias a DOM Nivel 2 que incorpora los métodos addEventListener, que permiten asociar a un evento de un control una función Javascript explicita (con nombre) o anónima (sin nombre).
Para los que No conocen que es DOM, significa el Modelo de Objetos del Documento, por ejemplo, el DOM Nivel 1 tiene los objetos Document, Window, Browser y el DOM Nivel 2 el método "addEventListener()" que usaremos para asociar eventos en tiempo de ejecución, junto con el método "preventDefault()" para cancelar el envío del formulario en el click del botón.
Validación del Formulario WebForm con Javascript Obstrusivo
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Obstrusiva.aspx.cs" Inherits="Validacion.Obstrusiva" %>
<!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>
<script>
function validarDatos() {
if (validarCampo("txtNombre", "Ingresa el Nombre") == false) return false;
if (validarCampo("cboCurso", "Seleccione el Curso") == false) return false;
return true;
}
function validarCampo(idControl, mensaje) {
var campo = document.getElementById(idControl);
if (campo != null) {
if (campo.value == "") {
alert(mensaje);
campo.focus();
return false;
}
}
else return false;
return true;
}
</script>
</head>
<body style="background-color:aqua">
<form id="form1" runat="server">
<div>
<h2>Validación Obstrusiva con Javascript</h2>
<h3>Registro de Alumnos</h3>
Nombre:
<asp:TextBox ID="txtNombre" runat="server" /><p />
Curso: <asp:DropDownList id="cboCurso" runat="server">
<asp:ListItem value="">Seleccione</asp:ListItem>
<asp:ListItem value="NET">NET</asp:ListItem>
<asp:ListItem value="Java">Java</asp:ListItem>
<asp:ListItem value="PHP">PHP</asp:ListItem>
</asp:DropDownList><p />
<asp:Button ID="btnEnviar" OnClientClick="return validarDatos();" Text="Enviar" runat="server" />
</div>
</form>
</body>
</html>
Nota: En el control Web "Button" se está asociando a la propiedad "OnClientClick" la función Javascript: "validarDatos()". Al devolver false la función no irá al servidor.
Validación del Formulario WebForm con Javascript No Obstrusivo
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="NoObstrusiva.aspx.cs" Inherits="Validacion.NoObstrusivo" %>
<!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>
<script type="text/javascript">
window.addEventListener("load", crearValidacion, false);
function crearValidacion() {
var btn = document.getElementById("btnEnviar");
btn.addEventListener("click", validarDatos, false);
function validarDatos(e) {
if (validarCampo("txtNombre", "Ingresa el Nombre",e) == false) return false;
if (validarCampo("cboCurso", "Seleccione el Curso",e) == false) return false;
return true;
}
function validarCampo(idControl,mensaje,e) {
var campo = document.getElementById(idControl);
if (campo != null) {
if (campo.value == "") {
alert(mensaje);
campo.focus();
e.preventDefault();
return false;
}
}
else return false;
return true;
}
}
</script>
</head>
<body style="background-color:aqua">
<form id="form1" runat="server">
<div>
<h2>Validación No Obstrusiva con Javascript</h2>
<h3>Registro de Alumnos</h3>
Nombre:
<asp:TextBox ID="txtNombre" runat="server" /><p />
Curso: <asp:DropDownList id="cboCurso" runat="server">
<asp:ListItem value="">Seleccione</asp:ListItem>
<asp:ListItem value="NET">NET</asp:ListItem>
<asp:ListItem value="Java">Java</asp:ListItem>
<asp:ListItem value="PHP">PHP</asp:ListItem>
</asp:DropDownList><p />
<asp:Button ID="btnEnviar" Text="Enviar" runat="server" />
</div>
</form>
</body>
</html>
Nota: En el código Javascript se asocia al evento "load" de la ventana (window) la función "crearValidacion()", la cual captura el botón y asocia a su evento "click" la función "validarDatos()". Para evitar enviar el formulario al servidor al estar vacío un campo se usa el método: "preventDefault()".
Comentario Final
En lo posible hay que usar Javascript No Obstrusivo para evitar mezclar el diseño con el código, ya sea usando jQuery o Javascript nativo. Actualmente, todos los navegadores soportan DOM Nivel 2 así es que el método "addEventListener" correrá sin problemas.
Descarga:
Validacion_WebForm_Javascript
Etiquetas:
Demos,
DOM,
Javascript,
jQuery,
Lduenas,
No Obstrusivo,
Obstrusivo,
Validación,
WebForms
El Libro del Día: Windows Phone 8 Application Development Essentials
El Libro del Día: 2014-06-27
Titulo: Windows Phone 8 Application Development Essentials
Autor: Tomasz Szostak
Editorial: Packt
Nro Paginas: 118
Capítulos:
Chapter 1: XAML in Windows Phone
Chapter 2: App Design – Best Practices
Chapter 3: Building a Windows Phone 8 Application using MVVM
Chapter 4: Integrating with Windows Phone
Chapter 5: Integrating with Twitter and Facebook
Descarga:
WindowsPhone8_ApplicationDevelopment_Essentials
Titulo: Windows Phone 8 Application Development Essentials
Autor: Tomasz Szostak
Editorial: Packt
Nro Paginas: 118
Capítulos:
Chapter 1: XAML in Windows Phone
Chapter 2: App Design – Best Practices
Chapter 3: Building a Windows Phone 8 Application using MVVM
Chapter 4: Integrating with Windows Phone
Chapter 5: Integrating with Twitter and Facebook
Descarga:
WindowsPhone8_ApplicationDevelopment_Essentials
Etiquetas:
Facebook,
Libros,
MVVM,
Packt,
Tomasz Szostak,
Twitter,
Windows Phone,
XAML
jueves, 26 de junio de 2014
El Libro del Día: Object-Oriented JavaScript
El Libro del Día: 2014-06-26
Titulo: Object-Oriented JavaScript
Autor: Stoyan Stefanov, Kumar Chetan Sharma
Editorial: Packt
Nro Paginas: 382
Capítulos:
Chapter 1: Object-oriented JavaScript
Chapter 2: Primitive Data Types, Arrays, Loops, and Conditions
Chapter 3: Functions
Chapter 4: Objects
Chapter 5: Prototype
Chapter 6: Inheritance
Chapter 7: The Browser Environment
Chapter 8: Coding and Design Patterns
Appendix A: Reserved Words
Appendix B: Built-in Functions
Appendix C: Built-in Objects
Appendix D: Regular Expressions
Descarga:
Object_Oriented_JavaScript
Titulo: Object-Oriented JavaScript
Autor: Stoyan Stefanov, Kumar Chetan Sharma
Editorial: Packt
Nro Paginas: 382
Capítulos:
Chapter 1: Object-oriented JavaScript
Chapter 2: Primitive Data Types, Arrays, Loops, and Conditions
Chapter 3: Functions
Chapter 4: Objects
Chapter 5: Prototype
Chapter 6: Inheritance
Chapter 7: The Browser Environment
Chapter 8: Coding and Design Patterns
Appendix A: Reserved Words
Appendix B: Built-in Functions
Appendix C: Built-in Objects
Appendix D: Regular Expressions
Descarga:
Object_Oriented_JavaScript
miércoles, 25 de junio de 2014
El Libro del Día: Pro ASP.NET 4.5 in C#
El Libro del Día: 2014-06-25
Titulo: Pro ASP.NET 4.5 in C# (5th Edition)
Autor: Adam Freeman, Matthew MacDonald, Mario Szpuszta
Editorial: Apress
Nro Paginas: 1198
Capítulos:
Part 1: Getting Started
Chapter 1: Your First ASP.NET Application
Chapter 2: Putting ASP.NET in Context
Chapter 3: Essential C# Language Features
Chapter 4: Using jQuery
Chapter 5: Essential Development Tools
Chapter 6: SportsStore: A Real Application
Chapter 7: SportsStore: Navigation & Cart
Chapter 8: SportsStore: Completing the Cart
Chapter 9: SportsStore: Administration
Chapter 10: SportsStore: Deployment
Chapter 11: Testable Web Apps
Part 2: The Core ASP.NET Platform
Chapter 12: Working with Web Forms
Chapter 13: Lifecycles and Context
Chapter 14: Modules
Chapter 15: Handlers
Chapter 16: Page and Control Lifecycle Events
Chapter 17: Managing Request Execution
Chapter 18: Managing State Data
Chapter 19: Caching
Chapter 20: Caching Output
Chapter 21: Handling Errors
Chapter 22: Managing Paths
Chapter 23: URL Routing
Chapter 24: Advanced URL Routing
Chapter 25: Authentication and Authorization
Chapter 26: Membership
Chapter 27: ASP.NET Configuration
Chapter 28: Asynchronous Request Handling
Part 3: Forms and Controls
Chapter 29: Working with Controls
Chapter 30: Forms and Request Validation
Chapter 31: Creating Custom Controls
Chapter 32: Stateful Controls
Chapter 33: Server-Side HTML Elements
Chapter 34: Model Binding
Chapter 35: Data Binding
Chapter 36: Basic Data Controls
Chapter 37: Complex Data Controls
Chapter 38: Other ASP.NET Controls
Part 4: Client-Side Development
Chapter 39: Managing Scripts and Styles
Chapter 40: Ajax and Web Services
Chapter 41: Client-Side Validation
Chapter 42: Targeting Mobile Devices
Descarga:
Pro_ASP.NET_4.5_C#
Titulo: Pro ASP.NET 4.5 in C# (5th Edition)
Autor: Adam Freeman, Matthew MacDonald, Mario Szpuszta
Editorial: Apress
Nro Paginas: 1198
Capítulos:
Part 1: Getting Started
Chapter 1: Your First ASP.NET Application
Chapter 2: Putting ASP.NET in Context
Chapter 3: Essential C# Language Features
Chapter 4: Using jQuery
Chapter 5: Essential Development Tools
Chapter 6: SportsStore: A Real Application
Chapter 7: SportsStore: Navigation & Cart
Chapter 8: SportsStore: Completing the Cart
Chapter 9: SportsStore: Administration
Chapter 10: SportsStore: Deployment
Chapter 11: Testable Web Apps
Part 2: The Core ASP.NET Platform
Chapter 12: Working with Web Forms
Chapter 13: Lifecycles and Context
Chapter 14: Modules
Chapter 15: Handlers
Chapter 16: Page and Control Lifecycle Events
Chapter 17: Managing Request Execution
Chapter 18: Managing State Data
Chapter 19: Caching
Chapter 20: Caching Output
Chapter 21: Handling Errors
Chapter 22: Managing Paths
Chapter 23: URL Routing
Chapter 24: Advanced URL Routing
Chapter 25: Authentication and Authorization
Chapter 26: Membership
Chapter 27: ASP.NET Configuration
Chapter 28: Asynchronous Request Handling
Part 3: Forms and Controls
Chapter 29: Working with Controls
Chapter 30: Forms and Request Validation
Chapter 31: Creating Custom Controls
Chapter 32: Stateful Controls
Chapter 33: Server-Side HTML Elements
Chapter 34: Model Binding
Chapter 35: Data Binding
Chapter 36: Basic Data Controls
Chapter 37: Complex Data Controls
Chapter 38: Other ASP.NET Controls
Part 4: Client-Side Development
Chapter 39: Managing Scripts and Styles
Chapter 40: Ajax and Web Services
Chapter 41: Client-Side Validation
Chapter 42: Targeting Mobile Devices
Descarga:
Pro_ASP.NET_4.5_C#
Etiquetas:
Adam Freeman,
AJAX,
Apress,
ASP .NET,
Asynchronous,
C#,
Caching,
jQuery,
Libros,
Mario Szpuszta,
Matthew MacDonald,
Membership,
Mobile,
URL Routing,
WebForms
martes, 24 de junio de 2014
El Demo del Día: Comparación de Rendimiento al Llenar Grillas Jerárquicas en WinForms
Comparación de Rendimiento al Llenar Grillas Jerárquicas en WinForms
Hoy veremos un caso muy interesante que es crear Grillas Jerárquicas en WinForms para realizar consultas desconectadas de 3 niveles. En el presente Demo veremos como consultar los Detalles de las Ordenes de los Clientes de la Base de Datos Northwind.
Problema con Grillas Jerárquicas en WinForms
En WinForms hay 2 problemas que se presentan cuando queremos trabajar con Grillas Jerárquicas:
1. El Control DataGridView no tiene soporte para listas jerárquicas.
2. Cual es la mejor forma de llenar la grilla:
2.1. Crear una lista de objetos jerárquicos que tengan como propiedades otras listas de objetos.
2.2. Crear varias listas de objetos lineales, es decir No jerárquicos y realizar el filtro en la App.
Solución del Problema con Grillas Jerárquicas en WinForms
1. Podemos agregar un Control DataGridView a otro Control DataGridView usando el método Add de su colección Controls, ocultarlo y ubicarlo dentro de la grilla usando el método SetBounds.
2. La mejor forma de llenar una grilla WinForms es la segunda, es decir, tener varias listas en un objeto con todas las listas (lineales No jerárquicas), ya que la carga es más rápido y consume menos memoria.
Creación del Procedimiento Almacenado que devuelva los 3 Selects
Create Procedure [dbo].[uspClienteOrdenDetalleListar]
As
Select CustomerID,CompanyName,Address,IsNull(ContactName,'') As ContactName From Customers
Select OrderID,OrderDate,CustomerID,EmployeeID From Orders
Select d.OrderID,d.ProductID,p.ProductName,d.UnitPrice,d.Quantity
From Order_Details d Inner Join Products p On d.ProductID=p.ProductID
Creación de las Clases de Tipo Entidades del Negocio (be: Business Entities)
Creamos una aplicación WinForms llamada: "Grillas_Jerarquicas_3Niveles" y agregamos las sgtes clases:
1. Clase beDetalle
using System;
namespace Grillas_Jerarquicas_3Niveles
{
public class beDetalle
{
public int IdOrden { get; set; }
public int IdProducto { get; set; }
public string NombreProducto { get; set; }
public decimal PrecioUnitario { get; set; }
public short Cantidad { get; set; }
public decimal PrecioTotal {
get
{
return (PrecioUnitario * Cantidad);
}
}
}
}
2. Clase beOrden
using System;
using System.Collections.Generic;
namespace Grillas_Jerarquicas_3Niveles
{
public class beOrden
{
public int IdOrden { get; set; }
public DateTime FechaOrden { get; set; }
public string IdCliente { get; set; }
public int IdEmpleado { get; set; }
public List<beDetalle> Detalles { get; set; }
}
}
3. Clase beCliente
using System;
using System.Collections.Generic;
namespace Grillas_Jerarquicas_3Niveles
{
public class beCliente
{
public string IdCliente { get; set; }
public string Nombre { get; set; }
public string Direccion { get; set; }
public string Contacto { get; set; }
public List<beOrden> Ordenes { get; set; }
}
}
using System.Collections.Generic;
namespace Grillas_Jerarquicas_3Niveles
{
public class beListas
{
public List<beCliente> Clientes {get; set;}
public List<beOrden> Ordenes { get; set; }
public List<beDetalle> Detalles { get; set; }
}
}
Nota: Por un tema de simplificación del demo las clases han sido incluidas dentro de la misma aplicación WinForms, pero lo ideal es que estén en Librerías.
Creación de la Clase de Acceso a Datos (da: Data Access)
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
namespace Grillas_Jerarquicas_3Niveles
{
public class daCliente
{
public beListas listar(SqlConnection con)
{
beListas obeListas = new beListas();
List<beCliente> lbeCliente = new List<beCliente>();
List<beOrden> lbeOrden = new List<beOrden>();
List<beDetalle> lbeDetalle = new List<beDetalle>();
beCliente obeCliente = null;
beOrden obeOrden = null;
beDetalle obeDetalle = null;
//Llenar las 3 Listas desde el SP de SQL Server
SqlCommand cmd = new SqlCommand("uspClienteOrdenDetalleListar", con);
SqlDataReader drd = cmd.ExecuteReader();
if (drd != null)
{
//Leer el Primer Select de Clientes
while (drd.Read())
{
obeCliente = new beCliente();
obeCliente.IdCliente = drd.GetString(0);
obeCliente.Nombre = drd.GetString(1);
obeCliente.Direccion = drd.GetString(2);
obeCliente.Contacto = drd.GetString(3);
lbeCliente.Add(obeCliente);
}
//Leer el Segundo Select de Ordenes
if (drd.NextResult())
{
while (drd.Read())
{
obeOrden = new beOrden();
obeOrden.IdOrden = drd.GetInt32(0);
obeOrden.FechaOrden = drd.GetDateTime(1);
obeOrden.IdCliente = drd.GetString(2);
obeOrden.IdEmpleado = drd.GetInt32(3);
lbeOrden.Add(obeOrden);
}
}
//Leer el Tercer Select de Detalles
if (drd.NextResult())
{
while (drd.Read())
{
obeDetalle = new beDetalle();
obeDetalle.IdOrden = drd.GetInt32(0);
obeDetalle.IdProducto = drd.GetInt32(1);
obeDetalle.NombreProducto = drd.GetString(2);
obeDetalle.PrecioUnitario = drd.GetDecimal(3);
obeDetalle.Cantidad = drd.GetInt16(4);
lbeDetalle.Add(obeDetalle);
}
}
}
//Llenar las 3 Listas al objeto
obeListas.Clientes = lbeCliente;
obeListas.Ordenes = lbeOrden;
obeListas.Detalles = lbeDetalle;
return (obeListas);
}
}
}
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
namespace Grillas_Jerarquicas_3Niveles
{
public class brCliente
{
public beListas listarLineal()
{
beListas obeListas;
string conexion = ConfigurationManager.ConnectionStrings["conNW"].ConnectionString;
using (SqlConnection con = new SqlConnection(conexion))
{
con.Open();
daCliente odaCliente = new daCliente();
obeListas = odaCliente.listar(con);
} //con.Close(); con.Dispose(); con=null;
return (obeListas);
}
public List<beCliente> listarJerarquico()
{
beListas obeListas = listarLineal();
foreach (beCliente unCliente in obeListas.Clientes)
{
foreach (beOrden unaOrden in obeListas.Ordenes)
{
unaOrden.Detalles = obeListas.Detalles.FindAll
(x => x.IdOrden.Equals(unaOrden.IdOrden));
}
unCliente.Ordenes = obeListas.Ordenes.FindAll
(x => x.IdCliente.Equals(unCliente.IdCliente));
}
return (obeListas.Clientes);
}
}
}
Comentario Final
Hemos demostrado que con un poco de ingenio podemos crear Grillas Jerárquicas en WinForms usando Listas de Objetos y que mejor es usar Listas Lineales y filtrarlas a demanda en vez de crear listas jerárquicas que demoran en cargarse (12,000 msg Versus 200 msg) y consumen mucha memoria. Espero les sea útil sobre a todo los desarrolladores que trabajan en Cliente Servidor o Windows que mucha veces extrañan la facilidad de Visual Foxpro, Power Builder, etc.
Descarga:
Demo06_Grillas_Jerarquicas_3Niveles
Hoy veremos un caso muy interesante que es crear Grillas Jerárquicas en WinForms para realizar consultas desconectadas de 3 niveles. En el presente Demo veremos como consultar los Detalles de las Ordenes de los Clientes de la Base de Datos Northwind.
Problema con Grillas Jerárquicas en WinForms
En WinForms hay 2 problemas que se presentan cuando queremos trabajar con Grillas Jerárquicas:
1. El Control DataGridView no tiene soporte para listas jerárquicas.
2. Cual es la mejor forma de llenar la grilla:
2.1. Crear una lista de objetos jerárquicos que tengan como propiedades otras listas de objetos.
2.2. Crear varias listas de objetos lineales, es decir No jerárquicos y realizar el filtro en la App.
Solución del Problema con Grillas Jerárquicas en WinForms
1. Podemos agregar un Control DataGridView a otro Control DataGridView usando el método Add de su colección Controls, ocultarlo y ubicarlo dentro de la grilla usando el método SetBounds.
2. La mejor forma de llenar una grilla WinForms es la segunda, es decir, tener varias listas en un objeto con todas las listas (lineales No jerárquicas), ya que la carga es más rápido y consume menos memoria.
Creación del Procedimiento Almacenado que devuelva los 3 Selects
Create Procedure [dbo].[uspClienteOrdenDetalleListar]
As
Select CustomerID,CompanyName,Address,IsNull(ContactName,'') As ContactName From Customers
Select OrderID,OrderDate,CustomerID,EmployeeID From Orders
Select d.OrderID,d.ProductID,p.ProductName,d.UnitPrice,d.Quantity
From Order_Details d Inner Join Products p On d.ProductID=p.ProductID
Creación de las Clases de Tipo Entidades del Negocio (be: Business Entities)
Creamos una aplicación WinForms llamada: "Grillas_Jerarquicas_3Niveles" y agregamos las sgtes clases:
1. Clase beDetalle
using System;
namespace Grillas_Jerarquicas_3Niveles
{
public class beDetalle
{
public int IdOrden { get; set; }
public int IdProducto { get; set; }
public string NombreProducto { get; set; }
public decimal PrecioUnitario { get; set; }
public short Cantidad { get; set; }
public decimal PrecioTotal {
get
{
return (PrecioUnitario * Cantidad);
}
}
}
}
2. Clase beOrden
using System;
using System.Collections.Generic;
namespace Grillas_Jerarquicas_3Niveles
{
public class beOrden
{
public int IdOrden { get; set; }
public DateTime FechaOrden { get; set; }
public string IdCliente { get; set; }
public int IdEmpleado { get; set; }
public List<beDetalle> Detalles { get; set; }
}
}
3. Clase beCliente
using System;
using System.Collections.Generic;
namespace Grillas_Jerarquicas_3Niveles
{
public class beCliente
{
public string IdCliente { get; set; }
public string Nombre { get; set; }
public string Direccion { get; set; }
public string Contacto { get; set; }
public List<beOrden> Ordenes { get; set; }
}
}
4. Clase beListas
using System;using System.Collections.Generic;
namespace Grillas_Jerarquicas_3Niveles
{
public class beListas
{
public List<beCliente> Clientes {get; set;}
public List<beOrden> Ordenes { get; set; }
public List<beDetalle> Detalles { get; set; }
}
}
Nota: Por un tema de simplificación del demo las clases han sido incluidas dentro de la misma aplicación WinForms, pero lo ideal es que estén en Librerías.
Creación de la Clase de Acceso a Datos (da: Data Access)
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
namespace Grillas_Jerarquicas_3Niveles
{
public class daCliente
{
public beListas listar(SqlConnection con)
{
beListas obeListas = new beListas();
List<beCliente> lbeCliente = new List<beCliente>();
List<beOrden> lbeOrden = new List<beOrden>();
List<beDetalle> lbeDetalle = new List<beDetalle>();
beCliente obeCliente = null;
beOrden obeOrden = null;
beDetalle obeDetalle = null;
//Llenar las 3 Listas desde el SP de SQL Server
SqlCommand cmd = new SqlCommand("uspClienteOrdenDetalleListar", con);
SqlDataReader drd = cmd.ExecuteReader();
if (drd != null)
{
//Leer el Primer Select de Clientes
while (drd.Read())
{
obeCliente = new beCliente();
obeCliente.IdCliente = drd.GetString(0);
obeCliente.Nombre = drd.GetString(1);
obeCliente.Direccion = drd.GetString(2);
obeCliente.Contacto = drd.GetString(3);
lbeCliente.Add(obeCliente);
}
//Leer el Segundo Select de Ordenes
if (drd.NextResult())
{
while (drd.Read())
{
obeOrden = new beOrden();
obeOrden.IdOrden = drd.GetInt32(0);
obeOrden.FechaOrden = drd.GetDateTime(1);
obeOrden.IdCliente = drd.GetString(2);
obeOrden.IdEmpleado = drd.GetInt32(3);
lbeOrden.Add(obeOrden);
}
}
//Leer el Tercer Select de Detalles
if (drd.NextResult())
{
while (drd.Read())
{
obeDetalle = new beDetalle();
obeDetalle.IdOrden = drd.GetInt32(0);
obeDetalle.IdProducto = drd.GetInt32(1);
obeDetalle.NombreProducto = drd.GetString(2);
obeDetalle.PrecioUnitario = drd.GetDecimal(3);
obeDetalle.Cantidad = drd.GetInt16(4);
lbeDetalle.Add(obeDetalle);
}
}
}
//Llenar las 3 Listas al objeto
obeListas.Clientes = lbeCliente;
obeListas.Ordenes = lbeOrden;
obeListas.Detalles = lbeDetalle;
return (obeListas);
}
}
}
Creación de la Clase de Reglas de Negocios (br: Business Rules)
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
namespace Grillas_Jerarquicas_3Niveles
{
public class brCliente
{
public beListas listarLineal()
{
beListas obeListas;
string conexion = ConfigurationManager.ConnectionStrings["conNW"].ConnectionString;
using (SqlConnection con = new SqlConnection(conexion))
{
con.Open();
daCliente odaCliente = new daCliente();
obeListas = odaCliente.listar(con);
} //con.Close(); con.Dispose(); con=null;
return (obeListas);
}
public List<beCliente> listarJerarquico()
{
beListas obeListas = listarLineal();
foreach (beCliente unCliente in obeListas.Clientes)
{
foreach (beOrden unaOrden in obeListas.Ordenes)
{
unaOrden.Detalles = obeListas.Detalles.FindAll
(x => x.IdOrden.Equals(unaOrden.IdOrden));
}
unCliente.Ordenes = obeListas.Ordenes.FindAll
(x => x.IdCliente.Equals(unCliente.IdCliente));
}
return (obeListas.Clientes);
}
}
}
Nota: Esta clase tiene 2 métodos: listarLineal() que devuelve un objeto con las 3 listas y listarJerarquico() que devuelve una lista de clientes que a su vez tiene una lista de ordenes y esta a su vez tiene una lista de detalles de cada orden.
Aumentar la cadena de conexión en el archivo de configuración
Abrir el archivo App.Config y debajo de <configuration> escribir lo siguiente:
<connectionStrings>
<add name="conNW" providerName="System.Data.SqlClient"
connectionString="uid=UsuarioNW;pwd=123456;data source=DSOFT\Sqlexpress;
initial catalog=Northwind"/>
</connectionStrings>
Nota: Cambiar el usuario y password, también el data source con el servidor que se tenga disponible.
Nota: Cambiar el usuario y password, también el data source con el servidor que se tenga disponible.
Formulario de Prueba de Grillas Jerárquicas llenadas con Listas Lineales
Crear un formulario llamado: "frmGrillaListasLineales" con una Grilla llamada "dgvCliente" acoplada a todo el formulario, asociar al evento "Load" del formulario la función: "cargarDatos", tal como se muestra en el siguiente código:
using System.Windows.Forms;
using System.Diagnostics;
namespace Grillas_Jerarquicas_3Niveles
{
public partial class frmGrillaListasLineales : Form
{
private beListas obeListas;
private DataGridView dgvOrden;
private DataGridView dgvDetalle;
public frmGrillaListasLineales()
{
InitializeComponent();
}
private void cargarDatos(object sender, EventArgs e)
{
Stopwatch oReloj = new Stopwatch();
oReloj.Start();
brCliente obrCliente = new brCliente();
obeListas = obrCliente.listarLineal();
//Configurar Grilla Clientes
dgvCliente.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
dgvCliente.CellMouseMove += new DataGridViewCellMouseEventHandler
(cambiarPunteroMouse);
dgvCliente.CellContentClick += new DataGridViewCellEventHandler
(listarOrdenesPorCliente);
(listarOrdenesPorCliente);
dgvCliente.KeyPress += new KeyPressEventHandler(ocultarGrillas);
dgvCliente.DataSource = obeListas.Clientes;
//Configurar Grilla Ordenes
dgvOrden = new DataGridView();
dgvOrden.Visible = false;
dgvOrden.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
dgvOrden.CellMouseMove += new DataGridViewCellMouseEventHandler
(cambiarPunteroMouse);
dgvOrden.CellContentClick += new DataGridViewCellEventHandler
(listarDetallesPorOrden);
(listarDetallesPorOrden);
dgvCliente.Controls.Add(dgvOrden);
//Configurar Grilla Detalles
dgvDetalle = new DataGridView();
dgvDetalle.Visible = false;
dgvDetalle.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
dgvOrden.Controls.Add(dgvDetalle);
oReloj.Stop();
this.Text = String.Format("Tiempo Procesamiento: {0:n0} msg",
oReloj.Elapsed.TotalMilliseconds);
oReloj.Elapsed.TotalMilliseconds);
}
private void cambiarPunteroMouse(object sender, DataGridViewCellMouseEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
if (e.ColumnIndex.Equals(0)) dgv.Cursor = Cursors.Hand;
else dgv.Cursor = Cursors.Default;
}
private void listarOrdenesPorCliente(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex.Equals(0))
{
string IdCliente = dgvCliente.CurrentCell.Value.ToString();
dgvOrden.Visible = true;
dgvOrden.DataSource = obeListas.Ordenes.FindAll
(obe => obe.IdCliente.Equals(IdCliente));
(obe => obe.IdCliente.Equals(IdCliente));
int x = dgvCliente.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Left +
dgvCliente.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Width;
int y = dgvCliente.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Top +
dgvCliente.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Height;
dgvOrden.SetBounds(x, y, 800, 400);
}
}
private void listarDetallesPorOrden(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex.Equals(0))
{
int IdOrden = (int)dgvOrden.CurrentCell.Value;
dgvDetalle.Visible = true;
dgvDetalle.DataSource = obeListas.Detalles.FindAll
(obe => obe.IdOrden.Equals(IdOrden));
(obe => obe.IdOrden.Equals(IdOrden));
int x = dgvOrden.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Left +
dgvOrden.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Width;
int y = dgvOrden.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Top +
dgvOrden.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Height;
dgvDetalle.SetBounds(x, y, 600, 200);
}
}
private void ocultarGrillas(object sender, KeyPressEventArgs e)
{
if (((int)e.KeyChar).Equals(27))
{
if (dgvDetalle.Visible) dgvDetalle.Visible = false;
if (dgvOrden.Visible) dgvOrden.Visible = false;
}
}
}
}
Formulario de Prueba de Grillas Jerárquicas llenadas con Listas Jerárquicas
Crear un formulario llamado: "frmGrillaListasJerarquicas" con una Grilla llamada "dgvCliente" acoplada a todo el formulario, asociar al evento "Load" del formulario la función: "cargarDatos", tal como se muestra en el siguiente código:
using System.Windows.Forms;
using System.Diagnostics;
namespace Grillas_Jerarquicas_3Niveles
{
public partial class frmGrillaListasJerarquicas : Form
{
private List<beCliente> lbeCliente;
private DataGridView dgvOrden;
private DataGridView dgvDetalle;
public frmGrillaListasJerarquicas()
{
InitializeComponent();
}
private void cargarDatos(object sender, EventArgs e)
{
Stopwatch oReloj = new Stopwatch();
oReloj.Start();
brCliente obrCliente = new brCliente();
lbeCliente = obrCliente.listarJerarquico();
//Configurar Grilla Clientes
dgvCliente.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
dgvCliente.CellMouseMove += new DataGridViewCellMouseEventHandler
(cambiarPunteroMouse);
dgvCliente.CellContentClick += new DataGridViewCellEventHandler
(listarOrdenesPorCliente);
(listarOrdenesPorCliente);
dgvCliente.KeyPress += new KeyPressEventHandler(ocultarGrillas);
dgvCliente.DataSource = lbeCliente;
//Configurar Grilla Ordenes
dgvOrden = new DataGridView();
dgvOrden.Visible = false;
dgvOrden.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
dgvOrden.CellMouseMove += new DataGridViewCellMouseEventHandler
(cambiarPunteroMouse);
dgvOrden.CellContentClick += new DataGridViewCellEventHandler
(listarDetallesPorOrden);
(listarDetallesPorOrden);
dgvCliente.Controls.Add(dgvOrden);
//Configurar Grilla Detalles
dgvDetalle = new DataGridView();
dgvDetalle.Visible = false;
dgvDetalle.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
dgvOrden.Controls.Add(dgvDetalle);
oReloj.Stop();
this.Text = String.Format("Tiempo Procesamiento: {0:n0} msg",
oReloj.Elapsed.TotalMilliseconds);
oReloj.Elapsed.TotalMilliseconds);
}
private void cambiarPunteroMouse(object sender, DataGridViewCellMouseEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
if (e.ColumnIndex.Equals(0)) dgv.Cursor = Cursors.Hand;
else dgv.Cursor = Cursors.Default;
}
private void listarOrdenesPorCliente(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex.Equals(0))
{
string IdCliente = dgvCliente.CurrentCell.Value.ToString();
int posCliente = lbeCliente.FindIndex(obe => obe.IdCliente.Equals(IdCliente));
dgvOrden.Visible = true;
dgvOrden.DataSource = lbeCliente[posCliente].Ordenes;
int x = dgvCliente.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Left +
dgvCliente.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Width;
int y = dgvCliente.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Top +
dgvCliente.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Height;
dgvOrden.SetBounds(x, y, 800, 400);
}
}
private void listarDetallesPorOrden(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex.Equals(0))
{
string IdCliente = dgvCliente.CurrentCell.Value.ToString();
int posCliente = lbeCliente.FindIndex(obe => obe.IdCliente.Equals(IdCliente));
int IdOrden = (int)dgvOrden.CurrentCell.Value;
int posOrden = lbeCliente[posCliente].Ordenes.FindIndex
(obe=>obe.IdOrden.Equals(IdOrden));
dgvDetalle.Visible = true;
dgvDetalle.DataSource = lbeCliente[posCliente].Ordenes[posOrden].Detalles;
int x = dgvOrden.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Left +
dgvOrden.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Width;
int y = dgvOrden.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Top +
dgvOrden.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false).Height;
dgvDetalle.SetBounds(x, y, 600, 200);
}
}
private void ocultarGrillas(object sender, KeyPressEventArgs e)
{
if (((int)e.KeyChar).Equals(27))
{
if (dgvDetalle.Visible) dgvDetalle.Visible = false;
if (dgvOrden.Visible) dgvOrden.Visible = false;
}
}
}
}
Ejecución y Pruebas de Ambos Formularios
Configurar en el archivo "Program.cs" en la función "Main" lo siguiente:
Application.Run(new frmGrillaListasLineales());
Pulsar F5 para ejecutar y probar el primer formulario con Listas Lineales y la ventana sale de inmediato (de 200 a 400 msg aprox), luego clic a cualquier celda de la primera columna con el código del cliente y se mostrará sus ordenes, luego clic a cualquier celda de la columna con el código de la orden y se mostrará sus detalles.
Para salir de las grillas pulsar la tecla Escape sobre la grilla de Clientes. Detener la aplicación.
Configurar en el archivo "Program.cs" en la función "Main" lo siguiente:
Application.Run(new frmGrillaListaJerarquicas());
Pulsar F5 para ejecutar y probar el segundo formulario con Listas Jerárquicas y la ventana demora en mostrarse (mas de 12000 msg aprox), luego clic a cualquier celda de la primera columna con el código del cliente y se mostrará sus ordenes, luego clic a cualquier celda de la columna con el código de la orden y se mostrará sus detalles.
Comentario Final
Hemos demostrado que con un poco de ingenio podemos crear Grillas Jerárquicas en WinForms usando Listas de Objetos y que mejor es usar Listas Lineales y filtrarlas a demanda en vez de crear listas jerárquicas que demoran en cargarse (12,000 msg Versus 200 msg) y consumen mucha memoria. Espero les sea útil sobre a todo los desarrolladores que trabajan en Cliente Servidor o Windows que mucha veces extrañan la facilidad de Visual Foxpro, Power Builder, etc.
Descarga:
Demo06_Grillas_Jerarquicas_3Niveles
El Libro del Día: ASP.NET MVC 4 In Action
El Libro del Día: 2014-06-24
Titulo: ASP.NET MVC 4 In Action
Autor: Jeffrey Palermo, Jimmy Bogard, Eric Hexter,
Matthew Hinze, Jeremy Skinner
Editorial: Manning
Nro Paginas: 440
Capítulos:
PART 1 HIGH-SPEED FUNDAMENTALS
1 Introduction to ASP.NET MVC
2 Hello MVC world
3 View fundamentals
4 Action-packed controllers
PART 2 WORKING WITH ASP.NET MVC
5 View models
6 Validation
7 Ajax in ASP.NET MVC
8 Security
9 Controlling URLs with routing
10 Model binders and value providers
11 Mapping with AutoMapper
12 Lightweight controllers
13 Organization with areas
14 Third-party components
15 Data access with NHibernate
PART 3 MASTERING ASP.NET MVC
16 Extending the controller
17 Advanced view techniques
18 Dependency injection and extensibility
19 Portable areas
20 Full system testing
21 Hosting ASP.NET MVC applications
22 Deployment techniques
23 Upgrading to ASP.NET MVC 4
24 ASP.NET Web API
Descarga:
ASPNET_MVC4_InAction
Titulo: ASP.NET MVC 4 In Action
Autor: Jeffrey Palermo, Jimmy Bogard, Eric Hexter,
Matthew Hinze, Jeremy Skinner
Editorial: Manning
Nro Paginas: 440
Capítulos:
PART 1 HIGH-SPEED FUNDAMENTALS
1 Introduction to ASP.NET MVC
2 Hello MVC world
3 View fundamentals
4 Action-packed controllers
PART 2 WORKING WITH ASP.NET MVC
5 View models
6 Validation
7 Ajax in ASP.NET MVC
8 Security
9 Controlling URLs with routing
10 Model binders and value providers
11 Mapping with AutoMapper
12 Lightweight controllers
13 Organization with areas
14 Third-party components
15 Data access with NHibernate
PART 3 MASTERING ASP.NET MVC
16 Extending the controller
17 Advanced view techniques
18 Dependency injection and extensibility
19 Portable areas
20 Full system testing
21 Hosting ASP.NET MVC applications
22 Deployment techniques
23 Upgrading to ASP.NET MVC 4
24 ASP.NET Web API
Descarga:
ASPNET_MVC4_InAction
Etiquetas:
AJAX,
Areas,
ASP .NET,
Eric Hexter,
Jeffrey Palermo,
Jeremy Skinner,
Jimmy Bogard,
Libros,
Manning,
Matthew Hinze,
MVC,
NHibernate,
Web API
lunes, 23 de junio de 2014
El Demo del Día: Comparación de Rendimiento al Llenar Listas en WinForms
Comparación de Rendimiento al Llenar Listas en WinForms
En este post, compararemos el rendimiento de diferentes técnicas para llenar listas en Windows Forms desde un arreglo. En WinForms los controles ListBox, ComboBox, CheckedListBox y ListView tienen una colección de Items que representan los elementos del control. Aprenderemos varias formas de mejorar la performance o rendimiento al llenar controles de tipo listas.
Problema al Llenar Listas en WinForms
Si tenemos un arreglo con datos y queremos llenar un control de tipo lista en WinForms, muchos programadores crean un bucle (for, foreach, while) y leen del arreglo cada elemento y lo agregan al control lista. Si son muchos registros a agregar el tiempo aumenta considerablemente en proporción a la cantidad de filas y se nota un parpadeo al momento de pintar el control.
Solución al Problema de Llenar Listas en WinForms
Existen 2 formas de solucionar el problema del rendimiento al llenar un control lista desde un arreglo:
1. Usar el método AddRange de la colección de Items del control lista y agregar directamente el arreglo.
2. Usar el método BeginUpdate antes del bucle para congelar la actualización en pantalla de fila x fila y al final del bucle llamar al método EndUpdate para actualizar el pintado del control lista.
Diseño de la Aplicación WinForms
Crear una aplicación WinForms llamada: "Comparacion_Rendimiento_Listas" en C# y cambiar de nombre al formulario por "frmLista", luego agregar los siguientes controles:
- Un Control Label llamado "lblTiempoProcesamiento"
- Un Control ListBox llamado "lstNumero"
- Cinco Controles Button llamados: "btnLlenarArreglo", "btnLlenarListaForItemsAdd",
"btnLlenarListaAddRange", "btnLlenarListaForItemsAddBU" y "btnLlenarListaAddRangeBU".
Código Fuente de la Aplicación WinForms
En el código he creado un método "ejecutarMetodo" que tiene como parámetro un método gracias al tipo "Action" lo que me permite crear una rutina que permita "medir el tiempo de procesamiento de cualquier método" para poder comparar los tiempos en cada caso, para lo cual usamos la clase "Stopwatch" de "System.Diagnostics".
Además usamos un arreglo de cadenas, el cual lo llenamos con 10000 registros para poder ser pasados al control ListBox con las diferentes técnicas.
using System.Windows.Forms;
using System.Diagnostics;
namespace Comparacion_Rendimiento_Listas
{
public partial class frmLista : Form
{
private string[] lista;
public frmLista()
{
InitializeComponent();
}
public string ejecutarMetodo(Action metodo)
{
Stopwatch oReloj = new Stopwatch();
oReloj.Start();
metodo();
oReloj.Stop();
return (String.Format("Tiempo de Procesamiento: {0:n0} msg",
oReloj.Elapsed.TotalMilliseconds));
}
private void llenarArreglo()
{
lista=new string[10000];
for (int i = 0; i < lista.Length; i++)
{
lista[i] = i.ToString();
}
}
private void btnLlenarArreglo_Click(object sender, EventArgs e)
{
lblTiempoProcesamiento.Text = ejecutarMetodo(llenarArreglo);
}
private void llenarListaForItemsAdd()
{
lstNumero.Items.Clear();
for (int i = 0; i < lista.Length; i++)
{
lstNumero.Items.Add(lista[i]);
}
}
private void btnLlenarListaForItemsAdd_Click(object sender, EventArgs e)
{
lblTiempoProcesamiento.Text = ejecutarMetodo(llenarListaForItemsAdd);
}
private void llenarListaAddRange()
{
lstNumero.Items.Clear();
lstNumero.Items.AddRange(lista);
}
private void btnLlenarListaAddRange_Click(object sender, EventArgs e)
{
lblTiempoProcesamiento.Text = ejecutarMetodo(llenarListaAddRange);
}
private void llenarListaForItemsAddConBeginUpdate()
{
lstNumero.BeginUpdate();
lstNumero.Items.Clear();
for (int i = 0; i < lista.Length; i++)
{
lstNumero.Items.Add(lista[i]);
}
lstNumero.EndUpdate();
}
private void btnLlenarListaForItemsAddBU_Click(object sender, EventArgs e)
{
lblTiempoProcesamiento.Text = ejecutarMetodo(llenarListaForItemsAddConBeginUpdate);
}
private void llenarListaAddRangeConBeginUpdate()
{
lstNumero.BeginUpdate();
lstNumero.Items.Clear();
lstNumero.Items.AddRange(lista);
lstNumero.EndUpdate();
}
private void btnLlenarListaAddRangeBU_Click(object sender, EventArgs e)
{
lblTiempoProcesamiento.Text = ejecutarMetodo(llenarListaAddRangeConBeginUpdate);
}
}
}
Si ejecutamos la aplicación y damos clic al botón "Llenar Arreglo" notamos que el tiempo de procesamiento es de 2 msg (mili segundos), si damos clic al botón "Llenar Lista For Items Add" el tiempo de procesamiento es de 3306 msg (este puede variar cada vez que se da clic entre 2500 y 3500 aproximadamente), además en todo este tiempo se nota la aplicación en espera y con parpadeo.
Si damos clic al botón "Llenar Lista AddRange" o los otros 2 últimos botones (es decir las 3 formas de mejor rendimiento) notamos que el tiempo de procesamiento es de 69 msg (puede variar entre 60 y 80 msg).
Comentario Final
Después de hacer las comparaciones de rendimiento nos damos cuenta que la diferencia es extremadamente abismal, para 10000 filas el método clásico del bucle demora mas de 3000 msg mientras que las otras 3 técnicas solo 70 msg, es decir una diferencia de 3000, la cual aumenta con el aumento de los registros.
Después de esto, espero que ningún programador se olvide de colocar "BeginUpdate" antes del llenado de su lista y al final "EndUpdate" o use directamente el método AddRange.
Descarga:
Demo05_Rendimiento_Listas
En este post, compararemos el rendimiento de diferentes técnicas para llenar listas en Windows Forms desde un arreglo. En WinForms los controles ListBox, ComboBox, CheckedListBox y ListView tienen una colección de Items que representan los elementos del control. Aprenderemos varias formas de mejorar la performance o rendimiento al llenar controles de tipo listas.
Problema al Llenar Listas en WinForms
Si tenemos un arreglo con datos y queremos llenar un control de tipo lista en WinForms, muchos programadores crean un bucle (for, foreach, while) y leen del arreglo cada elemento y lo agregan al control lista. Si son muchos registros a agregar el tiempo aumenta considerablemente en proporción a la cantidad de filas y se nota un parpadeo al momento de pintar el control.
Solución al Problema de Llenar Listas en WinForms
Existen 2 formas de solucionar el problema del rendimiento al llenar un control lista desde un arreglo:
1. Usar el método AddRange de la colección de Items del control lista y agregar directamente el arreglo.
2. Usar el método BeginUpdate antes del bucle para congelar la actualización en pantalla de fila x fila y al final del bucle llamar al método EndUpdate para actualizar el pintado del control lista.
Diseño de la Aplicación WinForms
Crear una aplicación WinForms llamada: "Comparacion_Rendimiento_Listas" en C# y cambiar de nombre al formulario por "frmLista", luego agregar los siguientes controles:
- Un Control Label llamado "lblTiempoProcesamiento"
- Un Control ListBox llamado "lstNumero"
- Cinco Controles Button llamados: "btnLlenarArreglo", "btnLlenarListaForItemsAdd",
"btnLlenarListaAddRange", "btnLlenarListaForItemsAddBU" y "btnLlenarListaAddRangeBU".
Código Fuente de la Aplicación WinForms
En el código he creado un método "ejecutarMetodo" que tiene como parámetro un método gracias al tipo "Action" lo que me permite crear una rutina que permita "medir el tiempo de procesamiento de cualquier método" para poder comparar los tiempos en cada caso, para lo cual usamos la clase "Stopwatch" de "System.Diagnostics".
Además usamos un arreglo de cadenas, el cual lo llenamos con 10000 registros para poder ser pasados al control ListBox con las diferentes técnicas.
using System.Windows.Forms;
using System.Diagnostics;
namespace Comparacion_Rendimiento_Listas
{
public partial class frmLista : Form
{
private string[] lista;
public frmLista()
{
InitializeComponent();
}
public string ejecutarMetodo(Action metodo)
{
Stopwatch oReloj = new Stopwatch();
oReloj.Start();
metodo();
oReloj.Stop();
return (String.Format("Tiempo de Procesamiento: {0:n0} msg",
oReloj.Elapsed.TotalMilliseconds));
}
private void llenarArreglo()
{
lista=new string[10000];
for (int i = 0; i < lista.Length; i++)
{
lista[i] = i.ToString();
}
}
private void btnLlenarArreglo_Click(object sender, EventArgs e)
{
lblTiempoProcesamiento.Text = ejecutarMetodo(llenarArreglo);
}
private void llenarListaForItemsAdd()
{
lstNumero.Items.Clear();
for (int i = 0; i < lista.Length; i++)
{
lstNumero.Items.Add(lista[i]);
}
}
private void btnLlenarListaForItemsAdd_Click(object sender, EventArgs e)
{
lblTiempoProcesamiento.Text = ejecutarMetodo(llenarListaForItemsAdd);
}
private void llenarListaAddRange()
{
lstNumero.Items.Clear();
lstNumero.Items.AddRange(lista);
}
private void btnLlenarListaAddRange_Click(object sender, EventArgs e)
{
lblTiempoProcesamiento.Text = ejecutarMetodo(llenarListaAddRange);
}
private void llenarListaForItemsAddConBeginUpdate()
{
lstNumero.BeginUpdate();
lstNumero.Items.Clear();
for (int i = 0; i < lista.Length; i++)
{
lstNumero.Items.Add(lista[i]);
}
lstNumero.EndUpdate();
}
private void btnLlenarListaForItemsAddBU_Click(object sender, EventArgs e)
{
lblTiempoProcesamiento.Text = ejecutarMetodo(llenarListaForItemsAddConBeginUpdate);
}
private void llenarListaAddRangeConBeginUpdate()
{
lstNumero.BeginUpdate();
lstNumero.Items.Clear();
lstNumero.Items.AddRange(lista);
lstNumero.EndUpdate();
}
private void btnLlenarListaAddRangeBU_Click(object sender, EventArgs e)
{
lblTiempoProcesamiento.Text = ejecutarMetodo(llenarListaAddRangeConBeginUpdate);
}
}
}
Ejecución y Pruebas de la Aplicación WinForms
Si ejecutamos la aplicación y damos clic al botón "Llenar Arreglo" notamos que el tiempo de procesamiento es de 2 msg (mili segundos), si damos clic al botón "Llenar Lista For Items Add" el tiempo de procesamiento es de 3306 msg (este puede variar cada vez que se da clic entre 2500 y 3500 aproximadamente), además en todo este tiempo se nota la aplicación en espera y con parpadeo.
Si damos clic al botón "Llenar Lista AddRange" o los otros 2 últimos botones (es decir las 3 formas de mejor rendimiento) notamos que el tiempo de procesamiento es de 69 msg (puede variar entre 60 y 80 msg).
Comentario Final
Después de hacer las comparaciones de rendimiento nos damos cuenta que la diferencia es extremadamente abismal, para 10000 filas el método clásico del bucle demora mas de 3000 msg mientras que las otras 3 técnicas solo 70 msg, es decir una diferencia de 3000, la cual aumenta con el aumento de los registros.
Después de esto, espero que ningún programador se olvide de colocar "BeginUpdate" antes del llenado de su lista y al final "EndUpdate" o use directamente el método AddRange.
Descarga:
Demo05_Rendimiento_Listas
Etiquetas:
Actions,
Arrays,
C#,
Comparación,
Demos,
Lduenas,
Listas,
ListBox,
Performance,
Rendimiento,
Stopwatch,
WinForms
El Libro del Día: Windows Phone 8 Recipes
El Libro del Día: 2014-06-23
Titulo: Windows Phone 8 Recipes
Autor: Lori Lalonde, David R Totzke
Editorial: Apress
Nro Paginas: 417
Capítulos:
Chapter 1: Introduction to the Windows Phone SDK
Chapter 2: Multi-Resolution Support and Basic User Interface Components
Chapter 3: Gestures
Chapter 4: Tiles and Lock Screen
Chapter 5: Background Agents and Local Notifications
Chapter 6: Appointments and Contacts
Chapter 7: Camera, Photos, and Media
Chapter 8: Maps, Location, and Routing
Chapter 9: Communications and Speech
Chapter 10: Launching and Resuming Apps
Chapter 11: Data Storage
Chapter 12: Windows Azure Mobile Services
Chapter 13: Using the Microsoft Live SDK
Chapter 14: Publishing Your App
Descarga:
WindowsPhone8_Recipes
Titulo: Windows Phone 8 Recipes
Autor: Lori Lalonde, David R Totzke
Editorial: Apress
Nro Paginas: 417
Capítulos:
Chapter 1: Introduction to the Windows Phone SDK
Chapter 2: Multi-Resolution Support and Basic User Interface Components
Chapter 3: Gestures
Chapter 4: Tiles and Lock Screen
Chapter 5: Background Agents and Local Notifications
Chapter 6: Appointments and Contacts
Chapter 7: Camera, Photos, and Media
Chapter 8: Maps, Location, and Routing
Chapter 9: Communications and Speech
Chapter 10: Launching and Resuming Apps
Chapter 11: Data Storage
Chapter 12: Windows Azure Mobile Services
Chapter 13: Using the Microsoft Live SDK
Chapter 14: Publishing Your App
Descarga:
WindowsPhone8_Recipes
Etiquetas:
Apress,
Camera,
Data Storage,
David R Totzke,
Libros,
Lori Lalonde,
Maps,
Notifications,
Recipes,
Speech,
Tiles,
Windows Phone
viernes, 20 de junio de 2014
El Libro del Día: HTML5 for .NET Developers
El Libro del Día: 2014-06-20
Titulo: HTML5 for .NET Developers
Autor: Jim Jackson II, Ian Gilman
Editorial: Manning
Nro Paginas: 416
Capítulos:
1 HTML5 and .NET
2 A markup primer: classic HTML, semantic HTML, and CSS
3 Audio and video controls
4 Canvas
5 The History API: Changing the game for MVC sites
6 Geolocation and web mapping
7 Web workers and drag and drop
8 Websockets
9 Local storage and state management
10 Offline web applications
Descarga:
HTML5_ForNET_Developers
Titulo: HTML5 for .NET Developers
Autor: Jim Jackson II, Ian Gilman
Editorial: Manning
Nro Paginas: 416
Capítulos:
1 HTML5 and .NET
2 A markup primer: classic HTML, semantic HTML, and CSS
3 Audio and video controls
4 Canvas
5 The History API: Changing the game for MVC sites
6 Geolocation and web mapping
7 Web workers and drag and drop
8 Websockets
9 Local storage and state management
10 Offline web applications
Descarga:
HTML5_ForNET_Developers
Etiquetas:
.NET,
Caching,
Canvas,
CSS,
HTML5,
Ian Gilman,
Jim Jackson II,
Libros,
Manning,
Multimedia,
MVC,
Web Storage,
Web Workers,
WebSocket
jueves, 19 de junio de 2014
Nuevo Inicio PECI .NET - Taller de Servicios WCF Avanzado
Nuevo Inicio PECI .NET
A los interesados en Entrenamiento en .NET les comunico que este Domingo 22 de Junio en horario de 8:00 am a 2:00 pm. estaré iniciando un nuevo grupo del PECI .NET (Visual Studio 2013 Developer con C#) en ISILTECH (San Isidro - Lima).
Cabe recordar que el Programa incluye mas de 8 cursos sobre .NET:
Taller de Servicios Web Avanzado
Además para los que ya llevaron conmigo este Domingo 29 de Junio de 9:00 am a 1:00 pm. tendremos el Quinto Taller sobre "Servicios Web Avanzado: WCF Services, WCF Data Services y Web API" ya saben que los interesados tienen que enviar su confirmación al correo: Luis.duenash@gmail.com y también si desean pueden ir enviando la dirección (URL) del servicio que quieren que se consuma, si es con seguridad mejor (enviar usuario y clave). El costo sigue siendo el mismo.
Saludos.
A los interesados en Entrenamiento en .NET les comunico que este Domingo 22 de Junio en horario de 8:00 am a 2:00 pm. estaré iniciando un nuevo grupo del PECI .NET (Visual Studio 2013 Developer con C#) en ISILTECH (San Isidro - Lima).
Cabe recordar que el Programa incluye mas de 8 cursos sobre .NET:
- Desarrollo de Aplicaciones con .NET Framework 4.5.1
- Desarrollo de Aplicaciones con ADO .NET, POO y LINQ.
- Desarrollo de Aplicaciones Windows Forms.
- Desarrollo de Aplicaciones Web con ASP .NET Web Forms.
- Desarrollo de Aplicaciones Web con ASP .NET, MVC, HTML5, CSS3 y JQUERY
- Desarrollo de Aplicaciones Windows con Windows Presentation Foundation.
- Desarrollo de Aplicaciones Web con WPF, XBAP y Silverlight 5.
- Desarrollo de Aplicaciones de Datos con WCF Data Services, REST y Odata.
- Desarrollo de Aplicaciones de Colaboración con Windows Workflow Foundation.
- Desarrollo de Aplicaciones para Windows Store (Opcional si alcanza el tiempo)
- Es el programa que mas cursos contempla en un entrenamiento
- 100% Practico.
- Todos los ejemplos son del día a día (lo que uno hace en el trabajo)
- No se copia y se pega, se hace todo desde cero
- El nivel es intermedio avanzado (la mayoría ofrece nivel básico - intermedio)
- Solo se enseñan Buenas Practicas de Desarrollo (sobre todo de performance)
- No se usan librerías pre fabricadas de terceros (Building Blocks), se enseñan a crearlas
- 100% con Objetos (No DataSet, DataTables, DataView)
- Modelo de Desarrollo Distribuido (En capas)
- Todo sobre Programación Asíncrona en .NET (WinForms, WebForms, MVC, WPF, WCF Services, Workflow Foundation)
- Uso intensivo de Reflection en creación de Librerías de Código, de Controles y de Formularios.
- Cientos de Libros sobre Programación
- Cientos de Videos oficiales de Entrenamiento Microsoft, etc.
Taller de Servicios Web Avanzado
Además para los que ya llevaron conmigo este Domingo 29 de Junio de 9:00 am a 1:00 pm. tendremos el Quinto Taller sobre "Servicios Web Avanzado: WCF Services, WCF Data Services y Web API" ya saben que los interesados tienen que enviar su confirmación al correo: Luis.duenash@gmail.com y también si desean pueden ir enviando la dirección (URL) del servicio que quieren que se consuma, si es con seguridad mejor (enviar usuario y clave). El costo sigue siendo el mismo.
Saludos.
Libro La Biblia de Visual Basic .NET - Preguntas de Repaso - Capítulo 6
Preguntas de Repaso: Desarrollando Aplicaciones con WPF
70. Qué clase y en que espacio de nombres
se encuentra el objeto que permite ejecutar en segundo plano cada cierto tiempo
una acción en una aplicación WPD?
1. Qué se puede hacer en WPF que no se
puede hacer en WinForms no ASP .NET?
2. Cuáles son los principales
componentes en la arquitectura de WPF?
3. Menciona 5 características de WPF.
4. Qué lenguaje de marcas usa WPF?
5. Cuántos tipos de aplicaciones se
pueden crear en WPF?
6. Menciona 5 tipos de controles de WPF.
7. Cómo se llama el contenedor principal
de controles que viene por defecto en una ventana o página en WPF?
8. Menciona otros 3 contenedores de
controles WPF.
9. Menciona el evento de inicio que
ocurre al cargar una ventana y los eventos de cierre de ventana.
10. Qué debe configurarse para que una
cierta ventana de nuestra aplicación inicie primero?
11. Qué es una aplicación WPF del
Explorador o XBAP?
12. Que tipo de seguridad tiene por
defecto una aplicación WPF del explorador?
13. Qué clase se usa para navegar entre
páginas de una aplicación del explorador?
14. Qué se debe hacer para que una
aplicación del explorador pueda acceder a archivos, cuadros de diálogo,
registro de Windows, etc.?
15. Cómo se clasifican los cuadros de
diálogo?
16. Qué tipos de cuadros de diálogo se
pueden crear en WPF?
17. Qué espacio de nombres se debe
importar para usar los diálogos de archivos de Windows?
18. Cuál es el único cuadro de diálogo
interno de WPF?
19. Cómo se llama las clases para
implementar los diálogos de abrir y guardar respectivamente?
20. Con qué método del cuadro de diálogo
de imprimir (PrintDialog) se puede imprimir cualquier contenido inclusive
gráficos?
21. Cuantas técnicas tenemos para
presentar datos en una aplicación?
22. Cuáles son las ventajas principales
de WPF en el enlace de datos con respecto a WinForms y WebForms?
23. Cuántos tipos de enlaces (Data
Bindings) existen?
24. Qué propiedad de los controles de
listas implementa el enlace complejo en WPF?
25. Menciona 3 controles que soporten
enlace complejo.
26. Qué debe hacerse para crear enlace
simple?
27. Menciona 3 controles que soporten
enlace simple.
28. Cómo se llama la clase que hace de
intermediario entre los controles y el origen de datos en WPF?
29. Menciona los métodos de
desplazamiento del objeto CollectionView.
30. Con qué propiedades del
CollectionView se verifican el desbordamiento de registro?
31. En qué evento del CollectionView
podemos programar para mostrar la posición actual del origen de datos cuando
este cambie?
32. Qué propiedades de los controles
listas permiten configurar el campo a mostrar y el campo a guardar?
33. Para qué se usa la conversión de
datos en WPF?
34. Qué interface tenemos que implementar para realizar la
conversión de datos?
35. Qué métodos hay que programar en una
clase que implemente dicha interface?
36. Cómo se aplica en el XAML una clase
creada para formatear o convertir un tipo de dato en en control enlazado?
37. Cómo se puede ordenar las columnas en
el control ListView?
38. Cómo se obtiene la cabecera a la cual
se dio clic en un ListView?
39. Cómo se crea un objeto
CollectionView?
40. Para qué se usan las plantillas de
datos en WPF?
41. Qué tipo de objeto permite crear una
plantilla de datos y qué propiedad del control enlazado debemos configurar para
aplicar el objeto creado?
42. Cómo se sincronizan 2 listas
enlazadas a datos sin necesidad de programar el filtro?
43. Qué es una plantilla jerárquica y
cómo se implementa en WPF?
44. Qué control de WPF soporta una
plantilla jerárquica?
45. Qué requisitos debe tener el origen
de datos antes de enlazar a un control que usará una plantilla jerárquica?
46. Qué control WPF presenta de forma
simple filas y columnas sin necesidad de usar plantillas?
47. Cuáles son los tipos de columnas que
puede tener el control DataGrid?
48. Qué es necesario hacer al DataGrid
para que pueda ordenar los datos al dar clic a la cabeceras de sus columnas?
49. Cuántos tipos de documentos maneja
WPF?
50. Qué control se usa para visualizar
documentos fijos como los XPS?
51. Qué controles se usan para visualizar
documentos dinámicos como los archivos de texto, xml, html, etc.
52. Qué sigifica XPS y cómo se implementa
en WPF?
53. Cómo se llama la librería que permite
trabajar con documentos XPS?
54. Qué propiedad del control
DocumentViewer es necesario configurar para enlazar un documento?
55. Menciona los 3 tipos de anotaciones
que hay en WPF.
56. Qué clase es necesario usar para
implementar anotaciones en WPF?
57. Menciona 3 métodos de la clase
AnnotationService que sirvan para crear anotaciones.
58. Menciona 3 métodos de la clase
AnnotationService que sirvan para eliminar o borrar anotaciones.
59. Cuál es la ventaja de los documentos
dinámicos?
60. Cómo se puede convertir un archivo de
Word o Excel a XPS?
61. Qué se debe hacer si se usa por
código trabajar con Word o Excel desde .NET?
62. Cuáles son las 2 clases que
implementan Multimedia en WPF?
63. Cómo se llama la librería de clases
que implementa el reconocimiento de voz en WPF?
64. Cómo se llama la clase que permite
reproducir voz?
65. Qué métodos tiene la clase SpeechSynthesizer
para reproducir la voz?
66. Qué propiedades del objeto SpeechSynthesizer
permiten controlar el volumen y la velocidad de la voz?
67. Cómo se llama la clase (control) que
permite reproducir audio y video?
68. Qué propiedad del control
MediaElement especifica la URI con el archivo de audio o video a reproducir?
69. Qué eventos ocurren al iniciar y
finalizar la reproducción usando el MediaElement?
Suscribirse a:
Entradas (Atom)