Как использовать индикатор выполнения WinForms?
Я хочу показать ход вычислений, которые выполняются во внешней библиотеке.
Например, если у меня есть метод вычисления, и я хочу использовать его для 100000 значений в моем классе Form, я могу написать:
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);
}
}
но я не хочу этого делать.
Ответы
Ответ 1
Я бы посоветовал вам взглянуть на 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.
}
Ответ 2
Начиная с .NET 4.5 вы можете использовать комбинацию async и ждать с помощью Прогресс для отправки обновлений в поток пользовательского интерфейса:
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
более подробно описаны здесь:
Ответ 3
Привет, полезный учебник по жемчугу Dot Net: http://www.dotnetperls.com/progressbar
В согласии с Питером вам нужно использовать некоторое количество потоков или программа будет просто зависать, что несколько наносит ущерб цели.
Пример, который использует ProgressBar и BackgroundWorker: С#
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