В моем приложении MVVM моя модель просмотра называет 3 разных метода обслуживания, преобразует данные из каждого в общий формат и затем обновляет пользовательский интерфейс с использованием уведомлений о свойствах/наблюдаемых коллекций и т. Д.Как продолжить выполнение нескольких задач без блокировки потока пользовательского интерфейса?
Каждый метод уровня обслуживания запускает новый Task
и возвращает Task
к модели представления. Вот пример одного из моих методов обслуживания.
public class ResourceService
{
internal static Task LoadResources(Action<IEnumerable<Resource>> completedCallback, Action<Exception> errorCallback)
{
var t = Task.Factory.StartNew(() =>
{
//... get resources from somewhere
return resources;
});
t.ContinueWith(task =>
{
if (task.IsFaulted)
{
errorCallback(task.Exception);
return;
}
completedCallback(task.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
return t;
}
}
Вот код вызова и другие соответствующие части модели представления ...
private ObservableCollection<DataItem> Data = new ObservableCollection<DataItem>();
public ICollectionView DataView
{
get { return _dataView; }
set
{
if (_dataView != value)
{
_dataView = value;
RaisePropertyChange(() => DataView);
}
}
}
private void LoadData()
{
SetBusy("Loading...");
Data.Clear();
Task[] tasks = new Task[3]
{
LoadTools(),
LoadResources(),
LoadPersonel()
};
Task.WaitAll(tasks);
DataView = CollectionViewSource.GetDefaultView(Data);
DataView.Filter = FilterTimelineData;
IsBusy = false;
}
private Task LoadResources()
{
return ResourceService.LoadResources(resources =>
{
foreach(var r in resources)
{
var d = convertResource(r);
Data.Add(d);
}
},
error =>
{
// do some error handling
});
}
Это почти работает, но есть несколько небольших вопросов.
Номер 1: При вызове SetBusy
в самом начале, перед началом каких-либо задач, и до того, как я позвоню WaitAll
, я установил для свойства IsBusy
значение true. Это должно обновить пользовательский интерфейс и отобразить элемент управления BusyIndicator, но он не работает. Я также попытался добавить простые свойства строки и привязать их, и они также не обновляются. Функциональность IsBusy является частью базового класса и работает в других моделях представления, где у меня не более одной задачи, поэтому я не думаю, что есть проблема с уведомлением об имуществе или привязкой данных в XAML.
Все привязки данных, по-видимому, обновляются после завершения всего метода. Я не вижу никаких «исключений в первый раз» или ошибок привязки в окне вывода, что заставляет меня думать, что поток пользовательского интерфейса каким-то образом блокируется перед вызовом WaitAll.
Номер 2: Кажется, я возвращаю неправильные задачи из методов обслуживания. Я хочу, чтобы все после WaitAll
запускалось после того, как модель представления преобразует все результаты всех методов обслуживания в обратные вызовы. Однако, если я возвращаю задачу продолжения из метода службы, продолжение никогда не будет вызвано, а WaitAll
ждет навсегда. Странная вещь: контроль пользовательского интерфейса, связанный с ICollectionView, фактически отображает все правильно, я предположил, что это потому, что Data является наблюдаемой коллекцией, а CollectionViewSource знает об изменениях, связанных с коллекцией.
Я откладываю использование async/await, потому что я не уверен, что он «просто работает» с командами WPF и Prism DelegateCommand. Task.ContinueWhenAll, похоже, не существует для меня, мне нужно ссылаться на некоторую библиотеку расширений TPL? – BenCr
@BenCr Извините, это TaskFactory.ContinueWhenAll. В общем, ожидание/асинхронный материал работает * лучше * чем продолжение задач с WPF. Главное отличие состоит в том, что вам не нужно прыгать через сумасшедшие обручи, чтобы справляться с исключениями. –
Спасибо, Рид, похоже, он работает намного лучше. Надеюсь, кто-то может предоставить объяснение блокировки до того, как WaitAll был вызван, но это, безусловно, устраняет проблемы 1 и 2. Я дам асинхронный/ожидающий материал перейти на следующую итерацию и посмотреть, как я нахожусь. – BenCr