Istnieje kilka podejść.
Pierwsza polega na podzieleniu metody synchronicznej na różne części. Jest to najlepsze, jeśli twoja metoda synchroniczna oblicza różne typy rzeczy, które trafiają do różnych części interfejsu użytkownika:
async Task DoSyncAsync()
{
myDataBoundUIProperty1 = await Task.Run(() => DoSync1());
myDataBoundUIProperty2 = await Task.Run(() => DoSync2());
}
Drugi to używanie raportów z postępów. Jest to najlepsze, jeśli aktualizacje interfejsu użytkownika są tego samego typu:
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(...));
...
}
Istnieje ostateczna alternatywa, ale zdecydowanie polecam jedną z powyższych dwóch. Dwa powyższe rozwiązania spowodują lepszą konstrukcję kodu (separacja problemów). Trzecią alternatywą jest przekazać w TaskFactory
, które mogą być wykorzystane do uruchomienia dowolnego kodu w kontekście Ui:
Task DoSyncAsync()
{
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
var factory = new TaskFactory(scheduler);
return Task.Run(() => DoSync(factory));
}
void DoSync(TaskFactory factory)
{
...
scheduler.StartNew(() => { ... });
...
}
Ponownie, nie radzę to ostatnie rozwiązanie, gdyż zrównują swoją logikę aktualizacji UI z zadania w tle logika. Ale lepiej jest, niż bezpośrednio używać Dispatcher
lub.
Sprawdź "Control.Invoke (()" lub ten wątek: http://stackoverflow.com/questions/4331262/task-continuation-on-ui-thread –