Как выполнить функцию на фоновом потоке для Windows Phone 7?

Я использую Свет MVVM для создания WP7 (Windows Phone 7) приложение. Я хочу иметь всю работу, выполненную Моделью, которая будет выполнена на фоновом потоке. Затем когда работа сделана, сгенерируйте событие так, чтобы ViewModel мог обработать данные.

Я уже узнал, что не могу вызвать Delegate асинхронно из приложения WP7.

В настоящее время я пытаюсь использовать ThreadPool. QueueUserWorkItem (), чтобы выполнить некоторый код фонового потока и использовать DispatcherHelper Света MVVM. CheckBeginInvodeOnUI (), чтобы сгенерировать событие на UI распараллеливают для передачи сигналов о ViewModel, что данные были загружены (это разрушает VS2010 и Смешение 4, когда они пытаются отобразить представление времени проектирования).

Там какой-либо пример кода должен выполнить некоторый код фонового потока и затем отправить событие назад потоку UI для приложения WP7?

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

Редактирование - Вот является демонстрационной моделью

public class DataModel
{
    public event EventHandler<DataLoadingEventArgs> DataLoadingComplete;
    public event EventHandler<DataLoadingErrorEventArgs> DataLoadingError;
    List<Data> _dataCasch = new List<Data>();

    public void GetData()
    {
        ThreadPool.QueueUserWorkItem(func =>
        {
            try
            {
                LoadData();
                if (DataLoadingComplete != null)
                {
                    //Dispatch complete event back to the UI thread
                    DispatcherHelper.CheckBeginInvokeOnUI(() =>
                    {
                       //raise event 
                        DataLoadingComplete(this, new DataLoadingEventArgs(_dataCasch));
                    });
                }
            }
            catch (Exception ex)
            {
                if (DataLoadingError != null)
                {
                    //Dispatch error event back to the UI thread
                    DispatcherHelper.CheckBeginInvokeOnUI(() => 
                    {
                        //raise error
                        DataLoadingError(this, new DataLoadingErrorEventArgs(ex));
                    });
                }
            }
        });
    }

    private void LoadData()
    {
        //Do work to load data....
    }
}
11
задан Jeff R 20 July 2010 в 19:44
поделиться

2 ответа

Вот как я подхожу к решению этой проблемы.

Ваша ViewModel реализует INotifyPropertyChanged, верно? Нет необходимости отправлять События. Просто поднимите их «голыми» в модели, а затем отправьте RaisePropertyChanged в ViewModel.

И да, в вашем коде должна быть какая-то одноэлементная модель / база данных. В конце концов, что такое база данных SQL, как не какой-то гигантский синглтон? Поскольку у нас нет базы данных в WP7, не стесняйтесь создавать одноэлементный объект. У меня есть одна под названием «База данных» :)

Я только что попробовал распределить туда свои данные и понял, что на самом деле лучший подход - это просто реализовать INotifyPropertyChanged прямо на уровне модели. В этом нет ничего постыдного.

Учитывая это, вот что я делаю в одноэлементном объекте Database для загрузки и возврата моей «таблицы» Tours (обратите внимание на thread.sleep, чтобы он загружался за видимое время, обычно это менее 100 мс) .Класс базы данных теперь реализует INotifyPropertyChanged и вызывает события по завершении загрузки:

public ObservableCollection<Tour> Tours
{
  get
  {
    if ( _tours == null )
    {
      _tours = new ObservableCollection<Tour>();
      ThreadPool.QueueUserWorkItem(LoadTours);
    }
    return _tours;
  }
}

private void LoadTours(object o)
{
  var start = DateTime.Now;
  //simlate lots of work 
  Thread.Sleep(5000);
  _tours = IsoStore.Deserialize<ObservableCollection<Tour>>( ToursFilename ) ??  new ObservableCollection<Tour>();
  Debug.WriteLine( "Deserialize time: " + DateTime.Now.Subtract( start ).ToString() );
  RaisePropertyChanged("Tours");
}

Вы следите? Я десериализую список Tour в фоновом потоке, а затем вызываю событие propertychanged.

Теперь в ViewModel я хочу привязать список моделей TourViewModel, которые я выбираю с помощью запроса linq, как только вижу, что таблица Tours изменилась. Слушать событие базы данных в ViewModel, вероятно, немного дешево - было бы «лучше» инкапсулировать это в модели, но давайте не будем выполнять работу, в которой нам не нужно, а?

Перехватить событие базы данных в конструктор Viewmodel:

public TourViewModel()
{
Database.Instance.PropertyChanged += DatabasePropertyChanged;
}

Прослушайте соответствующее изменение таблицы (мы любим волшебные строки! ;-)):

private void DatabasePropertyChanged(object sender, PropertyChangedEventArgs e)
{
  if(e.PropertyName == "Tours")
  {
    LoadTourList();
  }
}

Выберите записи, которые я хочу, из таблицы, затем сообщите представлению, что есть новые данные:

public void LoadTourList()
{
  AllTours = ( from t in Database.Instance.Tours
    select new TourViewModel( t ) ).ToList();

  RaisePropertyChanged( "AllTours" );
}

И, наконец, в вашей ViewModelBase лучше всего проверить, нуждается ли ваш RaisePropertyChanged в диспетчеризации. Мой метод "SafeDispatch" почти такой же, как и метод из MVVMlight:

private void RaisePropertyChanged(string property)
{
  if ( PropertyChanged != null )
  {
    UiHelper.SafeDispatch(() =>
      PropertyChanged(this, new PropertyChangedEventArgs(property)));
  }
}

Он отлично работает в моем коде, и я думаю, что он довольно аккуратный?

Наконец, дополнительно для экспертов: в WP7 было бы неплохо добавьте на страницу ProgressBar с IsIndeterminate = True - это отобразит пунктирную полосу выполнения. Затем вы можете сделать то, что при первой загрузке ViewModel вы можете установить для свойства ProgressBarVisible значение Visible (и вызвать связанное событие PropertyChanged). Свяжите видимость ProgressBar с этим свойством ViewModel. При возникновении события Database PropertyChanged установите для видимости значение «Свернуто», чтобы индикатор выполнения исчез.

Таким образом, пользователь будет видеть индикатор выполнения «IsIndeterminate» в верхней части экрана во время выполнения десериализации. Хороший!

16
ответ дан 3 December 2019 в 07:36
поделиться

Я раньше не разрабатывал для WP7, но нашел эту статью, которая может оказаться полезной!

Вот пример кода Dining Philosopher из статьи, который должен дать вам хорошее представление о том, как поднять событие в UI из другого потока:

public DinnersViewModel(IDinnerCatalog catalog)
{
    theCatalog = catalog;
    theCatalog.DinnerLoadingComplete +=
        new EventHandler<DinnerLoadingEventArgs>(
              Dinners_DinnerLoadingComplete);
}

public void LoadDinners()
{
    theCatalog.GetDinners();
}

void Dinners_DinnerLoadingComplete(
    object sender, DinnerLoadingEventArgs e)
{
    // Fire Event on UI Thread
    View.Dispatcher.BeginInvoke(() =>
        {
            // Clear the list
            theDinners.Clear();

            // Add the new Dinners
            foreach (Dinner d in e.Results)
                theDinners.Add(d);

            if (LoadComplete != null)
                LoadComplete(this, null);
        });
}

Надеюсь, это будет полезно :).

Одна вещь, которая смущает: вы сказали, что когда вы используете помощник для поднятия события, VS2010 падает... что именно вы видите, когда он падает? Получаете ли вы исключение?

0
ответ дан 3 December 2019 в 07:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: