Multi-color Text in ProgressBar

The standard ProgressBar class provided by the .NET Framework does not have a way to write custom text over the progress bar. However, it can still be achieved by extending from the standard ProgressBar and by implementing your own OnPaint event handler.

The other thing to keep in mind is the color of the displayed text, because its background will be a combination of 2 colors: the section without the progress bar (BackColor – usually a lighter color) and the section with the progress bar (ForeColor – usually a darker color). So you need to make sure that the text will be clearly visible on either background color.

One way to deal with this is to display the text in both colors. The text section that has the light background (no progress bar) will be displayed in a dark color, and the text section on the dark background (with progress bar) will have a light color.

To accomplish this effect, we create 2 bitmaps. One that represents the light text (Brushes.White) on the dark background (with progressbar – see Bitmap b1) and another that represents the dark text (Brushes.Black) on the light background (no progress bar – see Bitmap b2).

Lastly, the appropriate Rectangle sections of the 2 Bitmaps are copied to their corresponding Rectangle sections on the Graphics instance of the ProgressBar.

The following C# code shows all the relevant pieces that you can copy to the appropriate places in your Form code.

namespace My.Project
{
  public class MyForm : Form
  {
    private CustomProgressBar progressBar;

    public MyForm()
    {
      InitializeComponent();
    }

    private void InitializeComponent()
    {
      this.progressBar = new CustomProgressBar();

      // 
      // progressBar
      // 
      this.progressBar.LabelText = null;
      this.progressBar.Location = new Point(0, 0);
      this.progressBar.Name = "progressBar";
      this.progressBar.Size = new Size(300, 25);
      this.progressBar.Style = ProgressBarStyle.Continuous;
    }

    private void setProgressBarText(string text)
    {
      progressBar.LabelText = text;
      progressBar.Invalidate();
    }

    private void setProgressBarValue(int pct)
    {
      progressBar.Value = pct;
    }

    class CustomProgressBar : ProgressBar
    {
      private string labelText;

      public string LabelText
      {
        get { return labelText; }
        set { labelText = value; }
      }

      public CustomProgressBar() : base()
      { 
         this.SetStyle(ControlStyles.UserPaint, true);
         this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
         this.SetStyle(ControlStyles.DoubleBuffer, true);
         this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
         this.Paint += OnPaint;
      }

      private void OnPaint(object sender, PaintEventArgs e)
      {
        Graphics gr = e.Graphics;
        int bw = e.ClipRectangle.Width * this.Value / this.Maximum;
        SolidBrush foreColor = new SolidBrush(this.ForeColor);

        if (string.IsNullOrEmpty(labelText))
        {
          gr.FillRectangle(foreColor, e.ClipRectangle.X,
            e.ClipRectangle.Y, bw, e.ClipRectangle.Height);
        }
        else
        {
          Font textFont = SystemFonts.DefaultFont;
          float left = 5f;
          float top = (this.Height >> 1)
            - (gr.MeasureString(LabelText, textFont).Height / 2.0F);
          PointF labelPos = new PointF(left, top);

          Bitmap b1 = new Bitmap(e.ClipRectangle.Width, e.ClipRectangle.Height);
          Graphics gr1 = Graphics.FromImage(b1);
          gr1.FillRectangle(foreColor, e.ClipRectangle.X,
            e.ClipRectangle.Y, bw, e.ClipRectangle.Height);
          gr1.DrawString(LabelText, textFont, Brushes.White, labelPos);
          Rectangle r1 = new Rectangle(0, 0, bw, e.ClipRectangle.Height);

          Bitmap b2 = new Bitmap(e.ClipRectangle.Width, e.ClipRectangle.Height);
          Graphics gr2 = Graphics.FromImage(b2);
          gr2.FillRectangle(new SolidBrush(this.BackColor), bw,
            e.ClipRectangle.Y, e.ClipRectangle.Width - bw, e.ClipRectangle.Height);
          gr2.DrawString(LabelText, textFont, Brushes.Black, labelPos);
          Rectangle r2 = new Rectangle(bw, 0,
            e.ClipRectangle.Width - bw, e.ClipRectangle.Height);

          gr.DrawImage(b1, r1, r1, GraphicsUnit.Pixel);
          gr.DrawImage(b2, r2, r2, GraphicsUnit.Pixel);
        }
      }
    }
  }
}

Leave a Reply

Your email address will not be published. Required fields are marked *

*