En este tutorial crearemos una barra de progreso personalizada con una apariencia muy elegante y atractiva. Donde podemos personalizar los colores o tamaño de todos los componentes a voluntad. Por lo tanto, la barra de progreso personalizada permite las siguientes personalizaciones:
- Cambiar el color y la altura del canal o rastreador.
- Cambiar el color y la altura del deslizador o indicador.
- Mostrar el valor actual (izquierda, derecha, centro, deslizante, No mostrar).
- Mostrar el valor máximo.
- Agregar un símbolo o cualquier texto antes o después del valor.
- Cambiar el color y el tamaño del texto del valor.
- Cambiar la ubicación del texto del valor.
- Cambiar la ubicación del eje Y del texto del valor.
- Y mantiene todas las funciones y comportamientos de la barra de progreso convencional, excepto el cambio de dirección del deslizador o indicador.
Bien, comencemos el tutorial.
1.- Preparar una clase y una enumeración
- Agregar una nueva clase para la barra de progreso personalizada (Yo lo nombraré RJProgressBar).
- Importar la librería de Windows Forms, Drawing y ComponentModel.
- Crear una enumeración para la posición del texto del valor de la barra de progreso.
- Finalmente, heredar del control ProgressBar.
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
namespace CustomControls.RJControls
{
public enum TextPosition
{
Left,
Right,
Center,
Sliding,
None
}
class RJProgressBar : ProgressBar
{2.- Definir campos
- Definir los campos de apariencia y asignar sus valores predeterminados.
- Definir 2 campos booleanos para detener la pintara del fondo del control, o toda la pintura del control.
//Fields
//-> Appearance
private Color channelColor = Color.LightSteelBlue;
private Color sliderColor = Color.RoyalBlue;
private Color foreBackColor = Color.RoyalBlue;
private int channelHeight = 6;
private int sliderHeight = 6;
private TextPosition showValue = TextPosition.Right;
private string symbolBefore = "";
private string symbolAfter = "";
private bool showMaximun = false;
//-> Others
private bool paintedBack = false;
private bool stopPainting = false;3.- Constructor
- Especificar el estilo y el comportamiento del control. En este caso, el control será pintado por el usuario y no por el sistema operativo. Hacerlo es muy importante, ya que solo entonces es posible anular el evento Paint en los controles nativos de Windows.
- Y finalmente establecer un color de texto predeterminado.
//Constructor
public RJProgressBar()
{
this.SetStyle(ControlStyles.UserPaint, true);
this.ForeColor = Color.White;
}4.- Generar propiedades
Generar las propiedades de los campos de apariencia declarados anteriormente, y así poder cambiar la apariencia del control desde el cuadro de propiedades. Luego, en los descriptores de acceso Set, invocar el método Invalidate, para repintar el control y así actualizar la apariencia.
//Properties
[Category("RJ Code Advance")]
public Color ChannelColor
{
get { return channelColor; }
set
{
channelColor = value;
this.Invalidate();
}
}
[Category("RJ Code Advance")]
public Color SliderColor
{
get { return sliderColor; }
set
{
sliderColor = value;
this.Invalidate();
}
}
[Category("RJ Code Advance")]
public Color ForeBackColor
{
get { return foreBackColor; }
set
{
foreBackColor = value;
this.Invalidate();
}
}
[Category("RJ Code Advance")]
public int ChannelHeight
{
get { return channelHeight; }
set
{
channelHeight = value;
this.Invalidate();
}
}
[Category("RJ Code Advance")]
public int SliderHeight
{
get { return sliderHeight; }
set
{
sliderHeight = value;
this.Invalidate();
}
}
[Category("RJ Code Advance")]
public TextPosition ShowValue
{
get { return showValue; }
set
{
showValue = value;
this.Invalidate();
}
}
[Category("RJ Code Advance")]
public string SymbolBefore
{
get { return symbolBefore; }
set
{
symbolBefore = value;
this.Invalidate();
}
}
[Category("RJ Code Advance")]
public string SymbolAfter
{
get { return symbolAfter; }
set
{
symbolAfter = value;
this.Invalidate();
}
}
[Category("RJ Code Advance")]
public bool ShowMaximun
{
get { return showMaximun; }
set
{
showMaximun = value;
this.Invalidate();
}
}
[Category("RJ Code Advance")]
[Browsable(true)]
[EditorBrowsable(EditorBrowsableState.Always)]
public override Font Font
{
get { return base.Font; }
set
{
base.Font = value;
}
}
[Category("RJ Code Advance")]
public override Color ForeColor
{
get { return base.ForeColor; }
set
{
base.ForeColor = value;
}
}5.- Pintar el fondo y el canal
Para pintar el fondo y el canal de la barra de progreso personalizada, debe anular el método de evento OnPaintBackground y realizar los dibujos o la pintura como se muestra en el siguiente código.
//-> Paint the background & channel
protected override void OnPaintBackground(PaintEventArgs pevent)
{
if (stopPainting == false)
{
if (paintedBack == false)
{
//Fields
Graphics graph = pevent.Graphics;
Rectangle rectChannel = new Rectangle(0, 0, this.Width, ChannelHeight);
using (var brushChannel = new SolidBrush(channelColor))
{
if (channelHeight >= sliderHeight)
rectChannel.Y = this.Height - channelHeight;
else rectChannel.Y = this.Height - ((channelHeight + sliderHeight) / 2);
//Painting
graph.Clear(this.Parent.BackColor);//Surface
graph.FillRectangle(brushChannel, rectChannel);//Channel
//Stop painting the back & Channel
if (this.DesignMode == false)
paintedBack = true;
}
}
//Reset painting the back & channel
if (this.Value == this.Maximum || this.Value == this.Minimum)
paintedBack = false;
}
}6.- Pintar el deslizador o indicador
En este caso, para pintar el deslizador de la barra de progreso personalizada, debe anular el método de evento OnPaint como se muestra en el siguiente código.
//-> Paint slider
protected override void OnPaint(PaintEventArgs e)
{
if (stopPainting == false)
{
//Fields
Graphics graph = e.Graphics;
double scaleFactor = (((double)this.Value - this.Minimum) / ((double)this.Maximum - this.Minimum));
int sliderWidth = (int)(this.Width * scaleFactor);
Rectangle rectSlider = new Rectangle(0, 0, sliderWidth, sliderHeight);
using (var brushSlider = new SolidBrush(sliderColor))
{
if (sliderHeight >= channelHeight)
rectSlider.Y = this.Height - sliderHeight;
else rectSlider.Y = this.Height - ((sliderHeight + channelHeight) / 2);
//Painting
if (sliderWidth > 1) //Slider
graph.FillRectangle(brushSlider, rectSlider);
if (showValue != TextPosition.None) //Text
DrawValueText(graph, sliderWidth, rectSlider);
}
}
if (this.Value == this.Maximum) stopPainting = true;//Stop painting
else stopPainting = false; //Keep painting
}7.- Pintar el texto del valor
Crear el siguiente método e invocarlo en el método de evento OnPaint, como se muestra en el bloque de arriba y en el siguiente código.
//-> Paint value text
private void DrawValueText(Graphics graph, int sliderWidth, Rectangle rectSlider)
{
//Fields
string text = symbolBefore + this.Value.ToString() + symbolAfter;
if (showMaximun) text = text + "/" + symbolBefore + this.Maximum.ToString() + symbolAfter;
var textSize = TextRenderer.MeasureText(text, this.Font);
var rectText = new Rectangle(0, 0, textSize.Width, textSize.Height + 2);
using (var brushText = new SolidBrush(this.ForeColor))
using (var brushTextBack = new SolidBrush(foreBackColor))
using (var textFormat = new StringFormat())
{
switch (showValue)
{
case TextPosition.Left:
rectText.X = 0;
textFormat.Alignment = StringAlignment.Near;
break;
case TextPosition.Right:
rectText.X = this.Width - textSize.Width;
textFormat.Alignment = StringAlignment.Far;
break;
case TextPosition.Center:
rectText.X = (this.Width - textSize.Width) / 2;
textFormat.Alignment = StringAlignment.Center;
break;
case TextPosition.Sliding:
rectText.X = sliderWidth - textSize.Width;
textFormat.Alignment = StringAlignment.Center;
//Clean previous text surface
using (var brushClear = new SolidBrush(this.Parent.BackColor))
{
var rect = rectSlider;
rect.Y = rectText.Y;
rect.Height = rectText.Height;
graph.FillRectangle(brushClear, rect);
}
break;
}
//Painting
graph.FillRectangle(brushTextBack, rectText);
graph.DrawString(text, this.Font, brushText, rectText, textFormat);
}
}