【问题标题】:How do I put text on ProgressBar?如何在 ProgressBar 上放置文本?
【发布时间】:2011-04-01 14:10:41
【问题描述】:

我在我的 c# 桌面应用程序中使用了 ProgressBar 控件。我在一个线程中使用了它,而不是在其中声明了控件的线程。它工作正常。 现在我想知道如何在进度条控件中显示一些文本,例如“启动注册”等。我也想将它用作选取框进度条。请帮助我。

【问题讨论】:

标签: c# winforms progress-bar


【解决方案1】:

您必须重写 OnPaint 方法,调用基本实现并绘制您自己的文本。

您需要创建自己的CustomProgressBar,然后覆盖OnPaint 以绘制您想要的任何文本。

自定义进度条类

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

示例 WinForms 应用程序

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;


        }

   
    }
}

【讨论】:

  • 这个工作的任何例子?我永远无法得到我添加到 OnPaint 以实际显示的任何内容。
  • @MatthewLock:我已经为自定义进度条类和示例应用程序添加了代码。希望这会有所帮助
  • 请添加“this.Controls.Add(customProgressBar1);”到 Main1() 构造函数。
  • 感谢您的解决方案。但是如果进度条样式=选取框呢?文本正在显示,但进度条被冻结。
  • 很棒的代码!这应该是@Muse 的答案。两件事:更正百分比计算---> int percent = (int)(((double)this.Value / (double)this.Maximum) * 100);字符串文本 = DisplayStyle == ProgressBarDisplayText.Percentage ? percent.ToString() + '%' : CustomText; ------------- 和第二个: ----- 停止闪烁,正如 KraangPrime 建议的那样添加 ---> SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
【解决方案2】:

我写了一个不闪烁/闪烁 TextProgressBar

你可以在这里找到源代码:https://github.com/ukushu/TextProgressBar

警告:这有点儿马车!但是,我认为这比这里的另一个答案要好。由于我没有时间修复,如果您愿意与他们一起做某事,请通过某种方式向我发送更新:) 谢谢。

样品:

【讨论】:

  • 你能提供一个如何使用它的例子吗?
  • 和内置progressBar完全一样。
【解决方案3】:

避免闪烁的文本

上面 Barry 提供的solution 非常好,但是存在“闪烁问题”。

一旦 Value 大于零,OnPaint 就会被重复调用,并且文本会闪烁。

有一个解决方案。我们不需要对象的 VisualStyles,因为我们将使用自己的代码来绘制它。

将以下代码添加到 Barry 编写的自定义对象中,您将避免闪烁:

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

这不是我自己写的。它在这里找到它:https://stackoverflow.com/a/299983/1163954

我已经测试过了,它可以工作。

【讨论】:

  • 还可以通过在控件初始化中添加SetStyle( ControlStyles.OptimizedDoubleBuffer, true );来避免闪烁。
  • +1 @KraangPrime - 谢谢!就在那里,没有闪烁。无需依赖其他外部库。
  • @KraangPrime 我尝试添加该行而不是此答案中的导入,并且我的程序在尝试显示时被炸毁。不知道为什么会发生这种情况,但我想我会提到它。
  • 为了澄清@KraangPrime 的评论,将SetStyle( ControlStyles.OptimizedDoubleBuffer, true); 添加到public CustomProgressBar() 构造函数方法中。像魅力一样工作!
【解决方案4】:

我将创建一个名为 InfoProgresBar 的控件,它为这个功能提供一个或两个标签(主作业、当前作业)和 ProgressBar,并使用它来代替那个 ProgressBar。

【讨论】:

  • 自定义 ProgressBar 似乎是个好主意,会尝试一下...谢谢
【解决方案5】:

我已经使用了这个简单的代码,它可以工作!

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

它只有一个简单的问题,就是这样:当进度条开始上升时,百分比会隐藏一段时间,然后再次出现。 我没有自己写。我在这里找到了它: text on progressbar in c#

我使用了这段代码,它确实有效。

【讨论】:

  • 这很糟糕,因为它会泄漏Graphics对象,并且在被遮挡后不会重绘。
  • 从技术上讲,这本身并没有“泄漏”,因为它都是托管的; Graphics 对象的引用计数将在该行之后立即为零,使其被垃圾收集。不过,我确实同意它可以处理得更好。
【解决方案6】:

我尝试在进度条上放置一个具有透明背景的标签,但始终无法正常工作。所以我发现Barry这里的解决方案非常有用,虽然我错过了漂亮的Vista风格进度条。因此,我将 Barry 的解决方案与 http://www.dreamincode.net/forums/topic/243621-percent-into-progress-bar/ 合并,并设法保持原生进度条,同时在其上显示文本百分比或自定义文本。我也没有看到这个解决方案有任何闪烁。很抱歉挖掘了旧线程,但我今天需要这个,所以其他人可能也需要它。

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

    }

}

【讨论】:

  • 我们添加了一个枚举 ProgressBarDisplayText.CurrentValue String.Format("{0} / {1}", Value, MaximumValue)
【解决方案7】:

只想在@codingbadger 回答中指出一些问题。使用“ProgressBarRenderer”时,您应始终在使用该类之前检查“ProgressBarRenderer.IsSupported”。对我来说,这是我无法修复的 Win7 视觉样式错误的噩梦。因此,解决方案的更好方法和解决方法是:

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

请注意,填充将是一个简单的矩形,而不是块。只有在支持 ProgressBarRenderer 时才会使用块

【讨论】:

    【解决方案8】:

    我创建了一个使用透明标签控件的 InfoProgressBar 控件。在使用计时器的表单上进行测试时,如果使用小于 250 毫秒的计时器间隔,我会在每 30-40 次值更改时显示一些小故障(可能是因为更新屏幕所需的时间大于计时器间隔) .

    可以修改 UpdateText 方法以将所有计算值插入到 CustomText 中,但这不是我需要的东西。这将消除对 DisplayType 属性和枚举的需要。

    TransparentLabel 类是通过添加一个新的 UserControl 并将其更改为使用以下实现从 Label 继承来创建的:

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

    我没有对相关的设计器代码进行任何更改,但这里是为了完整性。

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

    然后我创建了另一个新的 UserControl 并将其更改为从 ProgressBar 派生,并具有以下实现:

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

    以及设计器代码:

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

    【讨论】:

      【解决方案9】:

      您可以尝试放置一个 Label 控件并将其放在进度条控件的顶部。然后,您可以将所需的任何文本设置为标签。我自己没有这样做过。如果可行,它应该是比覆盖 onpaint 更简单的解决方案。

      【讨论】:

      • 我发现没有办法让标签背景透明。设置 BackColour = Transparent 让它变成灰色。
      猜你喜欢
      • 2014-11-29
      • 2020-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-18
      • 2015-01-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多