MickJuice
MickJuice

Reputation: 539

Update UI Progress bar from external method

So this is my first foray into using delegates, events, Backgroundworkers, WPF...pretty much everything is new. I have an external class that runs a long running method that I would like to report progress on:

public class ShortFileCreator
{
    public void CreateShortUrlFile(string outputfilepath)
    {         
        foreach(string line in lines)
        {
                //work work work processing file
                if (ReportProgress != null)
                {
                //report progress that a file has been processed
                    ReportProgress(this, new ProgressArgs {TotalProcessed = numberofurlsprocessed
                                                         , TotalRecords = _bitlyFile.NumberOfRecords});
                }
        }
    }

    public delegate void ReportProgressEventHandler (object sender, ProgressArgs args);

    public event ReportProgressEventHandler ReportProgress;

    public class ProgressArgs : EventArgs
    {
        public int TotalProcessed { get; set; }
        public int TotalRecords { get; set; }
    }
}

In my WPF Form, I want to kickoff the CreateShortUrlFile method and update the form's progress bar.

private void btnRun_Click(object sender, RoutedEventArgs e)
    {
       var shortFileCreator = new ShortFileCreator();           

        _worker = new BackgroundWorker
        {
            WorkerReportsProgress = true,
            WorkerSupportsCancellation = true
        };

        shortFileCreator.ReportProgress += ShortFileCreator_ReportProgress;

        _worker.DoWork += delegate(object s, DoWorkEventArgs args)
        {
            _bitlyFileWorker.CreateShortUrlFile(saveFileDialog.FileName);
        };

        _worker.RunWorkerAsync();
    }

    protected void ShortFileCreator_ReportProgress(object sender, ShortFileCreator.ProgressArgs e)
    {
        //update progress bar label
        txtProgress.Content = String.Format("{0} of {1} Records Processed", e.TotalProcessed, e.TotalRecords);
        //update progress bar value
        progress.Value = (double) e.TotalProcessed/e.TotalRecords;
    }

However when I run this, it processes one line and then I get the exception: The calling thread cannot access this object because another thread owns it. What other thread owns this? Shouldn't the ReportProgress event return ProgressArgs to any subscribers?

Upvotes: 0

Views: 1307

Answers (1)

sa_ddam213
sa_ddam213

Reputation: 43596

This is because UI controls like the ProgressBar and TextBox can not be touched by another thread, in this case you are trying to update them from the BackgroundWorker thread.

A way around this is to Invoke the call back to the UI thread, you can do this using the Dispatcher

protected void ShortFileCreator_ReportProgress(object sender, ShortFileCreator.ProgressArgs e)
{
    Dispatcher.Invoke((Action)delegate
    {
       //update progress bar label
       txtProgress.Content = String.Format("{0} of {1} Records Processed", e.TotalProcessed, e.TotalRecords);
       //update progress bar value
       progress.Value = (double) e.TotalProcessed/e.TotalRecords;
    });
}

Upvotes: 1

Related Questions