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); } }