如何使用WinForms进度条?
我想显示在外部库中执行的计算的进度。
例如,如果我有一些计算方法,我想在我的Form类中使用它100000的值,我可以写:
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Caluculate(int i) { double pow = Math.Pow(i, i); } private void button1_Click(object sender, EventArgs e) { progressBar1.Maximum = 100000; progressBar1.Step = 1; for(int j = 0; j < 100000; j++) { Caluculate(j); progressBar1.PerformStep(); } } }
每次计算后我应该执行一步。 但是如果我用外部方法进行所有100000次计算呢? 我应该什么时候“执行步骤”,如果我不想让这个方法依赖于进度条? 我可以,例如,写
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void CaluculateAll(System.Windows.Forms.ProgressBar progressBar) { progressBar.Maximum = 100000; progressBar.Step = 1; for(int j = 0; j < 100000; j++) { double pow = Math.Pow(j, j); //Calculation progressBar.PerformStep(); } } private void button1_Click(object sender, EventArgs e) { CaluculateAll(progressBar1); } }
但我不想这样做。
我build议你看看BackgroundWorker 。 如果你的WinForm中有一个很大的循环,它将被阻塞,你的应用程序看起来就像被挂起了一样。
查看BackgroundWorker.ReportProgress()
来查看如何将进度报告回UI线程。
例如:
private void Calculate(int i) { double pow = Math.Pow(i, i); } private void button1_Click(object sender, EventArgs e) { progressBar1.Maximum = 100; progressBar1.Step = 1; progressBar1.Value = 0; backgroundWorker.RunWorkerAsync(); } private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { var backgroundWorker = sender as BackgroundWorker; for (int j = 0; j < 100000; j++) { Calculate(j); backgroundWorker.ReportProgress((j * 100) / 100000); } } private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { progressBar1.Value = e.ProgressPercentage; } private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // TODO: do something with final calculation. }
从.NET 4.5开始,您可以使用asynchronous组合并等待 进度 ,将更新发送到UI线程:
private void Caluculate(int i) { double pow = Math.Pow(i, i); } public void DoWork(IProgress<int> progress) { // This method is executed in the context of // another thread (different than the main UI thread), // so use only thread-safe code for (int j = 0; j < 100000; j++) { Caluculate(j); // Use progress to notify UI thread that progress has // changed if (progress != null) progress.Report((j + 1) * 100 / 100000); } } private async void button1_Click(object sender, EventArgs e) { progressBar1.Maximum = 100; progressBar1.Step = 1; var progress = new Progress<int>(v => { // This lambda is executed in context of UI thread, // so it can safely update form controls progressBar1.Value = v; }); // Run operation in another thread await Task.Run(() => DoWork(progress)); // TODO: Do something after all calculations }
目前,任务是实现BackgroundWorker
的首选方式。
任务和
Progress
在这里更详细地解释:
- 4.5中的asynchronous:在asynchronousAPI中启用进度和取消
- 从 Stephen Cleary 报告asynchronous任务的进度
- 任务并行库replace为BackgroundWorker?
嘿,有一个有用的教程DotNet珍珠: http : //www.dotnetperls.com/progressbar
与彼得一致,你需要使用一定数量的线程,否则程序就会挂起,有些打败目的。
使用ProgressBar和BackgroundWorker的示例:C#
using System.ComponentModel; using System.Threading; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, System.EventArgs e) { // Start the BackgroundWorker. backgroundWorker1.RunWorkerAsync(); } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { for (int i = 1; i <= 100; i++) { // Wait 100 milliseconds. Thread.Sleep(100); // Report progress. backgroundWorker1.ReportProgress(i); } } private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { // Change the value of the ProgressBar to the BackgroundWorker progress. progressBar1.Value = e.ProgressPercentage; // Set the text. this.Text = e.ProgressPercentage.ToString(); } } }//closing here