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

我想在ProgressBar上显示文本,( 没有 自定义进度条的所有废话)。 这并不难,并且不涉及OnPaint方法 – 如下面代码的button1所示。 但是,此方法会阻止UI线程,这是邪恶的 。 不幸的是,我最好采用异步方法导致文本闪烁,这非常烦人。

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

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

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

老实说,自定义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 ); } } 

这可能是因为你正在从另一个线程中绘制字符串。 如果您可以使用基于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; })); }