WPF Statusbar Updates - помогите, я, кажется, обхожу круги - PullRequest
2 голосов
/ 12 апреля 2010

Кажется, я ходил кругами.

У меня есть приложение WPF, у которого есть главное окно ленты со строкой состояния. При переходе к «представлению» пользовательский элемент управления отображается в качестве содержимого главного окна.

Представление имеет ViewModel, которая обрабатывает извлечение данных из базы данных, а текстовый вид представления установлен на ViewModel.

Мне нужно, чтобы длительная операция (извлечение данных) выполнялась в фоновом потоке, и пока он запускает состояние в главном окне, чтобы отчитываться соответствующим образом. Когда фоновая задача завершена, статус должен вернуться к «Готово» (во многом аналогично Visual Studio).

Как мне соединить это вместе, чтобы я мог выделить код доступа к данным во ViewModel, сохраняя при этом отзывчивый интерфейс?

Я пытался использовать BackgroundWorker в разных местах кода, и у меня все еще не работает пользовательский интерфейс.

Ответы [ 2 ]

1 голос
/ 19 апреля 2010

Мне кажется, что интерфейс 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 из потока.

1 голос
/ 19 апреля 2010

Вы можете привязать текстовый блок (индикатор выполнения) к свойству зависимости. Затем сделайте ваши вещи в отдельном потоке и обновите свойство зависимости в соответствии с прогрессом. Не забывайте, что вы должны делать это в правильной ветке, поэтому храните диспетчер.

...