Gerardo Contijoch

Experiencias del día a día trabajando con .NET – ASP.NET, C#, ASP.NET MVC y demas…

Archive for the ‘C#’ Category

Suspender el dibujado de un control o formulario

Posted by Gerardo Contijoch en septiembre 14, 2009

Hay ocasiones en que nuestros Forms terminan recargados de controles, los cuales cambian de estado o contenido todos a la vez. Si la cantidad de controles es mucha y el contenido de los mismo es modificado varias veces en un breve intervalo de tiempo o simplemente el redibujado de los mismos es muy lento (es decir, el usuario nota como se va llenando cargando el contenido del Form progresivamente), podemos optar por suspender el redibujado del form en su totalidad hasta que los controles estén listos para ser dibujados.

Muy relacionado con mi último post, les dejo el link al código de una clase que se encarga de suspender y reanudar el redibujado de un control (o form en su totalidad si lo deseamos). La técnica se basa en el envío de mensajes al control o ventana en cuestión.

¡Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in .Net 2.0, C# | Etiquetado: , | 4 Comments »

Encontrar el valor de un Window Message

Posted by Gerardo Contijoch en septiembre 12, 2009

De vez en cuando, si trabajamos con WinForms, nos puede surgir la necesidad de manejar un evento a muy bajo nivel y para hacerlo tenemos que interceptar los famosos Window Messages, o mensajes de ventana. Estos mensajes son el mecanismo que utiliza Windows para comunicarse con una aplicación. Cuando apretamos una tecla y nuestra aplicación responde, parece que es nuestra aplicación la que ‘atrapó’ el teclazo, pero en realidad es el Sistema Operativo (SO) el que lo atrapó (debido a que el SO es quien controla el puerto donde esta conectado el teclado) y este le envía un mensaje a nuestra aplicación para que responda al mismo, normalmente imprimiendo el caracter representado por la tecla presionada. Aunque no lo parezca, el SO es quien tiene control de todo lo que sucede y cuando nuestra aplicación responde a distintos inputs, es en realidad el SO el que le ordena que haga una cosa u otra.

Si tenemos un Form y el SO determina que es necesario que sea redibujado (porque cambio su contenido por ejemplo), entonces se envía un mensaje conocido como WM_PAINT (en .NET veremos que esto dispara el evento Paint), en cambio si presionamos la tecla ‘Q’ en el teclado, entonces el mensaje recibido por nuestra aplicación es WM_KEYDOWN (también tenemos el evento KeyDown en .NET, el cual es lanzado cuando se recibe un mensaje de este tipo). Cabe aclarar que el nombre de los mensajes es solo una ayuda para nosotros los humanos, ya que los mensajes en realidad no son más que un valor entero, y estos nombres son los nombres de las constantes asociadas a los mismos.

Para interceptar estos mensajes lo que se suele hacer comúnmente es sobrescribir el método WndProc definido en la clase Control de la siguiente manera:

   1: ...
   2: private const int WM_PAINT = 15;
   3:
   4: protected override void WndProc(ref Message m) {
   5:
   6:     if (m.Msg == WM_PAINT) {
   7:         // Atrapamos el mensaje...
   8:     } else{
   9:         // Ignoramos el mensaje y dejamos que siga su curso
  10:         base.WndProc(ref m);
  11:     }
  12:
  13: }
  14: ...

El método WndProc es el que se encarga de procesar todos los mensajes que recibe un Form y es por eso que ese es el punto indicado para atraparlos.

Ahora el problema esta en conocer el valor de los mensajes, ya que muchas veces uno sabe que mensaje procesar, pero no sabe cual es su valor. Estos valores están definidos en la clase System.Windows.Forms.NativeMethods y System.Design.NativeMethods, pero lamentablemente estas clases son internas y no tenemos acceso a ellas, por lo que vamos a tener que usar .NET Reflector para ver su contenido.

La info de este post fue sacada de aquí.

¡Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in .Net 2.0, C# | Etiquetado: , , | Leave a Comment »

TraceTool: simplificando el logging en .NET (¡y en otras plataformas también!)

Posted by Gerardo Contijoch en mayo 31, 2009

Les voy a presentar una herramienta que me cambió la forma de hacer logging en mis aplicaciones .NET. Estoy hablando de TraceTool, creado por Thierry Parent.

TraceTool consiste en una librería (disponible para .NET, Java, Delphi, Javascript, entre otros lenguajes) para hacer tracing o logging (como prefieran llamarlo) y un visor (de uso opcional) que es capaz de interpretar tanto los logs o trazas generadas por la librería como así también otros orígenes.

En este post me voy a limitar a hacer una presentación de la herramienta (actualmente en su versión 11), pero para más información pueden consultar el artículo original, el cual contiene muchos más detalles sobre el funcionamiento y configuración de la misma.

Uso de la librería

En la mayoría de los casos la única clase con la que vamos a necesitar interactuar directamente es TTrace. Esta clase expone 3 propiedades, Debug, Warning y Error, las cuales se utilizan para registrar logs de información, advertencia y errores respectivamente. Veamos un ejemplo:

   1: public void Log() {
   2:     TTrace.Debug.Send("Esto es un mensaje.");
   3:     TTrace.Warning.Send("Esto es una advertencia...");
   4:     TTrace.Error.Send("Esto es un mensaje de error!");
   5: }

Ese código produce la siguiente salida (vista en el visor de TraceTool):

TraceTool-log1

Como se ve, lo único que diferencia a cada tipo de mensaje, es el icono que lo precede. Si lo deseamos, también podemos agregar un comentario o texto asociado al mensaje de la siguiente manera:

TTrace.Debug.Send("Esto es un mensaje.", "Texto con info extra sobre el mensaje...");

Que nos quedaría así en el visor:

TraceTool-log2

Una característica por de más de interesante es el hecho de que el método Send() nos devuelve una instancia de un objeto de tipo TraceNode (que es la clase base de la cual derivan los objetos Debug, Warning y Error), la cual nos permite seguir enviando trazas pero anidadas dentro de la traza original (es decir, sub-trazas):

   1: TraceNode nodo = TTrace.Debug.Send("Nodo nivel 0.");
   2: nodo.Send("¡Me encuentro en el nivel 1!");
   3: nodo.Send("¡Yo tambien!");
   4: nodo.Send("¡Y yo!");
   5: TTrace.Debug.Send("Otro nodo nivel 0.");

TraceTool-LogAnidado

Un efecto parecido se puede lograr con los métodos Indent() y UnIndent() que nos permiten indentar todas las trazas sin necesidad de guardar una referencia a un nodo en particular. Esto suele ser muy útil cuando logueamos las entradas, procesamiento y salidas de ciertos métodos aunque para ello TraceTool ya tiene un set de métodos específicamente adaptado para tal fin:

   1: private void UnMetodo() {
   2:     TTrace.Debug.EnterMethod("UnMetodo()");
   3:
   4:     try {
   5:         TTrace.Debug.Send("Procesando...");
   6:         // ...
   7:         throw new Exception();
   8:     } catch {
   9:         // Aca se podría loguear la excepción
  10:         TTrace.Error.Send("Error!");
  11:     } finally {
  12:         TTrace.Debug.ExitMethod("UnMetodo()");
  13:     }
  14: }

Esto produce la siguiente salida:

TraceTool-EnterExit

Loguear más que sólo texto

Con TraceTool también es posible loguear el contenido de objetos enteros con la misma facilidad con la que logueamos sólo texto:

   1: public class Persona {
   2:     public string Nombre { get; set; }
   3:     public string Apellido { get; set; }
   4:     public int Edad { get; set; }
   5: }
   6:
   7: /*...*/
   8:
   9: TTrace.Debug.Send("Logueando solo texto");
  10: var p = new Persona() { Nombre = "Gerardo", Apellido = "Contijoch", Edad = 27 };
  11: TTrace.Debug.SendObject("Logueando un objeto", p);

TraceTool-Objeto

También es posible incluir en la información a loguear sobre un objeto detalles como sus campos, métodos, eventos, miembros privados, etc.

En el caso que quisiéramos registrar el valor de una variable de un tipo primitivo, como puede ser un número, este método no resulta práctico (¿para qué mostrar información de la clase Int32 cuando solo queremos su valor?) y es por eso que la clase TraceNode posee el método SendValue():

   1: TTrace.Debug.SendValue("Logueando un Int32", 50);
   2: TTrace.Debug.SendValue("Logueando un DateTime", new DateTime(2005, 10, 6));
   3: TTrace.Debug.SendValue("Logueando un Boolean", false);

TraceTool-Primitive

(Los logs pueden configurarse para que no se registre la información como la hora y el hilo en el que se ejecutó el logueo)

Como si todo esto fuera poco, también existen métodos similares para registrar Xmls (AddXml()), imágenes (SendBitmap()) y hasta tablas (SendTable()). Pueden ver su uso en el artículo donde se presenta TraceTool.

Configuración

Hay dos puntos donde se puede configurar el logueo. El primero es la propiedad Options de la clase TTrace. Esta propiedad nos va a permitir configurar el formato de los logs que se registren.

Para controlar la verbosidad de los logs se pueden usar las propiedades SendEvents, SendFunctions, SendInherited, SendPrivate, SendProcessName y SendThreadId, las cuales permiten configurar que información se registra sobre los objetos que logueamos.

La otra propiedad importante de esta clase es SendMode, la cual nos permite indicarle a la API si vamos a enviar logs al visor (util cuando se depura, pero no siempre en producción) o no.

El otro punto donde podemos configurar el logueo es la propiedad WinTrace de TTrace. TTrace internamente utiliza a la clase WinTrace para registrar todo y es por ello que algunos parámetros referidos al logueo se especifican acá.

Para setear el path a un archivo de logs vamos a usar el método SetLogFile(). Junto con el path de logs también se puede configurar el particionamiento de los logs, es decir, si se crea un log diferente por día, por máximo de líneas o simplemente un único log ilimitado.

Lamentablemente, los logs son únicamente en formato Xml y esto no se puede cambiar.

Cabe aclarar que TraceTool tiene 2 logs diferentes, uno es el local (el que normalmente usaremos nosotros) y otro es el log del Visor, el cual puede registrar todo lo que se le envíe (usar este log es útil cuando hay mas de una aplicación logueando al mismo lugar). Ambos logs pueden ser configurados independientemente el uno del otro. Para más detalles sobre la configuración pueden ver el artículo original en donde se detalla un poco más el asunto.

Soporte Unicode

Desafortunadamente, si bien la API tiene soporte para logs encodeados como Unicode, el visor aún no los soporta y no es posible abrir logs con caracteres especiales. Es por ello que vamos a tener que cuidarnos de lo que guardamos en los logs porque puede que no podamos abrirlos con el visor. Para solucionar esto, yo opté por modificar la API (el código fuente esta disponible para bajar) y filtrar todos los caracteres especiales con una versión ligeramente modificada del método que presenté en este post (ojo con las tablas, que necesitan los caracteres ‘\t’ para ser procesadas correctamente).

Bien, como dije al comienzo, esto fue sólo una breve presentación de la herramienta, no quise entrar en muchos detalles porque iba a terminar siendo una reescritura completa del artículo original de Thierry Parent. Espero que les resulte interesante esta herramienta y por sobretodo, útil.

¡Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in .Net 2.0, C# | Etiquetado: | 2 Comments »

Keyword default en C#

Posted by Gerardo Contijoch en febrero 15, 2009

Desde la versión 2.0 de C# hay un nuevo uso para keyword default que no todos conocen y resulta de muchísima utilidad en ciertos casos. Típicamente a este keyword lo vemos en bloques de tipo switch, pero también puede ser utilizado para la inicialización de variables. En este último caso, la función del keyword es devolver el valor por defecto de un tipo dado. Veamos un ejemplo:

   1:
   2: public class UnaClase { ... }
   3:
   4: public struct UnaEstructura { ... }
   5:
   6: public void Test(){
   7:
   8:     int i = default(int);
   9:     string s = default(string);
  10:     UnaClase c = default(UnaClase);
  11:     UnaEstructura est = default(UnaEstructura);
  12:
  13:     /* ... */
  14: }
  15:

Acá la variable i se va a inicializar en 0 (que es el valor por defecto para los tipos numéricos), s en null (que es el valor por defecto para strings), c en null (valor por defecto para tipos por referencia) y est va inicializarse con una nueva estructura cuyos campos toman el valor por defecto de sus tipos.

En un código como el anterior no tiene mucha utilidad este keyword ya que sabemos de antemano el valor por defecto de los tipos dados y podemos asignarlos directamente. La verdadera utilidad se descubre cuando trabajamos con tipos genéricos:

   1: public class GenericList<T> {
   2:
   3:     private class Node {
   4:         public Node Next;
   5:         public T Data;
   6:     }
   7:
   8:     private Node head;
   9:
  10:     //...
  11:
  12:     public T GetNext() {
  13:         T temp = default(T);
  14:         Node current = head;
  15:         if (current != null) {
  16:             temp = current.Data;
  17:             current = current.Next;
  18:         }
  19:         return temp;
  20:     }
  21: }

En este ejemplo (sacado de acá) es imposible saber que valor por defecto hay que asignarle a la variable temp (dentro del método GetNext()) ya que su tipo es genérico. Por supuesto también puede utilizarse para determinar si una variable esta o no inicializada sin necesidad de conocer su tipo.

¡Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in .Net 2.0, C# | Etiquetado: , | Leave a Comment »