为什么异步ProgressBar上的文本会闪烁?

时间:2016-01-20 20:37:34

标签: c# visual-studio-2012 asynchronous progress-bar

我想在ProgressBar上显示文字,(没有 custom progress bar的所有废话)。这并不难,并且不涉及OnPaint方法 - 如以下代码的button1所示。但是,此方法会阻止UI线程,即evil。 不幸的是,我最好采用异步方法导致文本闪烁,这非常烦人。

有人可以告诉我如何在没有闪烁的情况下异步更新文本吗?

  

(要运行以下代码,只需将其粘贴到新项目中即可   包含带3个按钮和3个ProgressBars的表单)。

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.Threading;

namespace  ProgBarTest //change namespace in Program.cs accordingly
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //This method will block the main UI thread
            // (thus freezing the other buttons)
            StartUpdate(this.progressBar1); 
        }
        private void button2_Click(object sender, EventArgs e)
        {
            //this method (and the next) don't block the UI, and can be used concurrently,
            // but the text flickers
            Task t = new Task(() => StartUpdate(this.progressBar2));
            t.Start();
        }
        private void button3_Click(object sender, EventArgs e)
        {
            Task t = new Task(() => StartUpdate(this.progressBar3));
            t.Start();
        }

        private void StartUpdate( ProgressBar progBar)
        {
            for (int i = 1; i <= 100; i++)
            {
                UpdateProgressBar(i, progBar);
                Thread.Sleep(100);
            }
        }

        private void UpdateProgressBar(int i, ProgressBar progBar)
        {
            if (progBar.InvokeRequired) //for use with background Task
            {
                progBar.Invoke(new Action<int, ProgressBar>(UpdateProgressBar), 
                                  new Object[] { i, progBar });
            }
            else
            {
                //set progress bar:
                progBar.Value = i;
                progBar.Refresh();

                //set status label:
                string percentStr = i.ToString() + "%";
                PointF location = new PointF(progBar.Width / 2 - 10, progBar.Height / 2 - 7);
                using (Graphics gr = progBar.CreateGraphics())
                {
                    gr.DrawString(percentStr, new Font("Arial",10), Brushes.Red, location );
                }
            }
        }

    }
}

2 个答案:

答案 0 :(得分:1)

老实说,自定义ProgressBar是您最好的选择..如果您正确设置它。我知道我没有回答你的问题所以请不要投票,只提供不同的解决方案。我还没有对此进行测试,但理论上每次进度条必须重新绘制时都会绘制百分比,这会在每次更改值时发生。

您当前的问题是每次更改值时条形图都会重新绘制,因此文本会闪烁。如果它没有重新绘制,你会看到你绘制的百分比开始叠加在彼此之上,这也是不好的。

从设计的角度来看,最好将所有内容封装在一个控件中。

class CustomProgressBar : ProgressBar {
    public CustomProgressBar() : base() {}

    protected override void OnPaint(PaintEventArgs e) {
        // Call the OnPaint method of the base class.
        base.OnPaint(e);
        string percentStr = this.Value.ToString() + "%";
        PointF location = new PointF(this.Width / 2 - 10, this.Height / 2 - 7);
        // Call methods of the System.Drawing.Graphics object.
        e.Graphics.DrawString(percentStr, new Font("Arial",10), Brushes.Red, location );
    } 
}

答案 1 :(得分:0)

这可能是因为您正在从另一个线程中绘制字符串。如果您可以使用基于Control的文本元素,则可以使用与ProgressBar相同的方式使用BeginInvoke:

// Label progressLbl

private void UpdateProgressFromAnotherThread(int completed, int total, string progressText)
{
    this.progBar.BeginInvoke(new Action(() =>
    {
        this.progBar.Maximum = total;
        this.progBar.Value = completed;
    }));

    this.progressLbl.BeginInvoke(new Action(() =>
    {
        this.progressLbl.Text = progressText;
    }));
}