Kikootwo
Kikootwo

Reputation: 380

Specific Use Case For Background Updating Progress Bar

I have been searching for over two days for a solution to this issue, and have finally decided to ask this question. I have found MANY relevant topics, but none of them seem to solve my problem. Most recently, I tried all of the solutions listed here.

Background Info: I have a class that handles traversing a massive amount of data. The class is called Traverse. There is a class method called DoFullTraverse (Traverse.DoFullTraverse), that runs a complete traverse than can (depending on user input) take up to 30 seconds. I am working in WPF, MVVM pattern. I would like to update a status bar on the gui for the progress of the DoFullTraverse. I calculate at the beginning of the function the exact number of loops required for calculation, and then increment a loop counter. Each time it reaches another 1/100, I increment the progress bar by 1. My progress bar (in xaml) has its value bound to a property in my VM called PBarV.

Most Recent Attempt: I have tried 100 different solutions but my most recent attempt looks like this:

private void runTraverseAndUpdateBar()
{
    var worker = new BackgroundWorker();
    worker.DoWork += new DoWorkEventHandler(worker_DoWork);
    worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_Complete);
    worker.RunWorkerAsync();
    while (!ThreadCheck)
    {
        Thread.Sleep(500);
    }
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
    var worker = sender as BackgroundWorker;
    for (int i = 0; i < 36; i++)
    {
        Thread.Sleep(500);
        PBarV += 3;
    }
    e.Result = true;
}
void worker_Complete(object sender, RunWorkerCompletedEventArgs e)
{
    ThreadCheck = true;
}

I believe that I am fundamentally misunderstanding how the background worker does work.

The Main Problem: I can get this method to work just fine, if I throw the function into the background worker and continue as usual. The problem is, I need the data from that function before my program to continue. Therefore, I need it to execute linearly but still update the status bar properly.

If anyone can shed some light on what I am missing or even nudge me in the right direction, I would appreciate it greatly.

Edit: This is not duplicate. The post you provided does not cover the issue of linear executing and waiting for the background worker to complete before continuing.

Edit 2: (As Per @Clemens Request)

I need the background worker to complete work before the main program continues. I am running the computationally heavy process in the background worker specifically so that the progress bar can be updated. But, BEFORE the main program can continue, I need the information from Traverse.DoFullTraverse();

To be VERY specific. The main program should halt all execution (other than updating status bar) until the background worker has completed Traverse.DoFullTraverse();

Upvotes: 2

Views: 199

Answers (1)

user1228
user1228

Reputation:

Here's a trivial example you can play around with and apply to your view model. It's important to use prototypes to create code and learn how it works in order to apply it to a larger and more complex application.

Please note that the example doesn't include trivial stuff like how to implement INotifyPropertyChanged and ICommand--those are easy to do.

Also, note the comments within TraverseYo. Specifically, the ones that tell you what thread you're currently on. Understanding the flow of execution across threads is important to get this working correctly. If you don't know what thread you're on, simply get the ApartmentState of the current thread. If it's STA, you're most likely on the UI thread.

public class LongLastingWorkViewModel : INotifyPropertyChanged
{
    public bool Busy 
    {
        // INotifyPropertyChanged property implementation omitted
    }
    public double PercentComplete 
    {
        // INotifyPropertyChanged property implementation omitted
    }

    public ICommand PerformWork { get; set; }

    public LongLastingWorkViewModel()
    {
        // delegated ICommand implementation omitted--there's TONS of it out there
        PerformWork = new DelegatedCommand(TraverseYo);
    }

    private void TraverseYo()
    {
        // we are on the UI thread here
        Busy = true;
        PercentComplete = 0;
        Task.Run(() => {
            // we are on a background thread here
            // this is an example of long lasting work 
            for(int i = 0; i < 10; i++)
            {
                Thread.Sleep(10 * 1000); // each step takes 10 seconds

                // even though we are on a background thread, bindings
                // automatically marshal property updates to the UI thread
                // this is NOT TRUE for INotifyCollectionChanged updates!
                PercentDone += .1; 
            }
            Busy = false;            
        });
}

You can bind Busy to an overlay that blocks all UI while execution runs, bind PercentComplete to a progress bar, and PerformWork to a button.

Upvotes: 1

Related Questions