52

I have used ProgressBar Control in my c# desktop application.I have used it in a thread other then the thread in which control has been declared.Its working Fine. Now I am wondering how i can show some text inside progress bar control like "Initiating Registration" etc.Also I want to use it as Marquee progress bar.Please help me.

Pieniadz
  • 653
  • 3
  • 9
  • 22
Sandeep Pathak
  • 10,567
  • 8
  • 45
  • 57

9 Answers9

71

You will have to override the OnPaint method, call the base implementation and the paint your own text.

You will need to create your own CustomProgressBar and then override OnPaint to draw what ever text you want.

Custom Progress Bar Class

namespace ProgressBarSample
{

public enum ProgressBarDisplayText
{
    Percentage,
    CustomText
}

class CustomProgressBar: ProgressBar
{
    //Property to set to decide whether to print a % or Text
    public ProgressBarDisplayText DisplayStyle { get; set; }

    //Property to hold the custom text
    public String CustomText { get; set; }

    public CustomProgressBar()
    {
        // Modify the ControlStyles flags
        //http://msdn.microsoft.com/en-us/library/system.windows.forms.controlstyles.aspx
        SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        Rectangle rect = ClientRectangle;
        Graphics g = e.Graphics;

        ProgressBarRenderer.DrawHorizontalBar(g, rect);
        rect.Inflate(-3, -3);
        if (Value > 0)
        {
            // As we doing this ourselves we need to draw the chunks on the progress bar
            Rectangle clip = new Rectangle(rect.X, rect.Y, (int)Math.Round(((float)Value / Maximum) * rect.Width), rect.Height);
            ProgressBarRenderer.DrawHorizontalChunks(g, clip);
        }

        // Set the Display text (Either a % amount or our custom text
        int percent = (int)(((double)this.Value / (double)this.Maximum) * 100); 
        string text = DisplayStyle == ProgressBarDisplayText.Percentage ? percent.ToString() + '%' : CustomText;            

        using (Font f = new Font(FontFamily.GenericSerif, 10))
        {

            SizeF len = g.MeasureString(text, f);
            // Calculate the location of the text (the middle of progress bar)
            // Point location = new Point(Convert.ToInt32((rect.Width / 2) - (len.Width / 2)), Convert.ToInt32((rect.Height / 2) - (len.Height / 2)));
            Point location = new Point(Convert.ToInt32((Width / 2) - len.Width / 2), Convert.ToInt32((Height / 2) - len.Height / 2)); 
            // The commented-out code will centre the text into the highlighted area only. This will centre the text regardless of the highlighted area.
            // Draw the custom text
            g.DrawString(text, f, Brushes.Red, location);
        }
    }
}
}

Sample WinForms Application

using System;
using System.Linq;
using System.Windows.Forms;
using System.Collections.Generic;

namespace ProgressBarSample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            // Set our custom Style (% or text)
            customProgressBar1.DisplayStyle = ProgressBarDisplayText.CustomText;
            customProgressBar1.CustomText = "Initialising";
        }

        private void btnReset_Click(object sender, EventArgs e)
        {
            customProgressBar1.Value = 0;
            btnStart.Enabled = true;
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            btnReset.Enabled = false;
            btnStart.Enabled = false;

            for (int i = 0; i < 101; i++)
            {

                customProgressBar1.Value = i;
                // Demo purposes only
                System.Threading.Thread.Sleep(100);

                // Set the custom text at different intervals for demo purposes
                if (i > 30 && i < 50)
                {
                    customProgressBar1.CustomText = "Registering Account";
                }

                if (i > 80)
                {
                    customProgressBar1.CustomText = "Processing almost complete!";
                }

                if (i >= 99)
                {
                    customProgressBar1.CustomText = "Complete";
                }
            }
                        
            btnReset.Enabled = true;


        }

   
    }
}
Raithlin
  • 1,764
  • 10
  • 19
codingbadger
  • 42,678
  • 13
  • 95
  • 110
  • Any examples of this working? I can never get anything I add to OnPaint to actually show. – Matthew Lock Feb 24 '12 at 00:27
  • 2
    @MatthewLock: I've added code for the Custom Progress Bar class and Sample Application. Hope this helps – codingbadger Feb 24 '12 at 08:38
  • Please add "this.Controls.Add(customProgressBar1);" to the Main1() constructor. – Marcin Barylski May 14 '14 at 16:47
  • 1
    Thank's for solution. But what if progressbar style = Marquee? Text is showing, but progressbar is freeze. – streamdown Dec 06 '14 at 21:41
  • I had just come here to comment and saw that @streamdown had already mentioned it. Any way to create the marquee and stop flicker? – Stuart Apr 11 '15 at 10:17
  • Why is this not the accepted answer, I have no idea, but it has sample code that can help any who are still confused :) – Kaitlyn Aug 28 '15 at 19:29
  • Hi codingbadger, thanks for your solution! It's great! But how can I disable visual style? I tried http://stackoverflow.com/a/299983/1451100 but it won't change anything. Can you help me with this? – AllDayPiano Feb 11 '16 at 08:54
  • 3
    Great code! this should be the answer @Muse. Two things: correct the percentage calculation ---> int percent = (int)(((double)this.Value / (double)this.Maximum) * 100); string text = DisplayStyle == ProgressBarDisplayText.Percentage ? percent.ToString() + '%' : CustomText; ------------- and second: ----- TO STOP FLICKERING, as suggested by KraangPrime add ---> SetStyle(ControlStyles.OptimizedDoubleBuffer, true); – Leo Gurdian Apr 13 '17 at 22:19
  • Found this custom progressbar at codeproject site: https://www.codeproject.com/Articles/1082902/How-to-Paint-on-Top-of-a-ProgressBar-using-Csharp – AER 4AF Sep 19 '22 at 01:07
32

I have written a no blinking/flickering TextProgressBar

You can find the source code here: https://github.com/ukushu/TextProgressBar

enter image description here

WARNING: It's a little bit buggy! But still, I think it's better than another answers here. As I have no time for fixes, if you will do sth with them, please send me update by some way:) Thanks.

Samples:

enter image description here enter image description here no blinking/flickering TextProgressBar

no blinking/flickering TextProgressBar enter image description here enter image description here

Andrew_STOP_RU_WAR_IN_UA
  • 9,318
  • 5
  • 65
  • 101
16

AVOID FLICKERING TEXT

The solution provided by Barry above is excellent, but there's is the "flicker-problem".

As soon as the Value is above zero the OnPaint will be envoked repeatedly and the text will flicker.

There is a solution to this. We do not need VisualStyles for the object since we will be drawing it with our own code.

Add the following code to the custom object Barry wrote and you will avoid the flicker:

    [DllImportAttribute("uxtheme.dll")]
    private static extern int SetWindowTheme(IntPtr hWnd, string appname, string idlist);

    protected override void OnHandleCreated(EventArgs e)
    {
        SetWindowTheme(this.Handle, "", "");
        base.OnHandleCreated(e);
    }

I did not write this myself. It found it here: https://stackoverflow.com/a/299983/1163954

I've testet it and it works.

Community
  • 1
  • 1
rdongart
  • 390
  • 3
  • 10
  • 12
    You can also avoid flickering by adding `SetStyle( ControlStyles.OptimizedDoubleBuffer, true );` to the control initialization. – Kraang Prime Apr 03 '16 at 18:19
  • +1 @KraangPrime - thank you! that does it right there, no flickering. No need to rely on another external library. – Leo Gurdian Apr 13 '17 at 22:11
  • @KraangPrime I tried adding that line instead of the import that was in this answer and my program bombed out when attempting to display. Not sure why this would happen, but thought I would mention it. – vipersassassin Dec 13 '17 at 16:00
  • To clarify @KraangPrime's comment, add ```SetStyle( ControlStyles.OptimizedDoubleBuffer, true);``` to the ```public CustomProgressBar()``` constructor method. Works like a charm! – Billy Raseman Feb 21 '19 at 00:09
11

I wold create a control named for example InfoProgresBar, that provide this functionality with a label or two (Main Job, Current Job) and ProgressBar and use it instead of that ProgressBar.

8

I have used this simple code, and it works!

for (int i = 0; i < N * N; i++)
     {
        Thread.Sleep(50);
        progressBar1.BeginInvoke(new Action(() => progressBar1.Value = i));
        progressBar1.CreateGraphics().DrawString(i.ToString() + "%", new Font("Arial",
        (float)10.25, FontStyle.Bold),
        Brushes.Red, new PointF(progressBar1.Width / 2 - 10, progressBar1.Height / 2 - 7));
     }

It just has one simple problem and this is it: when progress bar start to rising, percentage some times hide, and then appear again. I did't write it myself.I found it here: text on progressbar in c#

I used this code, and it does work.

Community
  • 1
  • 1
pooria haddad
  • 795
  • 2
  • 8
  • 24
  • 2
    This is bad because it leaks the Graphics object, and won't redraw after being obscured. – Ben Voigt Apr 24 '14 at 20:18
  • Technically this doesn't "leak" per se as it's all managed; the reference count on the Graphics object will hit zero immediately after that line, leaving it to be garbage-collected. I do agree that it could be handled better, though. – Brian A. Henning Jan 08 '15 at 21:41
4

I tried placing a label with transparent background over a progress bar but never got it to work properly. So I found Barry's solution here very useful, although I missed the beautiful Vista style progress bar. So I merged Barry's solution with http://www.dreamincode.net/forums/topic/243621-percent-into-progress-bar/ and managed to keep the native progress bar, while displaying text percentage or custom text over it. I don't see any flickering in this solution either. Sorry to dig up and old thread but I needed this today and so others may need it too.

public enum ProgressBarDisplayText
{
    Percentage,
    CustomText
}

class ProgressBarWithCaption : ProgressBar
{
    //Property to set to decide whether to print a % or Text
    private ProgressBarDisplayText m_DisplayStyle;
    public ProgressBarDisplayText DisplayStyle {
        get { return m_DisplayStyle; }
        set { m_DisplayStyle = value; }
    }

    //Property to hold the custom text
    private string m_CustomText;
    public string CustomText {
        get { return m_CustomText; }
        set {
            m_CustomText = value;
            this.Invalidate();
        }
    }

    private const int WM_PAINT = 0x000F;
    protected override void WndProc(ref Message m)
    {
        base.WndProc(m);

        switch (m.Msg) {
            case WM_PAINT:
                int m_Percent = Convert.ToInt32((Convert.ToDouble(Value) / Convert.ToDouble(Maximum)) * 100);
                dynamic flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.WordEllipsis;

                using (Graphics g = Graphics.FromHwnd(Handle)) {
                    using (Brush textBrush = new SolidBrush(ForeColor)) {

                        switch (DisplayStyle) {
                            case ProgressBarDisplayText.CustomText:
                                TextRenderer.DrawText(g, CustomText, new Font("Arial", Convert.ToSingle(8.25), FontStyle.Regular), new Rectangle(0, 0, this.Width, this.Height), Color.Black, flags);
                                break;
                            case ProgressBarDisplayText.Percentage:
                                TextRenderer.DrawText(g, string.Format("{0}%", m_Percent), new Font("Arial", Convert.ToSingle(8.25), FontStyle.Regular), new Rectangle(0, 0, this.Width, this.Height), Color.Black, flags);
                                break;
                        }

                    }
                }

                break;
        }

    }

}
Community
  • 1
  • 1
user627283
  • 533
  • 5
  • 19
  • 1
    We added an enum ProgressBarDisplayText.CurrentValue String.Format("{0} / {1}", Value, MaximumValue) – JJS Jul 22 '15 at 19:17
1

Just want to point out something on @codingbadger answer. When using "ProgressBarRenderer" you should always check for "ProgressBarRenderer.IsSupported" before using the class. For me, this has been a nightmare with Visual Styles errors in Win7 that I couldn't fix. So, a better approach and workaround for the solution would be:

Rectangle clip = new Rectangle(rect.X, rect.Y, (int)Math.Round(((float)Value / Maximum) * rect.Width), rect.Height);
if (ProgressBarRenderer.IsSupported)
  ProgressBarRenderer.DrawHorizontalChunks(g, clip);
else
  g.FillRectangle(new SolidBrush(this.ForeColor), clip);

Notice that the fill will be a simple rectangle and not chunks. Chunks will be used only if ProgressBarRenderer is supported

kikocastroneto
  • 136
  • 1
  • 3
0

I have created a InfoProgressBar control which uses a TransparentLabel control. Testing on a form with a Timer, I get some slight glitches displaying the text every 30-40 value changes if using a timer interval of less than 250 milliseconds (probably because of the time required to update the screen is greater than the timer interval).

It would be possible to modify UpdateText method to insert all the calculated values into CustomText but it isn't something that I have needed yet. This would remove the need for the DisplayType property and enumerate.

The TransparentLabel class was created by adding a new UserControl and changing it to inheriting from Label with the following implementation:

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

namespace Utils.GUI
{
    public partial class TransparentLabel : Label
    {
        // hide the BackColor attribute as much as possible.
        // setting the base value has no effect as drawing the
        // background is disabled
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override Color BackColor
        {
            get
            {
                return Color.Transparent;
            }
            set
            {
            }
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x20; //  WS_EX_TRANSPARENT
                return cp;
            }
        }

        public override string Text
        {
            get
            {
                return base.Text;
            }
            set
            {
                base.Text = value;
                if(Parent != null) Parent.Invalidate(Bounds, false);
            }
        }

        public override ContentAlignment TextAlign
        {
            get
            {
                return base.TextAlign;
            }
            set
            {
                base.TextAlign = value;
                if(Parent != null) Parent.Invalidate(Bounds, false);
            }
        }

        public TransparentLabel()
        {
            InitializeComponent();

            SetStyle(ControlStyles.Opaque, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, false);

            base.BackColor = Color.Transparent;
        }

        protected override void OnMove(EventArgs e)
        {
            base.OnMove(e);
            RecreateHandle();
        }

        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
            // do nothing
        }
    }
}

I did not make any changes to the related designer code but here it is for completeness.

namespace Utils.GUI
{
    partial class TransparentLabel
    {
        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if(disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Component Designer generated code

        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
        }

        #endregion
    }
}

I then created another new UserControl and changed it to derive from ProgressBar with the following implementation:

using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Windows.Forms.Design.Behavior;

namespace Utils.GUI
{
    [Designer(typeof(InfoProgressBarDesigner))]
    public partial class InfoProgressBar : ProgressBar
    {
        // designer class to add font baseline snapline by copying it from the label
        private class InfoProgressBarDesigner : ControlDesigner
        {
            public override IList SnapLines
            {
                get
                {
                    IList snapLines = base.SnapLines;

                    InfoProgressBar control = Control as InfoProgressBar;

                    if(control != null)
                    {
                        using(IDesigner designer = TypeDescriptor.CreateDesigner(control.lblText, typeof(IDesigner)))
                        {
                            if(designer != null)
                            {
                                designer.Initialize(control.lblText);

                                ControlDesigner boxDesigner = designer as ControlDesigner;

                                if(boxDesigner != null)
                                {
                                    foreach(SnapLine line in boxDesigner.SnapLines)
                                    {
                                        if(line.SnapLineType == SnapLineType.Baseline)
                                        {
                                            snapLines.Add(new SnapLine(SnapLineType.Baseline, line.Offset, line.Filter, line.Priority));
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }

                    return snapLines;
                }
            }
        }

        // enum to select the type of displayed value
        public enum ProgressBarDisplayType
        {
            Custom = 0,
            Percent = 1,
            Progress = 2,
            Remain = 3,
            Value = 4,
        }

        private string _customText;
        private ProgressBarDisplayType _displayType;
        private int _range;

        [Bindable(false)]
        [Browsable(true)]
        [DefaultValue("{0}")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        // {0} is replaced with the result of the selected calculation
        public string CustomText
        {
            get
            {
                return _customText;
            }
            set
            {
                _customText = value;
                UpdateText();
            }
        }

        [Bindable(false)]
        [Browsable(true)]
        [DefaultValue(ProgressBarDisplayType.Percent)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        public ProgressBarDisplayType DisplayType
        {
            get
            {
                return _displayType;
            }
            set
            {
                _displayType = value;
                UpdateText();
            }
        }

        [Bindable(false)]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        // don't use the lblText font as if it is null, it checks the parent font (i.e. this property) and gives an infinite loop
        public override Font Font
        {
            get
            {
                return base.Font;
            }
            set
            {
                base.Font = value; 
            }
        }

        [Bindable(false)]
        [Browsable(true)]
        [DefaultValue(100)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        public new int Maximum
        {
            get
            {
                return base.Maximum;
            }
            set
            {
                base.Maximum = value;
                _range = base.Maximum - base.Minimum;
                UpdateText();
            }
        }

        [Bindable(false)]
        [Browsable(true)]
        [DefaultValue(0)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        public new int Minimum
        {
            get
            {
                return base.Minimum;
            }
            set
            {
                base.Minimum = value;
                _range = base.Maximum - base.Minimum;
                UpdateText();
            }
        }

        [Bindable(false)]
        [Browsable(true)]
        [DefaultValue(ContentAlignment.MiddleLeft)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        public ContentAlignment TextAlign 
        {
            get
            {
                return lblText.TextAlign;
            }
            set
            {
                lblText.TextAlign = value;
            }
        }

        [Bindable(false)]
        [Browsable(true)]
        [DefaultValue(typeof(Color), "0x000000")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        public Color TextColor
        {
            get
            {
                return lblText.ForeColor;
            }
            set
            {
                lblText.ForeColor = value;
            }
        }

        [Bindable(false)]
        [Browsable(true)]
        [DefaultValue(0)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        public new int Value
        {
            get
            {
                return base.Value;
            }
            set
            {
                base.Value = value;
                UpdateText();
            }
        }

        public InfoProgressBar()
        {
            InitializeComponent();

            CustomText = "{0}";
            DisplayType = ProgressBarDisplayType.Percent;
            Maximum = 100;
            Minimum = 0;
            TextAlign = ContentAlignment.MiddleLeft;
            TextColor = Color.Black;
            Value = 0;

            // means the label gets drawn in front of the progress bar
            lblText.Parent = this;

            _range = base.Maximum - base.Minimum;
        }

        protected void UpdateText()
        {
            switch(DisplayType)
            {
                case ProgressBarDisplayType.Custom:
                {
                    lblText.Text = _customText;
                    break;
                }
                case ProgressBarDisplayType.Percent:
                {
                    if(_range > 0)
                    {
                        lblText.Text = string.Format(_customText, string.Format("{0}%", (int)((Value * 100) / _range)));
                    }
                    else
                    {
                        lblText.Text = "100%";
                    }
                    break;
                }
                case ProgressBarDisplayType.Progress:
                {
                    lblText.Text = string.Format(_customText, (Value - Minimum));
                    break;
                }
                case ProgressBarDisplayType.Remain:
                {
                    lblText.Text = string.Format(_customText, (Maximum - Value));
                    break;
                }
                case ProgressBarDisplayType.Value:
                {
                    lblText.Text = string.Format(_customText, Value);
                    break;
                }
            }
        }

        public new void Increment(int value)
        {
            base.Increment(value);
            UpdateText();
        }

        public new void PerformStep()
        {
            base.PerformStep();
            UpdateText();
        }
    }
}

And the designer code:

namespace Utils.GUI
{
    partial class InfoProgressBar
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if(disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Component Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.lblText = new Utils.GUI.TransparentLabel();
            this.SuspendLayout();
            // 
            // lblText
            // 
            this.lblText.BackColor = System.Drawing.Color.Transparent;
            this.lblText.Dock = System.Windows.Forms.DockStyle.Fill;
            this.lblText.Location = new System.Drawing.Point(0, 0);
            this.lblText.Name = "lblText";
            this.lblText.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
            this.lblText.Size = new System.Drawing.Size(100, 23);
            this.lblText.TabIndex = 0;
            this.lblText.Text = "transparentLabel1";
            this.ResumeLayout(false);

        }

        #endregion

        private TransparentLabel lblText;

    }
}
Adam
  • 131
  • 1
  • 6
-1

Alliteratively you can try placing a Label control and placing it on top of the progress bar control. Then you can set whatever the text you want to the label. I haven't done this my self. If it works it should be a simpler solution than overriding onpaint.

thekindofme
  • 3,846
  • 26
  • 29
  • 3
    I found no way to make the label background transparent. Setting BackColour = Transparent left it grey. – ChrisJJ Sep 13 '11 at 22:02