Как запустить функцию в фоновом потоке для Windows Phone 7? - PullRequest
11 голосов
/ 20 июля 2010

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

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

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

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

Заранее спасибо, Джефф.

Редактировать - вот пример модели

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....
    }
}

Ответы [ 3 ]

16 голосов
/ 22 июля 2010

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

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

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

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

Итак, вот что я делаю в объекте-базе данных singleton, чтобы загрузить и вернуть мою "таблицу" 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");
}

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

Теперь в ViewModel я хочу привязать список TourViewModels, который я выбираю с помощью запроса linq, как только вижу, чтоТаблица туров изменилась.Вероятно, это немного дешево для прослушивания события Database во 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 сработает, установите видимость Collapsed, чтобы индикатор прогресса исчез.

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

0 голосов
/ 09 сентября 2010

Джефф, я все еще сам разбираюсь в этом.Я написал похожий вопрос и в итоге сам ответил на него, создав простой пример.Здесь:

Супер-простой образец MVVM-Light WP7?

Краткое содержание:

1) Я получил свою модель (да, мою модель) от ViewModelBase.Это дает мне реализацию сообщений Mvvm-Light и INotifyPropertyChanged, что удобно.Вы можете утверждать, что это не «чисто», но я не думаю, что это имеет значение.

2) Я использовал хелпер Mvvm-Light DispatcherHelper.CheckBeginInvokeOnUI так же, как и вы (из моей Модели, а не из моей ViewModel).

Надеюсь, это поможет.

0 голосов
/ 20 июля 2010

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

Вот пример кода «Обедающий философ» из статьи, который должен дать вам хорошую идеюо том, как вызвать событие в пользовательском интерфейсе из другого потока:

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 падает ... что именно вы видите, когда он падает?Вы получаете исключение?

...