WPF Dispatcher не работает как BGWorker - PullRequest
1 голос
/ 22 декабря 2011

Я нахожусь в середине приложения MVVM. Здесь у меня есть коллекция (ObservableCollection), которая может иметь операции добавления, удаления, выполненные из другого потока. Вот код Я старался сделать его как можно более полным:

private Thread _thread = null;
private Dispatcher _UIDispatcher = null;
public ObservableCollection<string> ListOfStrings { get; private set; }

Ктор:

 public MainWindowViewModel(Dispatcher dispatcher)
 {
            this.ListOfStrings = new ObservableCollection<string>();
            this.StartAsyncCall = new RelayCommand(AsyncCall, CanCallAsynch);
            this._UIDispatcher = dispatcher;
 }

Я принимаю диспетчера View в качестве справочного материала (хотя это не очень хорошая практика, поскольку это затрудняет юнит-тестирование. Гэри Холл предложил хороший подход с использованием AOP. Однако я могу изменить это позже в любое время.). И тогда этот метод, который я вызываю с помощью команды

private void AsyncCall()
{
    if (this.ListOfStrings.Count > 0)
       this.ListOfStrings.Clear();
     //_backgroundWorker.RunWorkerAsync();
    this._thread = new Thread(new ThreadStart(AddNumbersToList));
    this._thread.IsBackground = true;
    this._thread.Start();
}
 private void AddNumbersToList()
 {
       Action delAddNum = new Action(AddNumbersToList);
       for (int i = 0; i < 100000000; i++)
       {
           if (null != this._UIDispatcher &&
            !this._UIDispatcher.CheckAccess())
                    _UIDispatcher.Invoke(DispatcherPriority.Render,delAddNum);
           else
           {
                    this.ListOfStrings.Add(Convert.ToString(i.ToString()));
           }
        }
  }

ObservableCollection привязывается к списку в представлении.

Эффект, который я ожидал, состоял в том, что, поскольку ObservableCollection добавляет новые числа, они должны отображаться в пользовательском интерфейсе. Пользовательский интерфейс тоже будет отзывчивым. Но, как ни странно, пользовательский интерфейс не отвечает. То же самое, когда я пытаюсь достичь с помощью фонового работника, прекрасно достигается. Может кто-нибудь, пожалуйста, укажите, где я иду не так. Я не хочу переходить к фоновому работнику, поскольку хочу гибкости использования более детального класса Thread. Ниже приведен код, который я использую с BGWorker, и он работает нормально:

    private BackgroundWorker _backgroundWorker = new BackgroundWorker();

CTOR:

public MainWindowViewModel()
{
     this.ListOfStrings = new ObservableCollection<string>();
     this.StartAsyncCall = new RelayCommand(AsyncCall, CanCallAsynch);
     _backgroundWorker.DoWork += AddNumbersToList;
     _backgroundWorker.RunWorkerCompleted += this.LoadResultsCompleted;
     _backgroundWorker.WorkerReportsProgress = true;
     _backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(_backgroundWorker_ProgressChanged);
}

Методы:

 private void AsyncCall()
 {
    if (this.ListOfStrings.Count > 0)
      this.ListOfStrings.Clear();
      this._backgroundWorker.RunWorkerAsync();
 }

private void AddNumbersToList(object sender, DoWorkEventArgs e)
{
      BackgroundWorker bw = sender as BackgroundWorker;
      if(null!=bw)
      for (int i = 0; i < 10; i++)
      {
           bw.ReportProgress(0, i);
                    Thread.Sleep(1000);
        }
 }


 private void LoadResultsCompleted(object sender, RunWorkerCompletedEventArgs e)
 {
   if (e.Error == null)
   {
            this.ListOfStrings.Add("Completed");
            CommandManager.InvalidateRequerySuggested();
   }
 }

//This works just wonderful:
void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    this.ListOfStrings.Add(Convert.ToString(e.UserState.ToString()));
}

Заранее спасибо.

.. Джеймс

1 Ответ

0 голосов
/ 22 декабря 2011

Это рекурсия, которая вешает ваш код ...

Это работает для меня ...

    private void AsyncCall()
    {
        if (this.MyCollection.Count > 0)
            this.MyCollection.Clear();

        this._thread = new Thread(new ThreadStart(AddNumbersToList));
        this._thread.IsBackground = true;

        this._thread.Start();
    }

    private void AddNumbersToList()
    {
        for (int i = 0; i < 100000000; i++)
        {
            this.Dispatcher.Invoke(
                new Action(
                    delegate
                        {
                            this.MyCollection.Add(Convert.ToString(i.ToString()));
                        }));

            Thread.Sleep(100);
        }
    }
...