1.23.2010

Creando Gráficos tipo Pastel con el WPF toolkit

En un post anterior, hicimos una simple gráfica utilizando el WPF toolkit en donde mostrabamos como crear una grafica lineal en donde se mostraban las pulgas reportadas versos las pulgas corregidas. En este post vamos a crear la misma gráfica pero utilizando un gráfico tipo pie.

A diferencia del post anterior, esta vez vamos a agregar las series de forma dinámica de forma que podamos agregar series de diferentes tipos, por esta razón, la declaración del chart en XML lucirá de la siguiente manera:

image

Igualmente, vamos a agregar dos Radio button, uno para presentar el chart de forma lineal y otro para presentarlo como un pie.

image

El siguiente paso es crear un método para generar el gráfico lineal. Esta vez tenemos que crear las series, configurar las propiedades, agregar la colección de datos a desplegar, y agregar la serie al chart tal y como se ve en el siguiente código.

private void CrearGraficoLineal()
{
chartBugs.Series.Clear();
LineSeries BugsReported = new LineSeries();
BugsReported.Title = "Bugs Reported";
BugsReported.DependentValuePath = "Cantidad";
BugsReported.IndependentValuePath = "Fecha";
BugsReported.AnimationSequence = AnimationSequence.FirstToLast;
BugsReported.ItemsSource = LoadBugReported();

LineSeries BugsFixed = new LineSeries();
BugsFixed.Title = "Bugs Fixed";
BugsFixed.DependentValuePath = "Cantidad";
BugsFixed.IndependentValuePath = "Fecha";
BugsFixed.ItemsSource = LoadBugFixed();
BugsFixed.AnimationSequence = AnimationSequence.FirstToLast;

chartBugs.Series.Add(BugsReported);
chartBugs.Series.Add(BugsFixed);
}


Seguidamente procedemos a crear el método para crear las series tipo Pie. El código es exactamente al anterior, solamente que la instancia de la serie que se crea no es LineSeries sino PieSeries.



private void CrearGraficoLineal()
{
chartBugs.Series.Clear();
LineSeries BugsReported = new LineSeries();
BugsReported.Title = "Bugs Reported";
BugsReported.DependentValuePath = "Cantidad";
BugsReported.IndependentValuePath = "Fecha";
BugsReported.AnimationSequence = AnimationSequence.FirstToLast;
BugsReported.ItemsSource = LoadBugReported();

LineSeries BugsFixed = new LineSeries();
BugsFixed.Title = "Bugs Fixed";
BugsFixed.DependentValuePath = "Cantidad";
BugsFixed.IndependentValuePath = "Fecha";
BugsFixed.ItemsSource = LoadBugFixed();
BugsFixed.AnimationSequence = AnimationSequence.FirstToLast;

chartBugs.Series.Add(BugsReported);
chartBugs.Series.Add(BugsFixed);
}



Ahora solo falta invocar los métodos desde los eventos del radio button.



private void rdbLineal_Checked(object sender, RoutedEventArgs e)
{
CrearGraficoLineal();
}

private void rdbPastel_Checked(object sender, RoutedEventArgs e)
{
CrearGraficoPastel();
}





Al ejecutar el ejemplo anterior, los gráficos generados son los siguientes:



image



image





Oportunidad de Mejora



Como podemos ver en el código anterior, estamos repitiendo exactamente el mismo código para generar las series, con la excepción de que se crean instancias diferentes dependiendo del tipo de la serie que deseamos. Para optimizar este código, podríamos unificar estos métodos en uno solo y que genere una instancia de la serie deseada en base a un parámetro en el método que genera los gráficos. Si vemos en el object browser(F12), nos damos cuenta que ambas series heredan de la clase DataPointSeries, la cual es una clase abstracta.



image



Por esta razón, y aprovechandonos del polimorfismo podemos crear una serie del tipo DataPointSeries e instanciar la clase con el tipo que se nos indique vía parámetro. Para llevar a cabo esta tarea, iniciamos creando una enumeración para identificar los tipos de gráficos de forma consistente.



public enum TipoGrafico
{
Pastel,
Lineal
} ;


Seguidamente creamos el método unificado utilizando como tipo del parámetro la enumeración TipoGrafico, además creamos las instancias de las series en una estructura if dependiendo del valor en el parámetro.



private void CrearGrafico(TipoGrafico grafico)
{
chartBugs.Series.Clear();
DataPointSeries bugsReported = null;
DataPointSeries bugsFixed = null;

if (grafico == TipoGrafico.Lineal)
{
bugsFixed = new LineSeries();
bugsReported = new LineSeries();
}
else if (grafico == TipoGrafico.Pastel)
{
bugsFixed = new PieSeries();
bugsReported = new PieSeries();
}

if (bugsReported != null)
{
bugsReported.Title = "Bugs Reported";
bugsReported.DependentValuePath = "Cantidad";
bugsReported.IndependentValuePath = "Fecha";
bugsReported.AnimationSequence = AnimationSequence.FirstToLast;
bugsReported.ItemsSource = LoadBugReported();
chartBugs.Series.Add(bugsReported);
}
if (bugsFixed != null)
{
bugsFixed.Title = "Bugs Fixed";
bugsFixed.DependentValuePath = "Cantidad";
bugsFixed.IndependentValuePath = "Fecha";
bugsFixed.ItemsSource = LoadBugFixed();
bugsFixed.AnimationSequence = AnimationSequence.FirstToLast;
chartBugs.Series.Add(bugsFixed);
}
}



Nótese que si alguna de las series declaradas es nula, no se agrega al chart. Por último, cambiamos la invocación de los radio button hacia los métodos para generar las series.



private void rdbLineal_Checked(object sender, RoutedEventArgs e)
{
CrearGrafico(TipoGrafico.Lineal);
}

private void rdbPastel_Checked(object sender, RoutedEventArgs e)
{
CrearGrafico(TipoGrafico.Pastel);
}



Al ejecutar la aplicación, el resultado es el mismo que se obtenía con los métodos separados.



Technorati Tags: ,,,

1.08.2010

Linq y los Árboles de Expresiones ( Expression Trees) – Parte 1

Los árboles de expresiones nos permiten parsear expresiones enviadas a un método. Un árbol de expresiones en Linq es como lo indica su nombre, una estructura de árbol que contiene expresiones. Las expresiones por otra parte, en este caso particular son expresiones Lambda, las cuales como vimos en un post pasado, son una nueva manera de definir un delegate en C#. El árbol de expresiones es una estructura de datos que contiene expresiones lambda compiladas.

Los árboles de expresiones son dinámicos, ya que al igual que los delegates, permiten cambiar en tiempo de ejecución la expresión a ejecutar; es decir, estos árboles son estructuras que contienen expresiones que se utilizan para procesar los datos. Para entender como funcionan los árboles de expresiones vamos a analizar un ejemplo. En primera instancia, vamos a tener una clase Producto.

public class Producto
{
public int Id { get; set; }
public string Nombre { get; set; }
public double Precio { get; set; }

public Producto(int id, string nombre, double precio)
{
Id = id;
Nombre = nombre;
Precio = precio;
}
}



Seguidamente vamos a crear un arreglo de productos y vamos a aplicarle una expresión lambda utilizando un árbol de expresiones.



private static void ApplyFunction()
{
Producto[] productos = {
new Producto(1, "Laptop", 500),
new Producto(2, "Monitor", 300),
new Producto(3, "Zune HD", 230),
new Producto(4, "IPod Touch", 200),
new Producto(5, "Black Berry Storm", 540),
new Producto(6, "Wii", 199)
};

String Message = "";
Expression<Func<Producto, Boolean>> expProductos = miProducto => miProducto.Precio > 299;
foreach (Producto producto in productos)
if (expProductos.Compile().Invoke(producto))
Message = Message + producto.Nombre + "\r\n";
Console.WriteLine(Message);
}


La parte del árbol de expresiones empieza en la parte de la creación de lo que parece un bloque de código complejo. La clase Expression<T> acepta un delegate prototipo como parámetro. El delegate se define usando el delegate genérico Func<T, TResult>. En este caso, el delegate genérico acepta un string como parámetro de entrada y retorna un booleano.Sin embargo, se pueden utilizar cualquier tipo en la entrada o en el retorno que se requiera en la aplicación. La definición de la expresión viene de seguido, expProductos contiene una expresión booleana que recibe un string y devuelve un booleano, el cual es determinado a partir del precio del producto – si es mayor de 99 retorna true, de lo contrario retorna false. En este punto, expProductos contiene una estructura árbol que incluye todos los elementos requeridos para la expresión lambda: miProducto => miProducto.Precio > 299;



Para invocar la función se invoca la expresión utilizando Compile().Invoke(producto) donde se evalúa la variable que se envía por parámetro y se retorna verdadero si cumple con la expresión, o falso si no lo cumple.



En el siguiente post vamos a crear un árbol de expresiones desde 0.



Technorati Tags: ,,

1.06.2010

Entendiendo las Expresiones Lambda

Una de las características menos comprendidas de .NET son las expresiones Lambda. En este post voy a explicar lo más sencillo posible que son, y como funcionan.

Las expresiones lambda son un paso más para soportar programación funcional – aunque no son exclusivas de este paradigma – y a partir de C# 3.0 son consideradas el siguiente paso desde los delegates anónimos. Para comprender mejor como se llego a tener este concepto en C# 3.0 vamos a hacer un viaje en retrospectiva para ver como evolucionó este tema.

Inicialmente teniamos los delegates, los cuales en resumen me permitían pasar referencias a la función para poder ser invocadas por otra funcióna, en lenguaje C++ es un puntero a una función para poder ser utilizada como parámetro.

Por ejemplo, supongamos que queremos tener una función que imprima todos los número impares de un rango que el usuario le proporciona. Se podría crear una función que me indica que un número es impar o no de la siguiente forma:

public static bool EsImpar(int numero)
{
return (numero & 1) == 1;
}


Como queremos aplicarle la función a un grupo de números para determinar si son impares, podríamos crear un delegate – un puntero a una función – y acceder la función anterior como un parámetro. Inicialmente creamos un delegate con la signatura de la función esImpar, es decir con un parámetro int.



public delegate bool ProbarNumero(int numero);


Seguidamente procedemos a crear un método que nos imprima los números impares:



public static void ImprimirLosImpares(int inicio, int final, ProbarNumero prueba)
{
for (int i = inicio; i <= final; i++)
{
if (prueba(i))
Console.WriteLine(i);
}
}


Como podemos ver en este método, estamos pasando el delegate como parámetro para mandar a ejecutar una función la cual tiene como signatura un parámetro de tipo int. Este delegate va a apuntar a la función que yo le envíe por parámetro y la va a ejecutar en la sección del if. Seguidamente invocamos la función ImprimirLosImpares de la siguiente manera:



static void Main(string[] args)
{
ImprimirLosImpares(1, 15, EsImpar);
}



En esta ocasión, le pasamos como parámetro la función es impar, pero pudimos haber enviado como parámetro cualquier función que reciba un entero como parámetro. El resultado al ejecutar este código sería:



image



Esto me da independencia a la hora de invocar los métodos, por que yo podría tener una función imprimir, y varios métodos tales como EsPar, EsImpar, EsPrimo, etc. y utilizarlos cuando lo considere necesario.



En C# 2.0 este concepto de los delegates se extendió con el uso de los métodos anónimos. Los métodos anónimos me permiten eliminar la necesidad de tener que declarar el método y escribirlo en línea. Todo lo que escribimos antes en C#, podemos resumirlo en este método anónimo:



static void Main(string[] args)
{
ImprimirLosImpares(1, 15,
delegate(int numero)
{
return (numero & 1) == 1;
}
);
//ImprimirLosImpares(1, 15, EsImpar);
}


En este caso, en lugar de pasarle el puntero al método, le pasamos un método anónimo directamente como parámetro para que se ejecute dentro de ImprimirLosPares. El resultado al ejecutar este bloque de código es el mismo que el resultado anterior.



Las expresiones lambda son una sintaxis más compacta de lo anterior. Nosotros podemos en C# 3.0+ escribir la funcionalidad anterior simplemente con el siguiente código:



static void Main(string[] args)
{
ImprimirLosImpares(1, 15, numero => (numero & 1) == 1);
}



Las expresiones lamba son siempre de la forma:



parametros => expresión



por lo que en la función anterior, pasamos la variable número como parámetro y luego le aplicamos la condición para definir si es impar o no.



Technorati Tags: ,