Как использовать индикатор выполнения 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