Lilypie Primer PicLilypie Primer Ticker

martes, 20 de noviembre de 2007

Recursos adicionales que acompañan a VS2008

Junto con el lanzamiento de VS 2008 se liberaron muchos recursos adicionales que nos ayudarán a dar los primeros pasos en esta nueva plataforma.

Tenemos por ejemplo: Windows Vista P2P Toolkit, Free Game Developer Toolkit, Coding4Fun Developer Toolkit, Controles gratuitos, Host gratuito...y muchos otros.

Pueden encontrar una recopilación interesante en este post.

Espero les sea de utilidad, saludos

lunes, 19 de noviembre de 2007

Ya llegaron VS2008 y .NET Framework 3.5

Hoy fueron liberadas oficialmente las versiones RTM de estos dos productos. Aún no vienen en las tradicionales cajitas que podemos comprar en una tienda, pero podemos descargarlas desde varios sitios:

Si son suscriptores de MSDN pueden descargar el producto sin costo desde el sitio de msdn.

Si no son suscriptores:

* se puede bajar versiones de prueba de 90 dias de VS Team Suite aqui.
* Una versión de prueba de 90 días de Team Foundation System está disponible aqui.
* Una versión de prueba de 90 días de VS Professional estará disponible en los próximos dias.
* Las ediciones Express (gratuitas) están disponibles aqui.

y finalmente, si solamente quieren descargar el .NET framework 3.5, pueden encontrarlo aqui.

Espero les sirvan esos links, en los próximos les haré un resumen de las principales innovaciones que trae esta nueva versión.

El Training kit de Visual Studio 2008 y .NET Framework 3.5 ya está disponible

A partir de hoy está disponible el Training Kit de VS 2008 y .NET Framework 3.5. Es un archivo comprimido de poco más de 120 Mb que contiene presentaciones en PowerPoint, Demos y Labs.

Se puede descargar de aqui

Saludos

viernes, 16 de noviembre de 2007

Crónicas del Google CodeJam (Beta)

Ayer tuve la oportunidad de participar (con muy poco éxito O0o.:| ) en la primera prueba abierta al público del concurso de programación Google CodeJam, como les comenté aqui.

Les comento como funciona, haré una comparación con TopCoder, ya que es una plataforma de concursos bastante popular (en la que participo a veces), y era donde se llevaba a cabo el CodeJam los pasados años.

En pocas palabras: La mecánica del nuevo CodeJam es TOTALMENTE diferente a la de TopCoder, las principales diferencias son:

  1. TopCoder está basado en un Applet de Java que debe ser instalado en nuestro equipo. CodeJam está basado totalmente en el navegador.
  2. TopCoder pone estrictos límites en cuanto a los lenguajes de programación que se pueden usar (Normalmente C++, C#, VB.NET y Java. También se admitía Python únicamente para los CodeJams). El nuevo CodeJam no pone restricción alguna. Los participantes pueden usar el lenguaje que prefieran, desde assembler hasta SQL :) ya les explicaré como esto es posible.
  3. En TopCoder y en otros sitios de concursos como el de la UVA, se debe enviar el código fuente. En el nuevo CodeJam NO se envia ningún código. De ahi que es posible usar cualquier lenguaje de programación.
  4. En TopCoder el tiempo empieza a correr cuando el participante abre un problema para verlo. En CodeJam el tiempo empieza a correr a partir de la hora fijada para el inicio del concurso.
  5. En TopCoder se tiene la opción de ver el código de otros participantes y eventualmente desafiar aquellos que parezcan incorrectos, ganando (o perdiendo) puntos por ello. En CodeJam esto no es posible, ya que, como ya dije, no se envía ningún código.


Ahora les cuento como funciona este nuevo CodeJam:


Solamente se necesita un navegador web, no applets, addins, ActiveX, ni nada. Solo el navegador. Se ingresa al sitio del concurso a la hora fijada (ayer fue a las 6.00PM PST, 10 de la noche en mi país (-4.00 UTC)) e inmediatamente se pueden ver los problemas. Ayer fueron 4 problemas de algoritmos de diferentes niveles de dificultad, y obviamente de diferentes puntajes. El tiempo de la competencia fue de 2 horas. (Para entonces ya tenia mucho sueño :O)

El tiempo empieza a correr a partir de la hora de inicio del concurso, y pueden visualizarse todos los problemas al mismo tiempo. A mi parecer esto beneficia a quienes participan en grupo, perjudicando a los participantes individuales. Mientas mas grande el grupo, mas ventajas. En TopCoder los participante los individuales tenemos un poco más de oportunidad.

Ahora si, vamos al corazón del asunto: Una vez visto y analizado el problema, tenemos la opción de resolverlo en cualquier lenguaje de programación, usando cualquier herramienta de desarrollo. No existe ninguna restricción.

Básicamente nuestro programa deberá leer un archivo de texto, realizar algun proceso, y generar un archivo de salida.

Les comento un problema del concurso de ayer a manera de ejemplo: Se trataba sobre sistemas de numeración, y básicamente se pedía: Dado un número N expresado en Base-X convertirlo a Base-Y. Las bases podían contener números, letras, símbolos, etc.

Entonces nuestro programa debía leer un archivo parecido a este:

100
9 0123456789 oF8
Foo oF8 0123456789
13 0123456789abcdef 01
CODE O!CDE? A?JM!.

........

La primera línea nos indica el número de entradas que tiene el archivo. 100, en este caso. Las lineas sucesivas son de la forma:

[N] [Base-X] [Base-Y]

Entonces nuestro programa deberá recorrer todas las entradas del archivo, convertir N expresado en BaseX a BaseY, e ir generando un archivo de salida parecido a este:

Case #1: Foo
Case #2: 9
Case #3: 10011
Case #4: JAM!
Case #5: I%`jEEa
Case #6: *LAs969

........

Una vez que hemos leído, analizado codificado y probado nuestro programa, deberemos bajar el archivo de entrada. A partir de ese momento tenemos un corto tiempo para enviar el archivo de salida.

Existen dos tipo de archivos de entrada para cada problema:

1. Archivos de entrada Pequeños

Típicamente 100 entradas.

Tenemos 4 minutos para enviar la solución.

Somos informados inmediatamente si nuestra solución fue considerada correcta o no.

Normalmente tiene un puntaje menor que los archivos de entrada grandes.

2. Archivos de entrada Grandes

Contienen 1000, 10000 o más entradas

Tenemos 8 minutos para enviar la solución

Solo se evalúa nuestra solución al final del concurso.

Normalmente tienen un puntaje mayor que los archivos de entrada pequeños.

Si nuestra solución no fue correcta, tenemos la opción de bajar otro archivo de entrada y enviar la nueva solución. Si esto ocurre somos penalizados con 240 segundos. El tiempo es importante en caso empate.

Existe un foro dedicado al concurso aqui

Así es entonces como funciona el nuevo CodeJam de Google. habrá que esperar las versiones definitivas para seguir participando.

miércoles, 14 de noviembre de 2007

Google CodeJam (Beta)

Recien me enteré de que Google está cambiando la modalidad de su famoso concurso de programación CodeJam. En los pasados años, CodeJam era desarrollado por TopCoder, pero a partir de este año, será Google mismo quien operativice el concurso.

Mañana Jueves 15 se desarrollará por primera vez una prueba beta abierta al público, valdría la pena inscribirse.

Se ofrecen cupones de Google Store para los 3 primeros lugares y poleras para los primeros 200.

En el formulario de registro, se les pregunta a los participantes el país de residencia, pero solo aparecen Estados Unidos y Canadá, por lo que seguramente, si llego a ganar una polera, no me la podrán hacer llegar hasta Bolivia :-(.

De todas formas estoy muy entusiasmado con participar en esto.

Pueden inscribirse aqui

martes, 13 de noviembre de 2007

Por fin llegó la quinta estrella!

Luego de muchos meses de espera, finalmente está disponible la quinta estrella del programa  Desarrollador Cinco Estrellas.

Para lograr la quinta estrella se requieren aprobar 4 exámenes: el primero es sobre ASP.NET y AJAX, el segundo sobre arquitectura, el tercero sobre WCF y el último sobre WWF.

Los materiales vienen en videos, lo que personalmente no me gusta, hubiera preferido pdfs. No pude ver los videos usando Firefox, pero no tuve problemas con Internet Explorer.

Entre los incentivos, MS ofrece un diploma impreso que será enviado por correo a todos los que logren la quinta estrella. También se ofrece una invitación para el lanzamiento de VS 2008 en cada país.

Y al primero en lograr la quinta estrella se le obsequiará una cámara web de MS. Por lo que pude ver, ya hay dos personas que tienen la quinta estrella, asi que parece ser un poco tarde para optar por ese premio :).

Finalmente, la quinta estrella no es la cima del mundo, sino que una vez alcanzada, podemos optar por tomar 3 cursos adicionales para convertirnos en Desarrolladores Gold, o 6 cursos adicionales para ser Desarrolladores Platinum. Los 15 primeros desarrolladores que alcancen el nivel Platinum serán entrevistados por MSDN.

Esas son las novedades, visiten www.dce2005.com, vean los materiales y empiecen a rendir los exámenes.

 

Exitos

C# 3.0: Variables locales implícitamente tipadas

Una innovación interesante de la versión 3 de C# es la posibilidad de que el programador quede liberado de definir el tipo de dato de cada variable dejando a criterio del compilador el inferir el tipo de la variable, en base al valor a que se inicialice la variable.

Asi por ejemplo si inicializamos una variable x con el valor 1 (uno), resulta obvio que x es una variable de tipo int. O si inicializamos y con el valor "Hola mundo", es posible afirmar que y es del tipo string. Entonces es posible que el compilador determine (infiera) el tipo de la variable que estamos declarando, liberándonos de esta tarea.

C# 3 incluye la palabra reservada var que le indica al compilador que debe inferir el tipo de la variable que estamos declarando. Por ejemplo, a partir de la instrucción:

var x = 1;
var y
= "Hola mundo";

el compilador puede inferir que x es de tipo int. y que la variable y es del tipo string.

Es un error pensar que var declare una variable tipo Variant, es decir, una variable que puede cambiar su tipo dinámicamente. var simplemente nos evita tener que especificar el tipo de una variable al momento de declararla. Para el CLR cada variable tiene un y solo un tipo de dato, por tanto, el siguiente código no compila:

var x = 1;
x
= "Hola"; // Esta linea no compila

porque el compilador determinó que x es del tipo int, por lo que es ilegal asignarle un valor del tipo string.

El valor de inicialización es obligatorio cuando se declara una variable con var, asi el siguiente código no compila:

var x; // Esta linea no compila

porque el compilador necesita un valor a partir del cual pueda inferir el tipo de dato.


También es posible inferir el tipo de los arrays usando var, asi:

var numeros = new[] {1,2,3};

a partir de new[], el compilador sabe que estamos declarando un Array, y a partir de la lista de valores puede determinar el tipo de sus elementos.

Finalmente var también puede ser usada para declarar tipos complejos como listas o diccionarios, asi:

var lista = new List<int>();
var diccionario
= new Dictionary<int, string>();

lunes, 12 de noviembre de 2007

.NET Framework 3: Expresiones de inicialización de Objetos

Otra sencilla pero muy útil innovación del .NET Framework 3.0 son las expresiones de inicialización de objetos.

Si, por ejempo, tenemos la clase Persona con las propiedades Código, Nombre, FechaDeNacimiento y Estado, y queremos inicializar sus propiedades al momento de instanciarla, estábamos obigados a crear un constructor como este:

public Persona(int codigo, string nombre,
DateTime fechaDeNacimiento,
int estado)
{
this.Codigo = codigo;
this.Nombre = nombre;
this.FechaDeNacimiento = fechaDeNacimiento;
this.Estado = estado;
}

Entonces teniamos la posibilidad de invocar al constructor asi:


Persona p = new Persona(10, "Juan Perez",
new
DateTime(1980, 12, 10), 1);


con lo que la instancia es creada y sus propiedades inicializadas.

Ahora, con la versión 3 del .NET Framework tenemos la posibilidad de usar una forma alternativa que nos evita tener que implementar un constructor como el anterior. Esta nueva funcionalidad se implementa mediante las Expresiones de Inicialización de Objetos, que toman la siguiente forma:


Persona p = new Persona { Codigo=10,
Nombre
="Juan Perez", Estado=1 };


Es importante notar que nuestra clase debe implementar un constructor sin parámetros.

viernes, 9 de noviembre de 2007

Innovaciones del .NET Framework 3: Propiedades implementadas automáticamente

Otra innovación del .NET Framework 3 es la posibilidad de implementar automáticamente las propiedades, reduciendo la cantidad de código que debemos escribir.

En muchos casos implementamos propiedades triviales asi:

private string telefono;

public string Telefono
{
get { return telefono; }
set { telefono = value; }
}

En este caso get y set tienen implementaciones triviales, ya que get simplemente devuelve el valor de telefono y set asigna el valor recibido en value al campo telefono. Antes estábamos obligados a 1) declarar un campo privado telefono, 2) implementar get y set a mano.

Ahora, el .NET Framework 3 nos ahorra todo ese trabajo, ya que podemos reemplazar todo el código anterior por:

public string Telefono { get; set;}

Ya no tenemos la necesidad de declarar un campo privado ni de implementar get y set.

También tenemos la posibilidad de utilizar modificadores como private para get y set, asi:

public string Telefono { get; private set;}

Asi logramos tener una propiedad de solo lectura.

Esta sencilla innovación nos evita mucho del trabajo repetitivo, y nos permite concentrarnos en la implementación de aquellas propiedades que sí requieren procedimientos más complejos. Una característica muy bienvenida.


jueves, 8 de noviembre de 2007

.NET Framework 3: Extension Methods

Una innovación interesante de la versión 3.0 del .NET Framework son los denominados Extension Methods.

Para entender el concepto, tomemos la clase System.String. Esta clase tiene una gran cantidad de métodos que nos permiten realizar muchas operaciones sobre las cadenas. Sin embargo, en ocasiones he necesitado invertir una cadena, y después de escarbar por un buen rato, tuve que aceptar resignado que esta clase no implementa un método que permita realizar esta operación.

Analicemos un par de alternativas para solucionar este problema.

1. Crear una clase derivada de String e implementar un método Reverse() en ella. Esta opción no es viable en este caso concreto porque la clase String está sellada (sealed) por lo que no admite clases derivadas.

2. Implementar una biblioteca que incluya un método ReverseString y pasarle la cadena como parámetro, asi:

public class MyLib
{
public static string ReverseString(string s)
{
// el código aqui.
}
}

Entonces invocaríamos al método de esta manera:

string rev = MyLib.ReverseString("Cadena a invertir"');

Esta alternativa cumple con el propósito, pero es algo incómoda de usar, sería mucho más conveniente contar con el método Reverse() implementado directamente en la clase String.

Aqui es donde los Extension Methods hacen su aparición, ya que permiten añadir funcionalidades a un tipo existente del CLR sin necesidad de crear una clase derivada ni de recompilar el tipo original. Entonces un Extension Method nos permitiría hacer una llamada como esta:

string rev = miCadena.Reverse();

o incluso:
string rev = "Cadena a invertir".Reverse();
Veamos entonces como se implementa un Extension Method:

public static class MisExtensiones
{
public static string Reverse(this string s)
{
string buffer = "";
foreach (char c in s.ToCharArray())
{
buffer
= c.ToString() + buffer;
}
return buffer;
}
}

Este código nos permite hacer algunas precisiones respecto a la forma de implementar los Extension Methods:

  • Deben ser definidos dentro de una clase estática.
  • Deben ser declarados estáticos.
  • Toman como parámetro el tipo que se quiere extender precedido de la palabra reservada this.

Nada más, cumpliendo esos pocos requisitos podemos extender cualquier clase del .NET Framework.

Veamos un ejemplo interesante, tomado del este post del blog de ScottGu:


public static bool In(this object o, IEnumerable c)
{
foreach (object i in c)
{
if (i.Equals(o))
{
return true;
}
}
return false;
}

Este método extiende la clase Object y nos permite saber si un objeto está contenido dentro de una colección. Invocamos el método asi:

bool isInArray = miObjeto.In(miColeccion);

Lo más interesante es que al extender una clase, la nueva funcionalidad también estará disponible en todas sus clases derivadas. Entonces al aplicar esta técnica en las clases base adecuadas, podremos extender el .NET Framework de forma limpia, ordenada y transparente.

miércoles, 7 de noviembre de 2007

A registrar minombre@live.com

Desde ayer ya podemos crear cuentas de correo con el dominio @live.com. Hay que apurarse porque los nombres cortos se terminan rapido, y en lugar de marcos@live.com tendremos que conformarnos con algo como marcost34.t25@live.com :D:D


La página en que pueden crear su(s) cuenta(s) es esta.

Saludos.

Linq: Nunca es tarde para aprender

Sin duda, debería haberme subido al tren de Linq hace muchísimo tiempo pero mas vale tarde que nunca. Hoy me propongo iniciar mi aprendizaje de Linq, y estaré posteando a diario mis avances, con la esperanza de que lo que escriba aquí le sea útil a alguien que, aunque tarde como yo, quiere iniciarse en esta tecnología.

Estoy usando el libro "Linq for Visual C# 2005" de Fabio Claudio Ferracchiati, un libro de poco más de 170 páginas, que nos servirá por lo menos para dominar los rudimentos, para luego pasar a cosas más avanzadas.

Primero lo primero: Necesitamos datos que podamos consultar.


Linq puede consultar datos de diversas fuentes como objetos en memoria (Linq to Objects), bases de datos SQL, Ficheros XML entre otros. Empezaré usando Linq To Objects hasta dominar la sintaxis y luego (pronto, espero) pasaré a experimentar con las otras fuentes de datos.


Empecemos definiendo una sencilla clase Persona:


public class Persona
{
private
int id;

private
int idRol;

private
string apellido;

private
string nombre;

public string Apellido
{
get { return apellido; }
set { apellido = value; }
}

public string Nombre
{

get { return nombre; }

set { nombre = value; }

}

public
int IdRol
{

get { return idRol; }

set { idRol = value; }

}

public int Id
{
get { return id; }
set { id = value; }
}

public Persona(int id, int idRol, string apellido, string nombre)
{
this.Id = id;
this.IdRol = idRol;
this.Apellido = apellido;
this.Nombre = nombre;
}

}

Posteriormente extenderemos esta clase y añadiremos otras, pero por el momento nos sirve tal cual está.

Para que nuestros objetos puedan ser consultados mediante Linq, deben implementar la interface IEnumerable, por lo que podemos crear una lista de Personas y aprovechamos para poblarla con algunos elementos:

personas= new List<Persona>();
personas.Add(new Persona(1, 1, "Anderson", "Brad"));
personas.Add(new Persona(2, 2, "Gray", "Tom"));
personas.Add(new Persona(3, 2, "Perez", "Juan"));
personas.Add(new Persona(3, 3, "Morales", "Pedro"));


Ya estamos listos para empezar a consultar los datos usando Linq. Una consulta realmente simple es:

var query = from p
in personas
select
p;

Hay mucho que explicar en esta simple consulta.

  1. var: El .NET Framework incluye un nuevo tipo de datos denominado var, que significa Variant. Es una variable local implícitamente tipada, o sea que adopta un tipo determinado de acuerdo al contexto.
  2. from p in personas select p: Si manejamos SQL notaremos la similitud inmediatamente, pues en SQL escribiríamos SELECT * FROM Personas. En Linq primeramente especificamos a (o las) fuentes de las que extraeremos nuestros datos, en este caso "personas" que es una lista. En algún lugar leí que se decidió poner el datasource (la claúsula from) en primer lugar (a diferencia de SQL) con el fin de dar soporte a cosas tales como IntelliSense, imagino que también existen otras razones. La variable "p" representa un objeto dentro de la colección "personas".
  3. Select p: Esta claúsula equivale a SELECT * de SQL, es decir: "Recuperar todos los campos".

Asi de fácil, con esas 3 lineas, obtenemos todos los elementos de la colección personas y asignamos el resultado a la variable query.

Podemos ver los resultados de nuestra consulta asignando la variable query que hemos obtenido a un bindingSource, asi:

bindingSource1.DataSource = query;

entonces podremos visualizar inmediatamente los resultados en un DataGridView, por ejemplo. No olvidar fijar la propiedad AutoGenerateColumns a true.

Un par de consultas más, a manera de ejemplo:

var query = from p
in personas

select new { p.Apellido, p.Nombre};

Esta consulta nos permite seleccionar únicamente los campos que nos interesan, en este caso, el apellido y el nombre.

Por último,

var query = from p
in
personas
where p.Id == 1
select new { p.Apellido, p.Nombre };

Como se puede suponer, la cláusula where nos permite seleccionar únicamente aquellos elementos que cumplen una determinada condición. Podemos utilizar cualquier combinación de expresiones booleanas que el .NET framework nos permita, eso nos permitirá construir consultas muy complejas.

Hasta aquí llegamos con esta breve introducción.

martes, 6 de noviembre de 2007

Visual Studio 2008 se lanza a finales de noviembre

Pues si... En el evento de Microsoft TechEd Developers 2007 en Barcelona se anunció que Visual Studio 2008 y la versión 3.5 del .NET Framework estarán disponibles a finales de este mes. Algunos meses antes de la fecha inicialmente fijada que era febrero 2008.

Asi que ya no falta mucho para tener entre nosotros la version final de esta herramienta. Es de esperar de MS prepare alguna promoción como la que se hizo en el programa Desarrollador cinco estrellas cuando VS 2005 fue lanzado, lo que nos permitió a muchos desarrolladores contar con el programa en forma totalmente gratuita. Habrá que estar atentos a ver que noticias nos tienen.

Saludos.

jueves, 25 de octubre de 2007

Windows Vista User Experience Guidelines disponible en PDF

MS ha puesto a nuestra disposición (por demanda popular, según dicen) un documento en formato PDF que contiene los lineamientos del diseño de la Experiencia del Usuario de Windows Vista.

Este documento es básicamente un volcado del sitio de MS que trata sobre los lineamientos del diseño de la UI, de modo que las aplicaciones se integren mejor con la interfaz Aero.

Contiene más de 600 páginas y pesa casi 30 Mb, pero vale la pena porque está lleno de ilustraciones y consejos que nos permitirán crear aplicaciones mejor integradas este nuevo SO

Pueden encontrar el documento en cuestion aqui,

miércoles, 24 de octubre de 2007

MS Tester Center

Hace algunos dias se anunció con muchas fanfarrias el lanzamiento de un nuevo sitio dentro de MSDN llamado Tester Center.

El objetivo de este nuevo sitio es conformar una comunidad donde los probadores (testers) de software puedan compartir conocimientos y aprender de otros acerca de los procedimientos de pruebas, procesos, herramientas que se utilizan, etc. Prometen que tendrán videos, artículos, blogs, foros y mucha más información.

Actualmente, no tiene mucho contenido, únicamente un par de artículos interesantes. Seguramente con el paso del tiempo se convertirá en una valiosa herramienta, pero por el momento hay sitios mucho más completos.

Pueden visitar el Tester Center aqui

jueves, 4 de octubre de 2007

Código fuente del .NET Framework estará disponible en VS2008

Scott Guthrie nos anuncia que la versión final de VS 2008 incluirá la opción de descargar el código fuente de las librerias del .NET Framework con documentación y soporte de depuración incluídas.

Esto será de gran ayuda para lograr un mejor conocimiento de la estructura y el funcionamiento interno del framework, logrando que nuestras aplicaciones lo aprovechen de la mejor manera.

Pueden encontrar el post de ScottGu aqui

viernes, 21 de septiembre de 2007

Se lanza xUnit.net

Ayer fue anunciado el lanzamiento de un nuevo framework de pruebas unitarias para la plataforma .NET: xUnit.NET.

A decir de sus creadores (los mismos de NUnit), esta nueva herramienta implementa lecciones aprendidas en varios años de uso de NUnit.

Entre las principales diferencias entre NUnit y xUNIT.NET se citan:

  • Instanciación de objetos para cada Test Method.
  • Se eliminan los atributos [SetUp] y [TearDown]
  • Se elimina el atributo [ExpectedException] , y se lo reemplaza por Assert.Throws()
  • Funcionalidad Tipo Aspect
  • Varios atributos han sido eliminados:[TestFixture], [Ignore], [SetUp], [TearDown], [ExpectedException], [TestFixtureSetup], [TestFixtureTearDown].
  • Uso de genéricos en los Asserts.
  • Uso de delegados anónimos.
Se incluye una versión de consola y también se integra con Visual Studio 2005 mediante TestDriven.NET.

La página del proyecto es http://www.codeplex.com/xunit

El blog en que uno de los autores anuncia el lanzamiento del producto se encuentra en http://jamesnewkirk.typepad.com/posts/2007/09/announcing-xuni.html

Aprovecharé estos dias para probar este framework y les comentaré los resultados.

jueves, 19 de julio de 2007

La Librería de imágenes de Visual Studio 2005

Visual Studio incorpora una librería de gráficos, que incluye Bitmaps, Iconos y Animaciones que pueden darles a nuestras aplicaciones una apariencia más profesional e integrada con el Sistema Operativo.

Esta librería se instala junto a VS2005, pero se encuentra muy bien escondida en un oscuro directorio y comprimida en formato zip.

La librería en cuestión se instala por defecto en \Program Files \Microsoft Visual Studio 8\Common7\VS2005ImageLibrary\VS2005ImageLibrary.zip

Al descomprimir este archivo nos encontraremos con 3 carpetas:

Animations. Contiene películas en formato avi y gifs animados

Bitmaps. Contiene bitmaps que son utilizados en menús, toolbars, diálogos, TreeViews, ListViews, botones, etc. Los bitmaps vienen en 16 colores y colores de 24 y 32 bits.

Icons. Muchos iconos típicos de WinXP y Win9x

En cada carpeta se puede encontrar un archivo html con información de las imágenes. A continuación unos screenshots.



martes, 17 de julio de 2007

Ejecutar un instalador .msi que requiere .Net Framework 1.1 bajo .Net Framework 2.0

Si tenemos un archivo de Windows Installer (archivo .msi) que requiere el .Net Framework 1.1 pero en nuestro equipo solo tenemos instalado el .Net Framework 2.0, la aplicación se rehúsa a instalarse y simplemente obtenemos un mensaje como este:

“This setup requires the .NET Framework versión 1.1.4322. Please install the .NET Framework and blah blah blah…”

Una solución sería descargar e instalar el .Net Framework 1.1 como lo exige nuestra aplicación, con la consiguiente pérdida de tiempo y espacio en nuestro disco. Afortunadamente existe otra solución más directa: Modificar el archivo .msi

Ocurre que los archivos de instalación tienen instrucciones para verificar que se cumplan ciertas condiciones antes de ejecutarse, entonces la solución es quitar esas instrucciones con un editor.

Existe una herramienta de Microsoft llamada Orca que sirve para editar los archivos de instalación (.msi, .msm, .psp, y .msp). Esta herramienta está incluida en el Windows SDK, que puede ser descargado libremente. Se puede encontrar más información sobre la herramienta, así como el sitio de descarga aqui.

Una vez instalada la herramienta, abrimos el archivo .msi que queremos editar y buscamos la tabla Custom Action en el panel de la izquierda.


Entonces ubicamos las acciones DIRCA_CheckFx y VSDCA_VsdLaunchConditions y las eliminamos.

Nada más, guardamos el archivo y ya debería funcionar sin mayores protestas.

Ojalá les sea útil.

lunes, 16 de julio de 2007

Un ComboBox que muestra los Fonts instalados en el equipo

En este post compartiré con ustedes una sencilla forma de implementar un ComboBox (o un ListBox) que muestra los tipos de letra que tenemos instalados en el equipo, asi:

Vamos directo al código. Primero necesitamos crear en nuestro Form un campo privado que almacenará los tipos de letra instalados en el equipo y servirá como DataSource de nuestro ComboBox.

private InstalledFontCollection installedFonts = new InstalledFontCollection();

A continuación debemos configurar las Propiedades DataSource y DisplayMember del ComboBox, haremos esto en el constructor del Form, asi:

private void Form1_Load(object sender, EventArgs e)
{
comboBox1.DataSource = installedFonts.Families;
comboBox1.DisplayMember =
"Name";
comboBox1.DrawMode =
DrawMode.OwnerDrawFixed;
}

También hemos fijado la Propiedad DrawMode a OwnerDrawFixed, algo que también podríamos haber hecho usando el diseñador gráfico.

Finalmente la verdadera acción tiene lugar en el evento DrawItem del Combo, que implementamos así:

private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
FontFamily family = installedFonts.Families[e.Index];
FontStyle style = FontStyle.Regular;
if (!family.IsStyleAvailable(style))
style = FontStyle.Bold;
if (!family.IsStyleAvailable(style))
style = FontStyle.Italic;
Font fnt = new Font(family, 10, style);
Brush brush;
if (e.State == DrawItemState.Selected)
{
brush = new SolidBrush(Color.White);
}
else
{
brush = new SolidBrush(comboBox1.ForeColor);
}


e.DrawBackground();

e.Graphics.DrawString(family.GetName(0),
fnt, brush, e.Bounds.Location);

}

Quizás las líneas en que vamos modificando la variable style merezcan una explicación. Ocurre que no todos los Fonts soportan todos los estilos, así que si no nos aseguramos de que el Font que vamos a utilizar soporte un determinado estilo terminaremos obteniendo una linda Excepción.

Eso es todo, ya tenemos un ComboBox que muestra como lucen nuestras fuentes, haciendo que nuestras aplicaciones sean un poquito más amigables.

martes, 3 de julio de 2007

Arrastrar y mover un Control

Update 2008-09-16.
Pueden descargar el código de este post aqui


En este post expondré una sencilla forma de mover un control dentro su contenedor; algo que no es muy complicado en realidad, pero que tampoco es lo más intuitivo del mundo.

Para lograrlo haremos uso de los eventos MouseDown, MouseUp y MouseDown, implementados en la clase Control, por lo que el código aquí expuesto nos servirá para mover cualquier objeto derivado de dicha clase.

Para empezar creamos un formulario y añadimos un control cualquiera. Para este ejemplo utilizaré un simple Label.

Ahora necesitamos declarar un campo privado del tipo booleano que nos indique si actualmente estamos arrastrando el control.

private bool isDragging = false;

Entonces nos valemos de los eventos MouseDown y MouseUp de nuestro Label para fijar el valor del campo isDragging, asi:

private void label1_MouseDown(object sender, MouseEventArgs e)

{
isDragging = true;

}

private void label1_MouseUp(object sender, MouseEventArgs e)
{

isDragging = false;
}

Ahora si, la verdadera acción ocurre en el evento MouseMove de nuestro Label:

private void label1_MouseMove(object sender, MouseEventArgs e)
{

Control ctrl = sender as Control;
if (isDragging)

{

Point p1 = ctrl.PointToScreen(e.Location);

Point p2 = ctrl.Parent.PointToClient(p1);

ctrl.Location = p2;

}

}

Lo único novedoso de este código es el uso de los métodos PointToScreen y PointToClient que transforman las coordenadas a coordenadas de Pantalla y a coordenadas de Control respectivamente.

Nada más, con estas pocas líneas ya podemos permitir a nuestros usuarios acomodar los controles a su conveniencia, algo muy útil para diseñar formularios, reportes, interfaces de usuario y otras cosas por el estilo.

miércoles, 20 de junio de 2007

La Clase OperatingSystem

En uno de los foros en que participo, un colega preguntaba cómo se puede obtener la versión del Sistema Operativo que estamos ejecutando.

Se puede obtener una referencia a la versión del SO que estamos ejecutando con el siguiente código:

OperatingSystem os = Environment.OSVersion;

Entonces, a través de las propiedades de la clase OperatingSystem podemos acceder a la siguiente información:

os.Platform: La plataforma. Ej. Win32, Win32NT,....

os.VersionString: Una cadena con la información completa de nuestro SO
Ej: "Microsoft Windows NT 5.2.3790 Service Pack 2"

os.Version: La versión del SO. Las propiedades que tiene son:
Major, MajorRevision, Minor, MinorRevision y Revision

os.Version.ToString() nos devuelve una cadena de tipo "5.2.3790"

os.ServicePack: Una cadena que muestra la versión del Service Pack instalado.
Ej: "Service Pack 2"

Seguramente esta información le será útil a más de uno, saludos

martes, 19 de junio de 2007

Averiguar la ruta en que se ejecuta el programa

Hola a todos. Aun sigo sin poder conseguir mucho tiempo libre, pero aprovecho ahora para iniciar una serie de posts en que compartiré con ustedes varios snippets muy útiles.

Empiezo la serie con un snippet que permite obtener la ruta (path) desde la que se ejecuta el ejecutable de nuestra aplicación.

string appPath = Application.ExecutablePath;

o también

string appPath = System.Reflection.Assembly.GetExecutingAssembly().Location
Saludos

miércoles, 6 de junio de 2007

El retorno...

Aqui estoy otra vez.. con una explicación al prolongado silencio.

Abril y Mayo fueron meses muy ajetreados para mí.

Abril por todas las actividades asociadas con la Semana Santa y mayo por todos los preparativos de mi matrimonio. Asi es... finalmente me casé, y ahora estoy muy feliz, adaptándome al nuevo estilo de vida :)

Como ya pasaron todos los ajetreos, a partir de ahora escribiré de forma más frecuente, asi que no olviden visitarme.

Saludos.

jueves, 5 de abril de 2007

Una pausa obligada...

Estoy cooperando en las actividades de mi iglesia con motivos de la Semana Santa, asi que entre esa actividad y el trabajo de tiempo completo no he podido encontrar un tiempito para postear en este blog.

Con toda seguridad volveré a publicar cosas interesantes a partir de la subsiguiente semana.. asi que.. un poco de paciencia.

Saludos y que pasen una buena Semana Santa

lunes, 26 de marzo de 2007

Un ComboBox con imágenes

Update: Actualizado en www.MiCodigo.info

En este post les mostraré una forma de incluir imágenes al lado de cada elemento de un ComboBox , asi:





Para ello necesitaremos un Windows Form en el que incluiremos un control ComboBox y un Componente ImageList.



En el ImageList cargamos las imágenes que queremos mostrar. En lo posible, el tamaño de estas imágenes debe ser de 16x16.





Ahora necesitamos crear una clase muy simple que nos permita almacenar la etiqueta de los elementos del Combo y el índice de la imagen en el ImageList.



class ComboItem

{
private int _imageIndex;
private string _etiqueta;

public string Etiqueta
{
get { return _etiqueta; }
set { _etiqueta = value; }
}

public int ImageIndex
{
get { return _imageIndex; }
set { _imageIndex = value; }
}

public ComboItem(string etiqueta, int imageIndex)
{
this.Etiqueta = etiqueta;
this.ImageIndex = imageIndex;
}

public override string ToString()
{
return Etiqueta;
}
}

Nada más, ahora lo único que tenemos que hacer es añadir instancias de la clase ComboItem en nuestro comboBox, podemos hacerlo en el evento Load asi:


private void Form1_Load(object sender, EventArgs e)
{
comboBox1.Items.Add(new ComboItem("Aumentar", 0));
comboBox1.Items.Add(new ComboItem("Disminuir", 1));
comboBox1.Items.Add(new ComboItem("---------", -1));
comboBox1.Items.Add(new ComboItem("Editar", 2));
}

Nótese que el tercer item tiene el índice -1. Usaremos -1 para indicar que un elemento no tiene una imagen asociada.


Ahora si, la verdadera acción:


Primero debemos modificar la propiedad DrawMode del ComboBox, y fijarla en OwnerDrawFixed.


Y finalmente añadimos un manejador para el evento DrawItem del ComboBox, asi:



private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
ComboItem item = comboBox1.Items[e.Index] as ComboItem;
e.DrawBackground();
if (item.ImageIndex >= 0 && item.ImageIndex < imageList1.Images.Count)
e.Graphics.DrawImage(imageList1.Images[item.ImageIndex], new PointF(e.Bounds.Left, e.Bounds.Top));
e.Graphics.DrawString(item.Etiqueta, e.Font, new SolidBrush(e.ForeColor), new PointF(e.Bounds.Left + imageList1.ImageSize.Width + 1, e.Bounds.Top));
}




Eso es todo, nuestro ComboBox ahora muestra su imagen asociada a su izquierda.

En un siguiente post, mostraré cómo se puede crear un control derivado de ComboBox que incluya todo este procedimiento de modo que sea reutilizable.

jueves, 22 de marzo de 2007

Patrones de Diseño: Singleton

Este post marca el inicio de una serie que cubrirá los 23 patrones de diseño definidos en el libro "Design Patterns: Elements of Reusable Object Oriented Software" de Gamma, Helm, Johnson y Vlissides, más conocidos como  La Banda de los Cuatro (Gang of Four o GoF).

Empezaré con uno de los patrones más fáciles de implementar: el Patrón Singleton.

Según el libro de Dessign Patterns, el propósito del patrón Singleton es:

Asegurarse que una clase tiene solamente una instancia, y provee un punto de acceso global a ella.

Conozco dos formas de implementar este patrón:

  • Hacer privados los constructores de la clase e implementar un método estático que cree las instancias.
  • Implementar los mecanismos de control en los constructores de la clase.

Se recomienda usar el primer enfoque, porque es preferible tener un método especializado que implemente el patrón Singleton y dejar que el constructor haga las cosas que se supone que debe hacer: Inicializar las propiedades del objeto.

A manera de ejemplo, imaginemos que estamos creando un juego de Pacman, es lógico pensar que en nuestro juego debe existir UN y solo UN Pacman, entonces es una buena oportunidad para implementar un patrón Singleton, asi:

 

class Pacman
{
#region Patron Singleton
private static Pacman instancia = null;
public static Pacman Create()
{
if (instancia == null)
instancia = new Pacman();
return instancia;
}
#endregion

private Pacman()
{
// Inicializar Objeto
}
}

Podemos ver que hemos declarado el constructor como private de modo que no sea accesible desde el exterior de la Clase.

La implementación del patrón Singleton es bastante directa. Toda la acción ocurre en el método Create que inspecciona el valor de la variable instancia para determinar si debe crear o no una instancia de la Clase Pacman. Si no existe (si instancia == null), entonces se crea una nueva instancia y la almacena en el campo instancia. Si ya existe una instancia, entonces esa instancia será devuelta

Entonces, si intentamos crear una instancia de la clase Pacman usando el constructor, de esta manera:

Pacman p = new Pacman(); 

Obtenemos un error pues el constructor solo es accesible desde el interior de la Clase.

La única forma de obtener nuestro Pacman, es mediante el método Create asi:

Pacman p = Pacman.Create(); 

Una posible variante del método Create, es hacer que se lance una excepción cundo se intente crear más de una instancia.

 

 


martes, 20 de marzo de 2007

ALBI abrió sus puertas

Como se habia anunciado, ayer lunes 19 la Academia Latinoamericana de Business Intelligence (ALBI) abrió sus puertas. Se trata de otra propuesta de capacitación gratuita por parte de Microsoft.

Los objetivos de la Academia son:

Enseñar a optimizar la toma de decisiones en un entorno altamente competitivo.


Ayudar a comprender la necesidad de analizar la información en tiempo y forma para tomar las decisiones adecuadas, que aseguren a la compañía llegar a los objetivos planificados.

La ALBI sigue los mismos lineamientos de los otros programas de capacitación de MS, como el Desarrollador 5 estrellas, el Profesional 5 Estrellas o la Academia NetProtector. Es decir que es un programa en que el estudiante consigue recompensas a medida que avanza en el curso. En el caso de la ALBI, el estudiante obtiene una medalla por cada nivel que supera.

El programa completo consta de 4 niveles, que van del Nivel 0 al Nivel 3. El Nivel 0 se alcanza al inscribirse al programa, obteniendo asi la primera medalla (mas facil imposible)

El primer nivel (que es el único disponible actualmente) se centra en crear soluciones basadas en OLAP, utilizando la herramienta Analysis Services de SQL Server.

El contenido del primer nivel es el siguiente:

  • Unidad 1. Qué es Business Intelligence?
  • Unidad 2. Definiendo Soluciones OLAP
  • Unidad 3. Diseñando una Solución OLAP
  • Unidad 4. Construyendo una Solución OLAP
  • Unidad 5. Implementando cubos OLAP

Entonces los animo a inscribirse, aqui les dejo el link.

http://www.mslatam.com/latam/technet/albi

viernes, 16 de marzo de 2007

Capítulo II: XBindingList se vuelve ordenada

En el anterior post, vimos como crear la clase XBindingList e implementamos la característica de búsqueda.

Ahora continuaremos con la implementacion de XBindingList añadiendo la opción de ordenación.

Como vimos anteriormente, la forma de añadir prestaciones a una clase derivada de BindingList es solapando métodos y propiedades. Para el caso específico de la ordenación, trabajaremos con las propiedades SupportsSortingCore, SortPopertyCore, SortDirectionCore, IsSortedCore y el método ApplySortCore

Primero debemos informar al mundo que nuestra colección soporta ordenación, para ello solapamos la propiedad SupportSortingCore, asi:


protected override bool SupportsSortingCore
{
get
{
return true;
}
}


Ahora debemos solapar las tres propiedades en las que almacenamos el estado actual de la ordenación: IsSortedCore, SortDirectionCore y SortPropertyCore.


IsSortedCore nos sirve para determinar si la colección está actualmente ordenada o no.


SortDirectionCore determina el sentido de la ordenación. Los posibles valores son Ascending y Descending.


SortPropertyCore determina la propiedad que sirve de criterio para la ordenación.


Entonces solapamos esas tres propiedades asi:



private bool _isSorted;
private ListSortDirection _sortDirection;
private PropertyDescriptor _sortProperty;
 
protected override bool IsSortedCore
{
get
{
return _isSorted;
}
}

protected override ListSortDirection SortDirectionCore
{
get
{
return _sortDirection;
}
}

protected override PropertyDescriptor SortPropertyCore
{
get
{
return _sortProperty;
}
}

Simplemente hemos definido tres campos privados que almacenarán el estado, y hemos solapado las propiedades para accesarlos.


Ahora implementamos el mecanismo de búsqueda. Para ello debemos solapar el método ApplySortCore, asi:


protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
{
_sortProperty = prop;
_sortDirection = direction;

if (this.Count == 0)
return;


SortComparer <T> comparer = new SortComparer(prop, direction);

(this.Items as List<T>).Sort(comparer);


_isSorted = true;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));

}


Este método recibe dos parámetros: prop que es la propiedad que actuará como el criterio de ordenación, y direction que es el sentido de la orientación, que puede ser acendente o descendente.


A continuación creamos una instancia de la clase SortComparer que será usado por el mecanismo de ordenación para hacer las comparaciones. la clase SortComparer está definida asi:


public class SortComparer<T> : IComparer<T>
{
private ListSortDirection _sortDirection = ListSortDirection.Ascending;
private PropertyDescriptor _propDescriptor = null;

public SortComparer(PropertyDescriptor propDescriptor, ListSortDirection sortDirection)
{
this._propDescriptor = propDescriptor;
this._sortDirection = sortDirection;
}

int IComparer<T>.Compare(T x, T y)
{
object xValue = _propDescriptor.GetValue(x);
object yValue = _propDescriptor.GetValue(y);
return CompareValues(xValue, yValue);
}

private int CompareValues(object xValue, object yValue)
{
int retValue = 0;
if (xValue is IComparable)
retValue = (xValue as IComparable).CompareTo(yValue);
else if (yValue is IComparable)
retValue = (yValue as IComparable).CompareTo(xValue);
else if (!xValue.Equals(yValue))
retValue = xValue.ToString().CompareTo(yValue.ToString());

if (_sortDirection == ListSortDirection.Descending)
retValue *= -1;
return retValue;
}
}

La clase SortComparer implementa la interfaz IComparer, siendo el método CompareValues el encargado de realizar todo el trabajo pesado: las comparaciones.

La línea (this.Items as List<T>).Sort(comparer) es donde realmente la ordenación tiene lugar. Simplemente invocamos al método Sort de la clase List<T> pasando como parámetro la instancia de la clase SortComparer que acabamos de crear.

Eso es todo, estamos listo para probar nuestra implementación. La misma aplicación que creamos anteriormente para probar lá búsqueda nos servirá para probar la ordenación. Con un simple click en la cabecera de las columnas podremos ver la ordenación en funcionamiento.


Con la implementación actual, XBindingList se comporta bastante bien cuando de ordenar se trata, pero para completar el ejercicio, debemos implementar también el método RemoveSortCore que es invocado cuando queremos dejar de aplicar la ordenación. Ahi se complica el panorama porque debemos guardar el orden (o desorden) original para que pueda ser restaurado luego. ...pero ese será tema de otro post.


Siguiente Capítulo: XBindingList se vuelve selectiva.

martes, 13 de marzo de 2007

El prefijo sp_: Usar o no usar... esa es la cuestión

Dando un vistazo a los procedimientos almacenados que incorpora SQL Server, nos damos cuenta que sus nombres siguen una misma convención: Todos empiezan con 'sp_', asi tenemos sp_configure, sp_tables sp_columns, sp_help, sp_who, etc.

Nuestro primer instinto nos sugiere que 'sp' significa 'Stored Procedure', por tanto pensamos que lo mejor que podemos hacer es seguir la misma convención y comenzamos a crear procedimientos almacenados con nombres como sp_factura_insert, sp_empleado_delete, sp_salario_select, etc.

A primera vista parece que todo funciona adecuadamente, creamos los procedimientos, los ejecutamos sin problemas y obtenemos los resultados esperados.

Ahora.. la cruda realidad: Usar el prefijo sp_ para nombrar nuestros procedimientos almacenados NO es la mejor idea.

La razón es muy simple: SQL Server reconoce el prefijo sp_ como 'System Stored Procedure', es decir, un procedimiento almacenado de Sistema.

Esa caracter'istica influye en la estrategia que SQL Server utiliza para localizar el SP cuando intentamos ejecutarlo. Por ejemplo, si lanzamos una instrucción exec sp_persona_delete, SQL Server asume que se trata de un procedimiento almacenado de sistema, y por tanto debe encontrarse en la Base de Datos MASTER, donde residen todos los demás procedimientos almacenados de este tipo. Entonces primeramente intenta localizar el procedimiento en la BD Master. Al no encontrarlo, recién intenta encontrarlo en la BD activa.

Y cuales son las consecuencias de este comportamiento?: Una caída en el rendimiento. No parece influir mucho cuando la carga de transacciones es pequeña, pero cuando nuestro servidor debe atender miles de transacciones por minuto, entonces se notará una gran caída en el desempeño.

Otro punto a tomar en cuenta es que si creamos un procedimiento almacenado con el mismo nombre que un procedimiento almacenado de sistema, entonces nuestro procedimiento almacenado no se ejecutará nunca. Siempre será invocado el SP de sistema.

Por ejemplo, si creamos un SP llamado sp_who en la BD tempdb

USE tempdb
GO
CREATE PROCEDURE SP_WHO
AS
SELECT 'HOLA'
GO


ahora intentamos ejecutarlo

USE tempdb
GO
EXEC sp_who
EXEC dbo.sp_who
EXEC tempdb.dbo.sp_who
GO


Vemos que en todos los casos se ejecuta el SP de sistema. Aún cuando intentamos ejecutar nuestro SP con su nombre totalmente cualificado.


La única forma de ejecutar nuestro SP en lugar del SP de sistema es creándolo bajo un schema diferente a dbo, así:

USE tempdb
GO
CREATE SCHEMA mySchema AUTHORIZATION dbo
GO
CREATE PROCEDURE mySchema.SP_WHO
AS
SELECT 'HOLA'
GO
EXEC mySchema.sp_who
GO


Demasiadas complicaciones para obtener un simple Hola.


Moraleja: Jamás usar el prefijo 'sp_' en los nombres de nuestros procedimientos almacenados.

jueves, 8 de marzo de 2007

XBindingList: Un BindingList con esteroides

Una de las clases del .NET Framework que utilizo con más frecuencia es BindingList.

Como saben, BindingList es una colección genérica optimizada para servir como origen de datos para controles como el DataGridView y otros.

Es de gran utilidad, pero no están implementadas 3 características deseables: Búsqueda, Ordenación y Filtrado.

Mostraré la forma de crear una clase derivada de BindingList que implemente estas prestaciones. Como es un tema mas o menos extenso, irá desglosado en varios posts.

A falta de un mejor nombre, llamaré a esta clase XBindingList.

Definamos primero una clase Persona (ID, Nombre, Fecha de Nacimiento) que usaremos para nuestras pruebas.


public class Persona

{

private int _id;

private string _nombre;

private DateTime _fechaNacimiento;



public int Id

{

get { return _id; }

set { _id = value; }

}



public string Nombre

{

get { return _nombre; }

set { _nombre = value; }

}



public DateTime FechaNacimiento

{

get { return _fechaNacimiento; }

set { _fechaNacimiento = value; }

}



public Persona()

{

}



public Persona(int id, string nombre, DateTime fechaNacimiento)

:
this()

{

this.Id = id;

this.Nombre = nombre;

this.FechaNacimiento = fechaNacimiento;

}

}





Ahora si estamos listos para entrar en materia. Empecemos definiendo nuestra clase XBindingList:

public class XBindingList<T> : BindingList<T>

{

//...

}

Simplemente hemos declarado la clase XBindingList como una nueva colección genérica que se deriva de BindingList.

Ahora suministraremos la primera dosis de esteroides a nuestra nueva clase XBindingList: La capacidad de realizar búsquedas.

Se desea buscar algún valor en cualquiera de las propiedades de nuestra clase. Es decir, si usamos la clase Persona, debemos ser capaces de buscar un elemento ya sea por el Id, el Nombre o la Fecha de Nacimiento.

En general, para implementar nuevas características en una clase derivada de BindingList, debemos solapar (override) ciertas propiedades y métodos. Para las operaciones de búsqueda debemos solapar la propiedad SupportSearchingCore y el método FindCore. SuportsSearchingCore se encarga de comunicar al mundo si la colección es capaz o no de realizar búsquedas, mientras que FindCore se encarga de realizar la búsqueda propiamente dicha.

Primero vamos a enseñarle a XBindingList que responda afirmativamente cuando alguien le pregunte si es capaz de realizar búsquedas. Para ello solapamos la propiedad SupportsSearchingCore, asi:


protected override bool SupportsSearchingCore

{

get

{

return true;

}

}



Ahora viene el trabajo real: implementar el mecanismo de búsqueda.

El método FindCore recibe dos parámetros: prop que le indica en cuál propiedad debe buscar el valor, y key que es el valor que se debe buscar. Como punto de partida implementaremos un mecanismo de búsqueda secuencial. Es muy sencillo de codificar, pero es ineficiente cuando la colección es muy grande.


protected override int FindCore(PropertyDescriptor prop, object key)

{

foreach (T item in this)

{

if (prop.GetValue(item).ToString().Equals(key.ToString()))

{

return this.IndexOf(item);

}

}

return -1;

}



Esta es la idea básica, tal cual está implementado el algoritmo de búsqueda, solamente encuentra el primer elemento que coincida exactamente con la el valor de key. Sin duda se pueden añadir muchas mejoras como comparaciones parciales, buscar siguiente, etc. En algún momento postearé un algoritmo mejorado, por mientras los animo a que en algún comentario puedan compartir conmigo (y los otros lectores del blog) sus propios algoritmos.

Nada más, XBindingList ahora soporta búsquedas, veamos un ejemplo de su utilización.




Incluimos un BindingSource, un BindingNavigator y un DataGridView. En la barra de herramientas del BindingNavigator incluimos un Combobox (cbPropiedad), un TextBox (tbValor) y un Button (btnBuscar). Agregamos en cbPropiedad los nombres de las propiedades es que queremos poder buscar. En tbValor el usuario escribirá el valor que quiere buscar y la búsqueda se realizará al hacer click en el botón btnBuscar.

En el constructor del Form declaramos una variable llamada lista que es una instancia de XBindingList que almacenará elementos del tipo Persona, la poblamos con unos pocos datos y finalmente hacemos que nuestro BindingSource use lista como su origen de datos.



public FormTest2()

{

InitializeComponent();

XBindingList
<Persona> lista = new XBindingList<Persona>();

lista.Add(
new Persona(1, "Juan Perez", new DateTime(1980, 12, 10)));

lista.Add(
new Persona(2, "Maria Fernandez", new DateTime(1975, 12, 7)));

lista.Add(
new Persona(3, "Oscar Luna", new DateTime(1989, 4, 1)));

personaBindingSource.DataSource
= lista;

}




Asi que el único código que nos queda por escribir es precisamente el manejador del evento Click del botón. Es tan simple como esto:


private void btnBuscar_Click(object sender, EventArgs e)

{

int idx =personaBindingSource.Find(cbPropiedad.Text, tbValor.Text);

if (idx >= 0)

personaBindingSource.Position
= idx;

}




Eso es todo, ahora nuestra recién creada XBindingList soporta búsquedas.


Siguiente episodio: XBindingList se vuelve ordenada.