There are a couple of approaches.
The first is to split up the synchronous method into different parts. This is best if your synchronous method calculates different types of things that go into different parts of the UI:
async Task DoSyncAsync()
{
myDataBoundUIProperty1 = await Task.Run(() => DoSync1());
myDataBoundUIProperty2 = await Task.Run(() => DoSync2());
}
The second is to use progress reporting. This is best if your UI updates are all of the same type:
Task DoSyncAsync()
{
Progress<MyProgressType> progress = new Progress<MyProgressType>(progressUpdate =>
{
myDataBoundUIProperty = progressUpdate;
});
return Task.Run(() => DoSync(progress));
}
void DoSync(IProgress<MyProgressType> progress)
{
...
if (progress != null)
progress.Report(new MyProgressType(...));
...
}
There is a final alternative, but I strongly recommend one of the two above. The two solutions above will result in a better code design (separation of concerns). The third alternative is to pass in a TaskFactory
that can be used to run arbitrary code on the UI context:
Task DoSyncAsync()
{
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
var factory = new TaskFactory(scheduler);
return Task.Run(() => DoSync(factory));
}
void DoSync(TaskFactory factory)
{
...
scheduler.StartNew(() => { ... });
...
}
Again, I don't advise this last solution since it conflates your UI update logic with your background task logic. But it's better than using Dispatcher
or Control
directly.