2016-01-20 28 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