Saltar al contenido

Custom Button – Custom controls WinForm C#

Hola, continuando con los tutoriales de controles personalizados en Windows Form, esta vez haremos un botón plano con un radio de borde personalizable, obteniendo así un botón con esquinas redondeadas, en forma de pastilla, o un botón rectangular normal.

Normalmente en Windows Form, los botones redondos tienen una mala calidad de imagen en las esquinas, por lo que apliqué un pequeño truco para obtener un botón de esquinas redondeadas de buena calidad de acabado.

Bien, empecemos con el tutorial:

1.- Crear clase

Primeramente agregaremos una clase para el botón de nombre RJButton, o el nombre que desean, en nuestro proyecto de Windows Form.

2.- Importar librerías

Para construir el botón personalizado, es necesario importar la librería Windows Forms y la librería de dibujos (Drawing) y la librería ComponentModel para implementar atributos (Por ejemplo agrupar las propiedades en categorías).

using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;

3.- Heredar la clase Button

Es realmente complicado crear un control personalizado desde cero, por lo que simplemente heredaremos del control Button de la librería Windows Form y así ampliar su funcionalidad y modificar la apariencia.

public class RJButton : Button
{
}

4.- Declarar campos

En la clase, declararemos campos para la apariencia del botón y asignar sus valores predeterminados, por ejemplo: El grosor de borde, tamaño del radio de borde y el color de borde.

//Fields
private int borderSize = 0;
private int borderRadius = 20;
private Color borderColor = Color.PaleVioletRed;

5.- Generar propiedades

Generamos propiedades para exponer los campos anteriores declarados.

//Properties
[Category("RJ Code Advance")]
public int BorderSize
{
    get { return borderSize; }
    set
    {
        borderSize = value;
        this.Invalidate();
    }
}

[Category("RJ Code Advance")]
public int BorderRadius
{
    get { return borderRadius; }
    set
    {        
        borderRadius = value;       
        this.Invalidate();
    }
}

[Category("RJ Code Advance")]
public Color BorderColor
{
    get { return borderColor; }
    set
    {
        borderColor = value;
        this.Invalidate();
    }
}
[Category("RJ Code Advance")]
public Color BackgroundColor
{
    get { return this.BackColor; }
    set { this.BackColor = value; }
}

[Category("RJ Code Advance")]
public Color TextColor
{
    get { return this.ForeColor; }
    set { this.ForeColor = value; }
}

6.- Constructor

En el constructor, inicializaremos algunas propiedades del botón para la apariencia predeterminada. Por ejemplo, un estilo plano y sin bordes, con un tamaño, color de fondo y color de texto específicos. Pueden establecer cualquier otra propiedad que deseen, como un icono, alineación de texto e imagen.

También será necesario suscribir el evento Resize del botón, para mantener la proporción del radio del borde limitada a la altura del botón, ya que si el radio del borde es mayor que la altura del botón, el botón se deformará porque es imposible dibujar un arco más grande en dimensiones más pequeñas.

//Constructor
public RJButton()
{
    this.FlatStyle = FlatStyle.Flat;
    this.FlatAppearance.BorderSize = 0;
    this.Size = new Size(150, 40);
    this.BackColor = Color.MediumSlateBlue;
    this.ForeColor = Color.White;
    this.Resize += new EventHandler(Button_Resize);
}

private void Button_Resize(object sender, EventArgs e)
{
    if (borderRadius > this.Height)
        borderRadius = this.Height;
}

7.- Método crear ruta de figura

Luego de realizar los pasos anteriores, declararemos un método privado para obtener la ruta de gráficos para el diseño del botón con radio de borde personalizable. Para ello:

  • Agregaremos un arco en el eje inicial del rectángulo de tamaño igual al radio, comenzando en un ángulo de 180 grados con un rango de recorrido de 90 grados.
  • Agregaremos otro arco en la esquina superior derecha, comenzando en un ángulo de 270 grados con un rango de 90 grados.
  • Otro arco en la esquina inferior derecha, comenzando en un ángulo de 0 grados con un rango de 90 grados.
  • Finalmente agregaremos el ultimo arco en la esquina inferior izquierda para cerrar la forma del botón, comenzando en un ángulo de 90 grados con un rango de 90 grados.

Tener en cuenta que la dirección de los ángulos del círculo unitario en C# o Visual Basic son en sentido de las agujas del reloj (Hacia la derecha) como se muestra en la siguiente imagen.

//Methods
private GraphicsPath GetFigurePath(Rectangle rect, float radius)
{
    GraphicsPath path = new GraphicsPath();
    float curveSize = radius * 2F;

    path.StartFigure();
    path.AddArc(rect.X, rect.Y, curveSize, curveSize, 180, 90);
    path.AddArc(rect.Right - curveSize, rect.Y, curveSize, curveSize, 270, 90);
    path.AddArc(rect.Right - curveSize, rect.Bottom - curveSize, curveSize, curveSize, 0, 90);
    path.AddArc(rect.X, rect.Bottom - curveSize, curveSize, curveSize, 90, 90);
    path.CloseFigure();
    return path;
}

8.- Anular el método OnPaint

Para modificar o ampliar la apariencia del botón es necesario anular el método OnPaint del botón base. En este caso, dibujaremos un botón redondeado o uno tradicional (Cuadrado) dependiendo del valor del radio de borde.

protected override void OnPaint(PaintEventArgs pevent)
{
    base.OnPaint(pevent);

    Rectangle rectSurface = this.ClientRectangle;
    Rectangle rectBorder = Rectangle.Inflate(rectSurface,-borderSize, -borderSize);
    int smoothSize = 2;
    if (borderSize > 0)
        smoothSize = borderSize;

    if (borderRadius > 2) //Rounded button
    {
        using (GraphicsPath pathSurface = GetFigurePath(rectSurface, borderRadius))
        using (GraphicsPath pathBorder = GetFigurePath(rectBorder, borderRadius-borderSize))
        using (Pen penSurface = new Pen(this.Parent.BackColor, smoothSize))
        using (Pen penBorder = new Pen(borderColor, borderSize))
        {
             pevent.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            //Button surface
            this.Region = new Region(pathSurface);
            //Draw surface border for HD result
            pevent.Graphics.DrawPath(penSurface, pathSurface);

            //Button border                    
            if (borderSize >= 1)
                //Draw control border
                pevent.Graphics.DrawPath(penBorder, pathBorder);
        }
    }
    else //Normal button
    {
        pevent.Graphics.SmoothingMode = SmoothingMode.None;
        //Button surface
        this.Region = new Region(rectSurface);
        //Button border
        if (borderSize >= 1)
        {
            using (Pen penBorder = new Pen(borderColor, borderSize))
            {
                penBorder.Alignment = PenAlignment.Inset;
                pevent.Graphics.DrawRectangle(penBorder, 0, 0, this.Width - 1, this.Height - 1);
            }
        }
    }
}

9.- Anular el método OnHandleCreated

Tambien es necesario anular el método OnHandleCreated. Usaremos este método para solucionar el siguiente inconveniente: En el método anterior, para tener un botón de buena calidad de imagen, se dibuja el borde de la superficie con el mismo color de su padre contenedor. Por lo tanto, si el formulario o control contenedor cambia de color de fondo, el borde de la superficie del botón se hará visible dejando una mala impresión. Para solucionar el problema debemos detectar si el formulario o control contenedor cambia de color. Para ello suscribiremos el evento BackColorChanged del padre contenedor.

protected override void OnHandleCreated(EventArgs e)
{
    base.OnHandleCreated(e);
    this.Parent.BackColorChanged += new EventHandler(Container_BackColorChanged);
}

private void Container_BackColorChanged(object sender, EventArgs e)
{
    this.Invalidate();
}

Cabe mencionar que, el evento HandleCreated es el que más se parece al evento Load del formulario o control de usuario, ya que se ejecuta en primer lugar cuando se crea el identificador del control.

Muy bien, y eso es todo, ya puedes usar el nuevo botón personalizado desde la caja de herramientas, No olvides compilar el proyecto para guardar los cambios

Ver video tutorial

Los comentarios están cerrados.