sábado, 19 de abril de 2008

Extendiendo controles con GDI+

Que en los tiempos de Windows Presentation Foundation y Silverlight alguien dedique su tiempo a hablar de Windows Forms y GDI+ puede parecer, cuanto menos, curioso. Pero seguro que para algunos de aquellos que todavía no han dado el salto al Framework 3 (o 3.5) este post les puede ser de utilidad.

El objetivo del post es mostrar como podemos mejorar el aspecto grafico de nuestras aplicaciones de manera muy sencilla con GDI. Concretamente en el ejemplo se verá como modificar un control TextBox para poder modificar, mediante GDI el color del borde del mismo (una propiedad que el control no tiene).

Lo primero que hay que hacer es crear una clase y hacer que herede de TextBox, por ejemplo así:

class ExtendedTextBox : System.Windows.Forms.TextBox

En este momento tenemos una clase que tiene exactamente las mismas funcionalidades que un TextBox. Nuestro objetivo, cómo hemos dicho, es poder modificar el color del borde, para ello y antes de entrar realmente en el tema de GDI tenemos que preparar nuestra clase para permitir al que se pueda modificar el borde. Para ello podemos hacer lo siguiente:

  1. Declaramos una variable privada que nos servirá para guardar el color del borde actual:-> private Color m_cBorderColor;
  2. En el constructor hacemos lo siguiente:
    1. Inicializamos la variable anterior al color por defecto: m_cBorderColor = Color.Black;
    2. Definimos una propiedad que nos permita cambiar el color del borde, por ejemplo :

public Color BorderColor
{

get{return m_cBorderColor;

}

set{m_cBorderColor = value;}

}

Ahora ya tenemos preparada la clase para que nos puedan modificar el color del borde, pero aunque lo hagan, esto no tendrá ningún efecto real sobre nuestro control (únicamente estamos modificando el valor de la variable m_cBorderColor). Para que este color se aplique a nuestro TextBox, tenemos que usar GDI+. ¿Y cómo se hace esto? Pues lo primero que tenemos que hacer es sobrescribir el evento OnPaint de nuestra clase:


protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{}

Y lo segundo, es especificar en este método que es lo que queremos pintar (en nuestro caso es el color del borde del control, pero se podría modificar, dibujar, o pintar todo lo que se quisiese). El código del método quedaría así:

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e){
Graphics g = e.Graphics;
Pen p = new Pen(m_cBorderColor, 1);
g.DrawRectangle(p, new Rectangle(0, 0, this.Width - 1, this.Height - 1));
base.OnPaint(e);
}


Básicamente lo que estamos haciendo es acceder a la superficie de pintado del control (clase Graphics) , definir un Pincel (Pen) con el color almacenado en nuestra variable y un píxel de grosro, y dibujar un rectángulo que coincida con los bordes del textbox. Después simplemente se llama a la clase base para acabar de hacer el renderizado.

Con esto, parece que ya tendríamos suficiente para poder utilizar nuestro control en un formulario, pero si lo intentáis utilizar veréis que al modificar el color del control este no cambia. Esto pasa porque no le estamos diciendo al control que se repinte al modificar el color, para ello tenemos que modificar la propiedad BorderColor de la siguiente manera:
set
{
m_cBorderColor = value;
this.OnPaint(new PaintEventArgs(this.CreateGraphics(), this.ClientRectangle)); <- Repintar

}

Ahora, si modificáis el color, veréis como el borde se cambia, ¡pero aun no es suficiente! Si después de modificar el color , el formulario se minimiza o alguna otra ventana se pode delante, veréis cómo el control vuelve a perder el color que le hemos dado. ¿Por qué sucede esto? Porque las llamadas que el sistema operativo hace a la función de repintar del TextBox, ignoran por defecto a las funciones que sobrescriben a la función base (es decir no se está ejecutando la función
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) cuando el sistema operativo lo requiere. Una opción seria implementar en el control todos los posibles casos en que se puede dar esta casuística y llamar nosotros mismos a la función (al igual que hacemos en el set de la propiedad) pero esta solución, aparte de costosa es errónea. La solución pasa por usar la siguiente instrucción en el constructor de la clase:

SetStyle(ControlStyles.UserPaint, true);

Esta instrucción modifica el comportamiento por defecto y hace que nuestra función se ejecute cuando el sistema operativo necesite repintar el control.

Por último, debemos implementar el evento TextChanged del control, para que cada vez que se escriba una letra en el mismo se repinte el control.

this.TextChanged += new EventHandler(ExtendedTextBox_TextChanged); <- en el constructor //Implementación del método que captura el evento void ExtendedTextBox_TextChanged(object sender, EventArgs e) { this.OnPaint(new PaintEventArgs(this.CreateGraphics(), this.ClientRectangle)); } A partir de este momento ya tenemos un nuevo control TextBox al que fácilmente le podemos modificar el color del borde. De la misma manera que hemos visto este ejemplo, con GDI+ se pueden hacer cosas mucho más complejas, permitiendo tener aplicaciones Windows mucho más ricas y personalizadas, y con un alto valor añadido.

Podeis descargar el código del ejemplo aquí.


1 comentario:

Robert dijo...

Hola, me parece muy interesante el ejemplo, justo queria cambiar el color de borde al datagridview, como hago para descargar su ejemplo es un link roto, o podria enviarlo a mi correo:
rho1728@hotmail.com
Gracias