c# progressbar not updating

CalumMcCall picture CalumMcCall · Aug 2, 2010 · Viewed 11k times · Source

I have a ProgressBarWindow which has a progressbar and a cancel button on it which I use to report progress on file I/O. However, the UI Thread of the ProgressBarWindow and my main window both hang despite all the work being done in a backgroundworker. The progressbar is rendered, as is my main window, but does not update whilst the backgroundworker does its thing. The following code is called at the very end of the constructor of the main window:

iCountLogLinesProgressBar = new ProgressBarWindow();
iCountLogLinesProgressBar.cancelButton.Click += EventCountLogLinesProgressBarCancelButtonClicked;
iCountLogLinesProgressBar.Show();

iCountLogRecords = new BackgroundWorker();
iCountLogRecords.DoWork += EventCountLogLinesDoWork;
iCountLogRecords.ProgressChanged += EventCountLogLinesProgressChanged;
iCountLogRecords.RunWorkerCompleted += EventCountLogLinesRunWorkerCompleted;
iCountLogRecords.WorkerReportsProgress = true;
iCountLogRecords.WorkerSupportsCancellation = true;
iCountLogRecords.RunWorkerAsync(new BinaryReader(File.Open(iMainLogFilename, FileMode.Open, FileAccess.Read)));

EventCountLogLinesProgressChanged() looks like this:

private void EventCountLogLinesProgressChanged(object sender, ProgressChangedEventArgs e)
{
    iCountLogLinesProgressBar.Value = e.ProgressPercentage;
}

Here is a shortened version of ProgressBarWindow(the rest is just a couple of setters):

public partial class ProgressBarWindow : Window
{
    public ProgressBarWindow()
    {
        InitializeComponent();
        this.progressBar.Value = this.progressBar.Minimum = 0;
        this.progressBar.Maximum = 100;
    }

    public double Value
    {
        get
        {
            return progressBar.Value;
        }
        set
        {
            this.progressBar.Value = value;
        }
    }
} 

I've tried wrapping the value setter line in a dispatcher.invoke delegate but that gives me a stack overflow(I shouldn't have to have a dispatcher.invoke line anyway as backgroundworker calls ProgressChanged in the UI Thread, right?). I have checked msdn and googled but I can't seem to find anyone else with this problem.

EDIT Apologies, I did not realise my simplified code blocked the UI Thread, I get exactly the same behaviour despite using a backgroundworker so I erroneously assumed they were equivalent. I should have mentioned I was using a backgroundworker :P

Answer

Jon Skeet picture Jon Skeet · Aug 2, 2010

You're blocking the UI thread, so it won't re-render the UI until your loop has finished.

Move the background processing into a separate thread, and use appropriate Dispatcher calls (or BackgroundWorker) to marshal UI update calls back to the UI thread.

If your progress bar is really just meant to be a timer, you could use one of the Timer classes to update it.

EDIT: Okay, now you've changed the code, it looks okay to me. It should be thread-safe as you're only changing the UI on the UI thread. Is your background worker definitely reporting progress periodically?