2016-01-20 83 views
1

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

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

(要运行下面的代码,只需将其粘贴到包含的三个键和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); 
       } 
      } 
     } 

    } 
} 

回答

0

这可能是因为你正在绘制从另一个线程的字符串。如果你可以使用一个Control基于文本的元素,而不是你可以在相同的方式使用进度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; 
    })); 
} 
+0

谢谢。这是我的第一个策略,但我希望文本放置在ProgressBar的顶部,并且我无法找到将progresLbl设置为透明背景。 – kmote

+0

好吧,那么也许你可以尝试一些基于这个问题的答案(如果你还不想创建自定义ProgressBar):http://stackoverflow.com/q/10714358/2931053 – Pricey

1

老实说,一个自定义进度将是你最好的选择。如果您正确设置它。我知道我没有回答你的问题,所以请不要低估,只是提供不同的解决方案。我还没有测试过,但理论上每次进度条必须重新绘制时,都会绘制百分比,每次值改变时都会发生这种情况。

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

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

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

谢谢,你可能会对。但对我来说,我的方法很奇怪,我的方法从UI线程正常工作,只在后台线程上闪烁。但我喜欢你自定义进度条的简化版本(比上面引用的接受答案中的版本简单得多)。我必须试一试 – kmote