如何使用 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);
}
}

但我不想这么做。

404615 次浏览

我建议你看看 背景工作者。如果你的 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.
}

Since .NET 4.5 you can use combination of async and await with 进步 for sending updates to UI thread:

private void Calculate(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++)
{
Calculate(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在这里有更详细的解释:

嘿,有一个关于点网珍珠有用的教程: http://www.dotnetperls.com/progressbar

与 Peter 的观点一致,您需要使用一定量的线程,否则程序就会挂起,从而在某种程度上违背了设计的初衷。

使用 ProgressBar 和 BackoundWorker 的示例: 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

Task存在,使用 BackgroundWorker是不必要的,Task比较简单。例如:

Progressdialog.cs :

   public partial class ProgressDialog : Form
{
public System.Windows.Forms.ProgressBar Progressbar { get { return this.progressBar1; } }


public ProgressDialog()
{
InitializeComponent();
}


public void RunAsync(Action action)
{
Task.Run(action);
}
}

Done! Then you can reuse ProgressDialog anywhere:

var progressDialog = new ProgressDialog();
progressDialog.Progressbar.Value = 0;
progressDialog.Progressbar.Maximum = 100;


progressDialog.RunAsync(() =>
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000)
this.progressDialog.Progressbar.BeginInvoke((MethodInvoker)(() => {
this.progressDialog.Progressbar.Value += 1;
}));
}
});


progressDialog.ShowDialog();