Мне кажется, что интерфейс BackgroundWorker неудобен для этой цели, поэтому я предпочитаю напрямую использовать ThreadPool.
Вот основная идея:
public class MyViewModel
{
public SomeCollectionType<Widget> Widgets
{
get
{
if(!WidgetFillQueued)
{
WidgetFillQueued = true;
ThreadPool.QueueUserWorkItem(_ =>
{
WidgetLoadStatus = 0;
int totalCount = FetchWidgetCount();
while(_internalWidgetCollection.Count < totalCount && !AbortFill)
{
_internalWidgetCollection.AddRange(FetchMoreWidgets());
WidgetLoadStatus = (double)_internalWidgetCollection.Count / totalCount;
}
WidgetLoadStatus = null; // To indicate complete
});
}
return _internalWidgetCollection;
}
}
}
Если предположить, что WidgetLoadStatus является объектом DependencyProperty, связанным с пользовательским интерфейсом, система привязки WPF позаботится о переходах потоков, необходимых для обновления отображения строки состояния.
Предполагая, что _internalWidgetCollection разрешает многопоточный доступ и правильно реализует INotifyPropertyChanged, все обновления коллекции также приводят к обновлениям пользовательского интерфейса в потоке пользовательского интерфейса.
В моих реальных моделях представления есть много коллекций, поэтому вместо использования отдельных свойств, таких как WidgetFillQueued и WidgetLoadStatus, я использую структуру данных, которая отслеживает все выполняемые в данный момент операции и вычисляет объединенное значение состояния для отображения. Но приведенный выше код дает базовое представление о том, как правильно реализовать потоковую обработку.
Вышеуказанное также применимо к загрузке одного большого объекта, такого как загрузка файла: вместо того, чтобы каждый раз вызывать AddRange (), просто накапливайте данные до тех пор, пока все они не будут загружены, а затем установите свойство, содержащее данные. Обратите внимание, что если сам объект включает DispatcherObjects, он должен быть десериализован в потоке пользовательского интерфейса. Сделайте это, вызвав Dispatcher.BeginInvoke из потока.