Home > C#, WinForms > WinForms: Show progress on long-running operations

WinForms: Show progress on long-running operations

January 30th, 2012 Admin Leave a comment Go to comments

    When I have a long-running operation to execute I always use the best practice demonstrated here. Keeping the UI responsive to user interaction, I start a lengthy task in a separate thread and then update a ProgressBar control step by step from the executing operation. The progress data is passed to UI thread through the BeginInvoke or Invoke methods of the ProgressBar control or any other UI control, since all UI controls are created in UI thread and must be updated from it. Much time has passed since the time when the article was written. So now we have Anonymous Methods, Actions, Funcs, the upgraded ThreadPool class and so on. Using these innovations I’ve rewritten the approach described by Chris Sells. Look at the code sample below. This code sample simulates a treatment of group of files:

private void startBtn_Click(object sender, EventArgs e)
{
    // the array of file paths
    string[] filePaths = ... 

    UpdateProgressBar(0);            

    // start lengthy task in separate thread
    ThreadPool.QueueUserWorkItem(new WaitCallback(new Action<object>(delegate(object state)
    {
        DoSomethingWithFiles(filePaths);
    })));
}

private void DoSomethingWithFiles(string[] filePaths)
{
    for (int i = 0; i < filePaths.Length; i++)
    {
        string fileText = File.ReadAllText(filePaths[i]);

        // do something with the read file content
        ...

        // set refreshed value to ProgressBar
        UpdateProgressBar((i + 1) * 100 / filePaths.Length);
    }

    // set refreshed value to ProgressBar
    UpdateProgressBar(100);
}

private void UpdateProgressBar(int currentValue)
{
    // if it's UI thread, simply update ProgressBar as usual.
    // Otherwise, make the current method be called in UI thread.
    if (progressBar1.InvokeRequired)
        progressBar1.BeginInvoke(new Action(delegate() { UpdateProgressBar(currentValue); }));
    else
        progressBar1.Value = currentValue;
}

It’s assumed that startBtn and progressBar1 are added to the form. When startBtn is clicked, the startBtn_Click method is called and starts the DoSomethingWithFiles long-running operation in separate thread. The DoSomethingWithFiles calls the UpdateProgressBar from time to time to show the current progress. The UpdateProgressBar checks whether it’s called from UI thread. If so, it just updates the ProgressBar, otherwise, initiates its own invoking from the UI thread.

As you can see, the obtained code does the same but is more compact.

Related posts:
 
Categories: C#, WinForms Tags: ,
  1. Dietrich
    February 7th, 2012 at 03:56 | #1

    Hi,

    good code, but do you have an idea to do this for a ToolstripProgressbar?

    regards-

  2. Administrator
    February 7th, 2012 at 09:04 | #2

    Hello!
    I don’t see any difference. This code will work with ToolstripPregressbar in the same way. Just replace progressBar1 with the name of your ToolstripPregressbar. Let me know if I’m wrong.

  3. Dietrich
    February 7th, 2012 at 13:56 | #3

    Hi,

    when I try this (I use Vb.net) I get the message:
    InvokeRequired is not a member of ToolstripProgressbar.

    Regards-
    Dietrich

    The VB-code:
    Private Sub UpdateProgressBar(currentValue As Integer)
    ‘ if it’s UI thread, simply update ProgressBar as usual.
    ‘ Otherwise, make the current method be called in UI thread.
    If progressBar1.InvokeRequired Then
    progressBar1.BeginInvoke(New Action(Function() Do
    UpdateProgressBar(currentValue)
    End Function))
    Else
    progressBar1.Value = currentValue
    End If
    End Sub

  4. Administrator
    February 7th, 2012 at 14:16 | #4

    Hi!
    Ok, I understand now. ToolstripPregressbar seems not to exist without toolstrip.
    You can use InvokeRequired and BeginInvoke of any visual control on form. It can be toolstrip or form itself.

  1. No trackbacks yet.