How to use WinForms progress bar?

C#WinformsProgress Bar

C# Problem Overview


I want to show progress of calculations, which are performing in external library.

For example if I have some calculate method, and I want to use it for 100000 values in my Form class I can write:

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();
        }
    }
}

I should perform step after each calculation. But what if I perform all 100000 calculations in external method. When should I "perform step" if I don't want to make this method dependant on progress bar? I can, for example, write

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);
    }
}

but I don't want to do like that.

C# Solutions


Solution 1 - C#

I would suggest you have a look at BackgroundWorker. If you have a loop that large in your WinForm it will block and your app will look like it has hanged.

Look at BackgroundWorker.ReportProgress() to see how to report progress back to the UI thread.

For example:

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.
}

Solution 2 - C#

Since .NET 4.5 you can use combination of async and await with Progress 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
}

Tasks are currently the preferred way to implement what BackgroundWorker does.

> Tasks and Progress are explained in more detail here: > > - Async in 4.5: Enabling Progress and Cancellation in Async APIs > - Reporting Progress from Async Tasks by Stephen Cleary > - https://stackoverflow.com/questions/3513432/task-parallel-library-replacement-for-backgroundworker

Solution 3 - C#

Hey there's a useful tutorial on Dot Net pearls: http://www.dotnetperls.com/progressbar

In agreement with Peter, you need to use some amount of threading or the program will just hang, somewhat defeating the purpose.

Example that uses ProgressBar and 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

Solution 4 - C#

There is Task exists, It is unnesscery using BackgroundWorker, Task is more simple. for example:

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();

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDmytroView Question on Stackoverflow
Solution 1 - C#Peter RitchieView Answer on Stackoverflow
Solution 2 - C#quasoftView Answer on Stackoverflow
Solution 3 - C#Chong ChingView Answer on Stackoverflow
Solution 4 - C#TangMonkView Answer on Stackoverflow