Tuesday, October 05, 2010

WinForms ProgressBar with text

Progress bar with textSomebody asked how to use my transparent label on top of a ProgressBar control, since it disappeared when the progress bar changed its state. I had a look at it, but the transparent label wasn’t getting any notification of changes, even when I hooked into the WndProc method. So I wondered if it would be possible to sub-class the ProgressBar control and write some text on top of it that way. And that seems to work OK, as shown below.

Update – I’ve fixed the flickering

Update 2 – It now picks up the text colour from the ForeColor property

Update 3 – Should now work on Windows XP

Update 4 – Now lets you specify the font used for the text

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

namespace WinFormControls
{
 
  public class TextProgressBar : ProgressBar
  {
    protected override CreateParams CreateParams
    {
      get
      {
        CreateParams result = base.CreateParams;
        if (Environment.OSVersion.Platform == PlatformID.Win32NT
            && Environment.OSVersion.Version.Major >= 6)
        {
          result.ExStyle |= 0x02000000; // WS_EX_COMPOSITED 
        }

        return result;
      }
    }

    protected override void WndProc(ref Message m)
    {
      base.WndProc(ref m);
      if (m.Msg == 0x000F)
      {
        using (Graphics graphics = CreateGraphics())
        using (SolidBrush brush = new SolidBrush(ForeColor))
        {
          SizeF textSize = graphics.MeasureString(Text, Font);
          graphics.DrawString(Text, Font, brush, (Width - textSize.Width) / 2, (Height - textSize.Height) / 2);
        }
      }
    }

    [EditorBrowsable(EditorBrowsableState.Always)]
    [Browsable(true)]
    public override string Text
    {
      get
      {
        return base.Text;
      }
      set
      {
        base.Text = value;
        Refresh();
      }
    }

    [EditorBrowsable(EditorBrowsableState.Always)]
    [Browsable(true)]
    public override Font Font
    {
      get
      {
        return base.Font;
      }
      set
      {
        base.Font = value;
        Refresh();
      }
    }
  }
}

17 comments:

  1. Could you please explain me how do you use/implement this text on ProgressBar?

    I appreciate your help.

    Thanks.

    ReplyDelete
  2. Just copy the code to a new C# file and use the TextProgressBar class instead of the ProgressBar class in your application.

    ReplyDelete
  3. Great contro! but is there a solution for the flickering?

    ReplyDelete
  4. Indeed there is, I figured it out and it flickers no more

    ReplyDelete
  5. Excellent! works perfectly!
    Is there a also a way to change the text color?

    ReplyDelete
  6. There is now, just use the ForeColor property

    ReplyDelete
  7. Hi,

    Useful mod, works fine on Win7, but on win XP the label is not visible?? Any ideas?

    Danny

    ReplyDelete
  8. Hi,

    Great Mod. Works Fine on Win 7 PCs.

    Unfortunately it does not work on XP machines, the label is not visible.

    Any ideas why? Perhaps the Win7 Visual styles??

    Cheers

    Danny

    ReplyDelete
  9. Unfortunately I don't have access to an XP machine to investigate. Maybe the progress bar is implemented differently there whcih is causing the problem? Maybe try removing the CreateParams as well to see if that's causing the problem (might start flickering a bit though)

    ReplyDelete
  10. Hi,

    Sorry for the double post. Feel free to delete the first one.

    You were right about the CreateParams.

    I did a bit a trawling on the net and read somewhere that simply apply the command to Vista upwards.


    http://stackoverflow.com/questions/1282911/windows-form-paints-repeatedly-in-xp-but-not-in-vista


    protected override CreateParams CreateParams
    {
    get
    {
    CreateParams result = base.CreateParams;
    if (Environment.OSVersion.Platform == PlatformID.Win32NT
    && Environment.OSVersion.Version.Major >= 6)
    {
    result.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
    }

    return result;
    }
    }

    ReplyDelete
  11. great code , it worked perfectly. i was able to figure out how to change the color of the text half way through using get/set on ForeColor... i am new to C Sharp so i am learning, but i can't seem to figure out how to make the text font BOLD or a Different font type as well???

    ReplyDelete
  12. You need to change the reference to SystemFonts.DefaultFont to your own choice of font. I'll add it as a property of the control at some point, but I'm going to be busy for the next few days

    ReplyDelete
  13. Thanks for this great control. It helped me a lot. In my project I had about 10 progressbars in different places showing progress of very long operations. Just changed reference in design.cs and have Text property! Awesome piece of code!

    ReplyDelete
  14. Thanks for this great control. It helped me a lot as well

    ReplyDelete
  15. Thanks for the code. But somehow, when I test this on a loop from 1 to 10000, the text gets to display 10000 before the progress bar reaches to its maximum. Here is the simple code that I have:

    ProgressBar1.Minimum = 0;
    ProgressBar1.Value = 0;
    ProgressBar1.Maximum = 10000;
    ProgressBar1.Text = "";

    for (int iIdx = 1; iIdx <= ProgressBar1.Maximum; iIdx++)
    {
    ProgressBar1.Text = "Item " + iIdx.ToString() + " of " + ProgressBar1.Maximum.ToString() + " ...";
    ProgressBar1.Value = iIdx;
    }

    For hundreds of itertaions at the end, the text says Item 10000 of 10000 ... while the progress bar clearly has not finished yet. Something is really not working.

    Also, I have seen that in some compilers versions, it is complaining about the overridden Text property. The compiler is throwing an error saying that the Text property cannot be overriden because it is not abstract, virtual etc. Just thought putting this out ther.

    ReplyDelete
  16. To be honest it years since I've looked at this code. You may want to put an Application.ProcessMessages() in your loop to ensure everything is getting repainted which might fix it

    ReplyDelete