Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update UI Label from Task.Continuewith

Tags:

c#

.net

I am working on a Winform Application. The Method is started by a BackgroundWorker Thread. I am sorry. I did not mention this earlier.

private void Method()
{
 tasks[i] = Task.Factory
           .StartNew(() => fileProcessor.ProcessEachMachine(mdetail))
           .ContinueWith(UpdateLabel, TaskContinuationOptions.OnlyOnRanToCompletion);
}

I have a long running function ProcessEachMachine. In the continuation function UpdateLabel I want to access UIlabel and update the status.

private void UpdateLabel()
{
   progressLbl.Text = "updated";
}

But the label is not getting updated. How to access UILabel and update the text of it.?

like image 962
Sandeep Avatar asked Jan 16 '23 21:01

Sandeep


1 Answers

You have to set the TaskScheduler.FromCurrentSynchronizationContext on ContinueWith or else it will not be run in the UI context. Here is the MSDN on the override that you must use for this call to ContinueWith.

It should end up looking like this:

.ContinueWith(UpdateLabel, null, 
    TaskContinuationOptions.OnlyOnRanToCompletion,
    TaskScheduler.FromCurrentSynchronizationContext());

It may seem like nothing is happening, but the TPL is currently swallowing your cross thread exception. You should probably use the UnobservedTaskException if you are not going to inspect each result or check for its exception. Otherwise, when garbage collection occurs, the exception will happen then...which could create hard to debug errors.

UPDATE

Based on your update about the main Task being setup and started by a Backgroundworker, my main question is why this could not use a Task to start? In fact, if there is not more in the Method, then this is really just double work and might confuse other developers. You are already started asynchronously, so why not just do your work within the backgroundworker and use an OnComplete method that will UpdateLabel (as background workers are already context aware).

The main problem is still the same though, so here are some other solutions if you feel you must use the TPL:

  1. You can Invoke back onto the main UI thread within the UpdateLabel method
  2. You can pass the current context into the backgroundworker and use that instead
  3. You can Wait for your original Task to return and then use the worker's oncomplete event to update the label.

Here is how I would do this (all pseudo code)

Background Worker Method:

Method() called because of Background worker

private void Method()
{
    fileProcessor.ProcessEachMachine(mdetail);
}

Wire up background worker's OnRunWorkerCompleted:

if(!e.Cancelled && !e.Error)
    UpdateLabel();

Task only method

Call Method() from the main thread and just let the TPL do its work :)

Task.Factory.StartNew(() => fileProcessor.ProcessEachMachine(mdetail))
       .ContinueWith((precedingTask)=>{if(!precedingTask.Error)UpdateLabel;}, 
           null, TaskContinuationOptions.OnlyOnRanToCompletion,
           TaskScheduler.FromCurrentSynchronizationContext());
like image 98
Justin Pihony Avatar answered Jan 26 '23 05:01

Justin Pihony