Mohtaa
Mohtaa

Reputation: 129

Run Tasks on a separate thread with return value

I'm developping an application that should run a new task , makes some stuff and return a list of objects...

when spawning the new task , i need to simulate in my UI thread a progressbar changing in a for loop... and after the task completion , i have to update my view ( by updating the values of my ViewModel properties as i'm using MVVM pattern).

the problem is that when the task is launched, the GUI freezes and the progress bar is not updated! i thought that tasks are run in background on a sperate thread ( as they are pooled). so why my GUI is blocked??

here is a snippet of my code :

Task<List<Items>> myTask = Task.Factory.StartNew(() => queryHandler.GetItemssStatistics(items, sec, buyer, seller, From, To));

//Here is the simulation of ProgressBar changing that i need to be done when task is started
                    for (int i = 0; i < 100; i++)
                    {
Thread.Sleep(100);
                        StatusValue = i+1;
                    }
//Here is the section i need to do once the task finishes its execution to update my VM
//Wait for the task to finish its execution and retreive results:
                    List<Items> _tempResults = myTask.Result;
                    foreach (Items d in _tempResults)
                    {
                        ResultsList.Add(d);
                    }

                    if (ResultsList.Count == 0)
                    {
                        MessageBox.Show("Sorry, No items matched the defined research criteria!", "Warning",
                                        MessageBoxButton.OK,
                                        MessageBoxImage.Warning, MessageBoxResult.OK, MessageBoxOptions.ServiceNotification);
                    }
                    else
                    {
                        //Show items:
                        ResultsAvailable = Visibility.Visible;

                    }

Upvotes: 0

Views: 3319

Answers (2)

Eli Arbel
Eli Arbel

Reputation: 22739

There are a few problems with your code:

  • You're using Thread.Sleep on the UI thread
  • You're calling Task<T>.Result again on the UI thread - which blocks until the task has been completed.

You can do a few things:

  • Use ProgressBar.IsIndeterminate = true to show a marquee-style progress bar instead of faking the progress update or use a DispatcherTimer to update the progress bar.

  • Use async/await:

     async Task GetItems()
     {
         List<Items> tempResults = await Task.Run(() => queryHandler.GetItemssStatistics(...));
         // the following will be executed on the UI thread automatically
         // after the task is done, keeping the UI responsive
         foreach (Items d in _tempResults)
         {
             ResultsList.Add(d);
         }
     }
    

Upvotes: 2

bash.d
bash.d

Reputation: 13207

You are still stopping the UI from working, because you are calling Thread.Sleep on it. If you want your progress-bar to update, run another thread, or try to integrate the increasing of the progress-bar in your task.
Looking at your code, it would be better to do the update of the progress-bar in your task!
You are making the user wait for 10000 ms, if your task finishes before, the user has to wait...
Access the dispatcher of the progress-bar in your task, when one items has been progressed and do like

//process item
if(progressBar.Dispatcher.CheckAccess()){
progressBar.Value += 1; //maybe calculate the number of items you are processing
}
else {
//send a delegate to the dispatcher
MyDelegate myDel = new MyDelegate(delegate() {
   progressBar.Value += 1;
});    
progressBar.Dispatcher.Invoke(myDel);
}

The delegate's signature could be something like:

public void MyDelegate();

Upvotes: 0

Related Questions