Page 1 of 1

Add the percent into a progress bar (Updated) Also can be used to add in any text

#1 jacobjordan  Icon User is offline

  • class Me : Perfection
  • member icon

Reputation: 113
  • View blog
  • Posts: 1,499
  • Joined: 11-June 08

Posted 22 March 2009 - 01:28 PM

This tutorial discusses how to add text (like the percent as mentioned in the title, but you could add anything you please) into the middle of a progress bar. Instead of paying for some overpriced .NET control, you can accomplish this yourself in a very simple manner. I tried to accomplish this about a year ago when I wasn't a very good programmer at all, and failed. My attempt there was to have a label in the center of the progress bar and set its background color to transparent. However, that does not work because when the background color of a control is transparent, it will show what is behind it in its container. Since the label is contained in the form itself and not in the progress bar, that would result in you seeing through the label and the progress bar straight into the form's background color or background image. So, a label in the progress bar is out, what's next? The solution that I came up with is to use the progress bar's graphics to draw a string into the center of the progress bar.

Here are a few pictures that demonstrate a possible application of what i have just said:

Attached Image
(Center-Justified percent in 12pt Segoe UI font)

Attached Image
(Center-Justified custom text also in 12pt Segoe UI font)

Attached Image
(Left-Justified custom text in red 10pt Segoe UI font)

Those are actual cropped screenshots from my application.

The Code

The original code I used to do this only took 2 lines of code. However, it had some bugs in it, and this is exactly what I use now:
int percent = (int)(((double)(progressBar1.Value - progressBar1.Minimum) /
(double)(progressBar1.Maximum - progressBar1.Minimum)) * 100);
using (Graphics gr = progressBar1.CreateGraphics())
{
    gr.DrawString(percent.ToString() + "%",
        SystemFonts.DefaultFont,
        Brushes.Black,
        new PointF(progressBar1.Width / 2 - (gr.MeasureString(percent.ToString() + "%",
            SystemFonts.DefaultFont).Width / 2.0F),
        progressBar1.Height / 2 - (gr.MeasureString(percent.ToString() + "%",
            SystemFonts.DefaultFont).Height / 2.0F)));
}


Let's analyze that. The percent variable is the calculated percent of the progress bar's value. Now, the next line is a bit more complicated. First, it is using the DrawString() method of the System.Drawing.Graphics class to draw text in the progress bar. Let's analyze each argument:

1. percent.ToString() + "%"

That's really self-explanatory. It says what text will be displayed.

2. SystemFonts.DefaultFont

That is the font that is displayed. In my example screenshot, I used Segoe UI. However, this is probably a better choice for an application because it is getting the default system font, but you can make it whatever you want.

3. Brushes.Black

That is the color of the text which can be easily changed. It can be a SolidBrush, which is a brush of a solid color. Or, if you want to get fancy, it can be another type of brush (for example, a TextureBrush, which has a texture defined by an image to assign it).


5. (Code Below)
      new PointF(progressBar1.Width / 2 - (gr.MeasureString(percent.ToString()  + "%",
      SystemFonts.DefaultFont).Width / 2.0F),
      progressBar1.Height / 2 - (gr.MeasureString(percent.ToString()  + "%",
      SystemFonts.DefaultFont).Height / 2.0F))   


That is where the text will be drawn in relation to the progress bar's upper-left corner. It defines the upper-left corner of where the text will be drawn. This is the most important part and needs the most explanation. Let's take a look at how I arrived at those equations to center the text:

------X Position
      progressBar1.Width / 2 - (gr.MeasureString(percent.ToString()  + "%", 
      		SystemFonts.DefaultFont).Width / 2.0F)


The progressBar1.Width / 2 part will return the center point (in pixels) of the progress bar horizontally. Now, the part using the MeasureString calculates the width (in pixels) of the string to be drawn (percent.ToString() + "%"). It then halves that value and subtracts it from progressBar1.Width / 2, so that the final X position will position the text's center point exactly in the progress bar's center point.

------Y Position
      progressBar1.Height / 2 - (gr.MeasureString(percent.ToString()  + "%", 
      		SystemFonts.DefaultFont).Height / 2.0F) 


As with the X point, progressBar1.Height / 2 calculates the center point of the progress bar vertically. Also like the X point, the MeasureString method is used to calculate the height of the text. It is then being halved, and subtracted from "progressBar1.Height / 2", to perfectly center the text vertically.

Now, to make all this easier on you, i have also created a method called SetProgressBarText that does all this automatically, which you can just copy/paste into your class if you like. With this method, i also added an option to be able to left-justify the text as well. Here it is

        /// <summary>
        /// Adds text into a System.Windows.Forms.ProgressBar
        /// </summary>
        /// <param name="Target">The target progress bar to add text into</param>
        /// <param name="Text">The text to add into the progress bar. 
        /// Leave null or empty to automatically add the percent.</param>
        /// <param name="Location">Where the text is to be placed</param>
        /// <param name="TextColor">The color the text should be drawn in</param>
        /// <param name="TextFont">The font the text should be drawn in</param>
        private void SetProgressBarText
            (
            System.Windows.Forms.ProgressBar Target, //The target progress bar
            string Text, //The text to show in the progress bar
            ProgressBarTextLocation Location, //Where the text is to be placed
            System.Drawing.Color TextColor, //The color the text is to be drawn in
            System.Drawing.Font TextFont //The font we use to draw the text
            ) 
        {

            //Make sure we didn't get a null progress bar
            if (Target == null) { throw new ArgumentException("Null Target"); }

            //Now we can get to the real code

            //Check to see if we are to add in the percent
            if (string.IsNullOrEmpty(Text))
            {
                //We are to add in the percent meaning we got a null or empty Text
                //We give text a string value representing the percent
                int percent = (int)(((double)(Target.Value - Target.Minimum) /
                    (double)(Target.Maximum - Target.Minimum)) * 100);
                Text = percent.ToString() + "%";
            }

            //Now we can add in the text

            //gr will be the graphics object we use to draw on Target
            using (Graphics gr = Target.CreateGraphics())
            {
                gr.DrawString(Text,
                    TextFont, //The font we will draw it it (TextFont)
                    new SolidBrush(TextColor), //The brush we will use to draw it

                    //Where we will draw it
                    new PointF(
                    // X location (Center or Left)
                        Location == ProgressBarTextLocation.Left ? 
                        5 : //Left side
                        progressBar1.Width / 2 - (gr.MeasureString(Text, //Centered
                        TextFont).Width / 2.0F),
                    // Y Location (This is the same regardless of Location)
                    progressBar1.Height / 2 - (gr.MeasureString(Text,
                        TextFont).Height / 2.0F)));
            }
        }

        public enum ProgressBarTextLocation
        {
            Left,
            Centered
        }



Conclusion

To actually use that method, you must call it after you change the progress bar's value. If for some reason, the changing of the progress bar's value doesn't refresh it and clear the previously drawn text, simply call the Refresh method of the progress bar before you call this method. All it takes is a copy/paste. Enjoy!

History

This is the second version of this tutorial.

Ver. 1 - Demonstrated how to add the percent into the center of a progress bar without taking into account the text's width or height. There were also a few other bugs in v1's code.

Ver. 2 - Demonstrated how to add any text into a a progress bar, left or center justified. Bugs were fixed from v1's code. A method was also included that could be directly copy/pasted into any class.

Is This A Good Question/Topic? 3
  • +

Replies To: Add the percent into a progress bar (Updated)

#2 LengIeng  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 09-April 09

Posted 09 April 2009 - 06:12 AM

Hi, thanks for that.

However, I cannot see CreateGraphics() of ToolStripProgressBar control.

Can you show me on how to draw string on it?
Was This Post Helpful? 0
  • +
  • -

#3 Amrykid  Icon User is offline

  • 4+1=Moo
  • member icon

Reputation: 148
  • View blog
  • Posts: 1,589
  • Joined: 16-December 08

Posted 11 April 2009 - 02:27 PM

View PostLengIeng, on 9 Apr, 2009 - 06:12 AM, said:

Hi, thanks for that.

However, I cannot see CreateGraphics() of ToolStripProgressBar control.

Can you show me on how to draw string on it?

toolstrip controls don't draw themselves, thats why it doesn't exist.
you need to use the toolstrip itself to draw on them, i think...
Was This Post Helpful? 0
  • +
  • -

#4 Renagado  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 117
  • View blog
  • Posts: 388
  • Joined: 14-June 09

Posted 03 July 2009 - 03:17 PM

Thanks, works like a charm!
Was This Post Helpful? 0
  • +
  • -

#5 Guest_indeepsoft*


Reputation:

Posted 20 March 2010 - 06:31 AM

View PostLengIeng, on 09 April 2009 - 05:12 AM, said:

Hi, thanks for that.

However, I cannot see CreateGraphics() of ToolStripProgressBar control.

Can you show me on how to draw string on it?


From MSDN:

Quote

ToolStripProgressBar is the ProgressBar optimized for hosting in a ToolStrip. A subset of the hosted control's properties and events are exposed at the ToolStripProgressBar level, but the underlying ProgressBar control is fully accessible through the ProgressBar property.


So this should be working:
progressBar1.ProgressBar.CreateGraphics()

Was This Post Helpful? 0

#6 Guest_Colin*


Reputation:

Posted 21 March 2010 - 08:11 AM

Thanks for that code.

I managed to convert it to vb.net using an online conversion process. One question. Is there any way to prevent the flickering which I presume is due to the percentage being painted onto the progress bar?

I did see some code on another forum which was hosting the above but I could not figure out how to use it as I am quite new to vidual basic.

Regards
Was This Post Helpful? 0

#7 Guest_indeepsoft*


Reputation:

Posted 22 March 2010 - 12:09 PM

@Colin

Maybe this will help. I found this writing on progressbar thing messy.
The good part is that text is much more readable, when it's not on a progressbar and vice versa :)
Otherwise you may find these useful: link 1 , link 2.
Was This Post Helpful? 0

#8 Guest_igal*


Reputation:

Posted 18 April 2010 - 07:39 AM

the text disappears after max value was reached, what change does it need in order that the text will remain in his place ?

thanks
Was This Post Helpful? 0

#9 Guest_firda*


Reputation:

Posted 18 April 2010 - 11:54 AM

Try this instead:

protected override void WndProc(ref Message m) {
	base.WndProc(ref m);
	if(m.Msg == 15) {
		string s = Text;
		Rectangle cr = ClientRectangle;
		using(Graphics g = CreateGraphics()) {
			using(StringFormat sf = new StringFormat()) {
				sf.Alignment = StringAlignment.Center;
				sf.LineAlignment = StringAlignment.Center;
				using(Brush br = new SolidBrush(TextColor)) {
					g.DrawString(s, Font, br,
						new RectangleF(cr.Left, cr.Top, cr.Width, cr.Height), sf);
				}
			}
		}
	}
}
private Color textColor = Color.Black;
public override string Text {
	get { return base.Text; }
	set { base.Text = value; }
}
public Color TextColor {
	get { return textColor; }
	set { textColor = value; }
}
public override Font Font {
	get { return base.Font; }
	set { base.Font = value; }
}



The problem is, that using ControlStyle.UserPaint will call your OnPaint, but won't call DefWndProc.
Using UserPaint and calling DefWndProc in WndProc does not help, probably because BeginPaint/EndPaint,
handling WM_PAINT=15 yourself and CREATING YOUR OWN GRAPHICS works perfectly.

P.S.: Change the "string s = Text" to whatever you want (e.g. percent computation).
Was This Post Helpful? 0

#10 Guest_firda*


Reputation:

Posted 19 April 2010 - 01:14 AM

public class ProgressLabel: ProgressBar {
	private static StringFormat sfCenter = new StringFormat() {
		Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
	private Color textColor = DefaultTextColor;
	private string progressString;
	public ProgressLabel() { SetStyle(ControlStyles.AllPaintingInWmPaint, true); }
	protected override void OnCreateControl() {
		progressString = null;
		base.OnCreateControl();
	}
	protected override void WndProc(ref Message m) {
		switch(m.Msg) {
		case 15: if(HideBar) base.WndProc(ref m);
			else {
				ProgressBarStyle style = Style;
				if(progressString == null) {
					progressString = Text;
					if(!HideBar && style != ProgressBarStyle.Marquee) {
						int range = Maximum-Minimum;
						int value = Value;
						if(range > 42949672) { value = (int)((uint)value>>7); range = (int)((uint)range>>7); }
						if(range > 0) progressString = string.Format(progressString.Length == 0 ? "{0}%" : "{1}: {0}%",
						value*100/range, progressString);
					}
				}
				if(progressString.Length == 0) base.WndProc(ref m);
				else using(Graphics g = CreateGraphics()) {
					base.WndProc(ref m);
					OnPaint(new PaintEventArgs(g, ClientRectangle));
				}
			}
			break;
		case 0x402: goto case 0x406;
		case 0x406: progressString = null;
			base.WndProc(ref m);
			break;
		default:
			base.WndProc(ref m);
			break;
		}
	}
	protected override void OnPaint(PaintEventArgs e) {
		Rectangle cr = ClientRectangle;
		RectangleF crF = new RectangleF(cr.Left, cr.Top, cr.Width, cr.Height);
		using(Brush br = new SolidBrush(TextColor))
			e.Graphics.DrawString(progressString, Font, br, crF, sfCenter);
		base.OnPaint(e);
	}
	public bool HideBar {
		get { return GetStyle(ControlStyles.UserPaint); }
		set { if(HideBar != value) { SetStyle(ControlStyles.UserPaint, value); Refresh(); } }
	}
	public static Color DefaultTextColor {
		get { return SystemColors.ControlText; }
	}
	public Color TextColor {
		get { return textColor; }
		set { textColor = value; }
	}
	public override string Text {
		get { return base.Text; }
		set { if(value != Text) { base.Text = value; progressString = null; } }
	}
	public override Font Font {
		get { return base.Font; }
		set { base.Font = value; }
	}
}


Was This Post Helpful? 1

#11 MagnusB  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 01-June 11

Posted 01 June 2011 - 05:51 AM

Hi jacobjordan, your code is really helpful.
But I still have questions.
- How can you make sure that the width (or length) of progress bar is long enough for the inserted text?
- What will happen, if the length of progress bar < text.width?

Thank you in advance.

Best regards,
Was This Post Helpful? 0
  • +
  • -

#12 erVerma  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 12-December 12

Posted 13 December 2012 - 01:16 AM

Hey Jacob, your code is really good, but the only issue I faced is that while showing text in progress bar, the text is shown after flickering. Is there something that can be done to prevent thaty flickering effect and give a sleak & smooth effect?

thanks for co-operation in advance...
Was This Post Helpful? 0
  • +
  • -

#13 Krioma  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 20-December 12

Posted 20 December 2012 - 04:40 PM

View Postjacobjordan, on 22 March 2009 - 01:28 PM, said:

My attempt there was to have a label in the center of the progress bar and set its background color to transparent. However, that does not work because when the background color of a control is transparent, it will show what is behind it in its container. Since the label is contained in the form itself and not in the progress bar, that would result in you seeing through the label and the progress bar straight into the form's background color or background image.


This "The main form color shows through behind a transparent image or label" issue is easily remedied with...
labelWhatever.Parent = progressbarPictureBox;



I had the same issue with transparency in a picturebox until a friend showed me this trick!
Was This Post Helpful? 0
  • +
  • -

#14 Michael26  Icon User is online

  • DIC-head, major DIC-head
  • member icon

Reputation: 353
  • View blog
  • Posts: 1,508
  • Joined: 08-April 09

Posted 02 March 2013 - 04:53 AM

@OP can this be made into extension method to be used in everyday(export it to DLL and then reference it when needed)
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1