Как использовать индикатор выполнения WinForms?
Я хочу показать ход вычислений, которые выполняются во внешней библиотеке.
например, если у меня есть какой-то метод вычисления, и я хочу использовать его для 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);
}
}
но я не хочу этого делать.
3 ответа:
Я бы предложил вам взглянуть на BackgroundWorker. Если у вас есть петля, что большой в вашей WinForm он будет блокировать и ваше приложение будет выглядеть, как он повесил.
посмотреть
BackgroundWorker.ReportProgress()
чтобы увидеть, как доклад обратно в поток пользовательского интерфейса.например:
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 вы можете использовать комбинацию асинхронные и ждут С прогресса для отправки обновлений в поток пользовательского интерфейса:
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
объясняются более подробно здесь:
Эй, есть полезный учебник по Dot Net pearls: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