Jak uruchomić funkcję w wątku tła dla Windows Phone 7?

Używam MVVM Light do budowy aplikacji WP7 (Windows Phone 7). Chciałbym, aby wszystkie prace wykonywane przez Model były prowadzone na wątku tła. Następnie, po zakończeniu pracy, wywołaj zdarzenie, aby model widoku mógł przetwarzać dane.

Już się dowiedziałem, że nie mogę wywołać delegata asynchronicznie z aplikacji WP7.

Obecnie próbuję użyć ThreadPool.QueueUserWorkItem () aby uruchomić jakiś kod w wątku tła i użyć MVVM Light ' s Dyspozytor helper.CheckBeginInvodeOnUI() wywołanie zdarzenia w wątku UI, aby zasygnalizować Viewmodelowi, że dane zostały załadowane (powoduje to awarię VS2010 i Blend 4, gdy próbują wyświetlić widok czasu projektowania).

Czy Jest jakiś przykładowy kod do uruchomienia kodu w wątku w tle, a następnie wysłania zdarzenia z powrotem do wątku interfejsu dla aplikacji WP7?

Z góry dzięki, Jeff.

Edit-Oto przykładowy Model

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....
    }
}
Author: Jeff R, 2010-07-20

3 answers

Oto, jak podchodziłbym do rozwiązania tego problemu.

Twoj ViewModel implementuje INotifyPropertyChanged right? Nie ma potrzeby powiadamiania o zdarzeniach. Wystarczy podnieść je" gołe " w modelu, a następnie wysłać RaisePropertyChanged w ViewModel.

I tak, powinieneś mieć jakiś model Singletona / bazę danych w swoim kodzie. W końcu czym jest baza danych SQL, jeśli nie jakiś gigantyczny singleton? Ponieważ nie mamy bazy danych w WP7, nie wstydź się tworzyć obiektu singleton. Mam jedną. nazywa się "baza danych":)

Właśnie próbowałem threading moje dataloads tam, i zdaję sobie sprawę, że w rzeczywistości najlepszym podejściem jest po prostu wdrożenie INotifyPropertyChanged na poziomie modelu. nie ma w tym wstydu .

Więc biorąc pod uwagę, że oto co robię w obiekcie bazy danych singleton, aby załadować i zwrócić moje wycieczki "tabela" (zwróć uwagę na wątek.uśpienia, aby załadować widoczną ilość czasu, zwykle jego sub 100ms). Klasa bazy danych implementuje teraz INotifyPropertyChanged, i podnosi zdarzenia po zakończeniu ładowania:

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");
}
Nadążasz? Deserializuję listę wycieczek w wątku tła, a następnie wprowadzam odpowiednie wydarzenie.

Teraz w ViewModel, chcę listę TourViewModels do powiązania, które wybieram z zapytaniem linq, gdy widzę, że tabela Tours została zmieniona. Pewnie trochę tani jest odsłuchiwanie zdarzenia bazy danych w Viewmodelu - może być "ładniej" to ująć w modelu, ale nie róbmy nie musimy pracować, co?

Zaczep Zdarzenie bazy danych w konstruktorze Viewmodel:

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

Posłuchaj odpowiedniej zmiany tabeli (we love magic strings! ;-) ):

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

Wybierz rekordy, które chcę z tabeli, a następnie powiedz widoku, że są nowe dane:

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

  RaisePropertyChanged( "AllTours" );
}

I wreszcie, w Twoim ViewModelBase, najlepiej sprawdzić, czy Twój RaisePropertyChanged wymaga wysyłki. Moja metoda "SafeDispatch" jest prawie taka sama jak ta z MVVMlight:

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

To działa idealnie w moim kodzie, i myślę, że jest dość schludny?

Wreszcie, extra dla ekspertów: w WP7, może być dobrze dodać pasek postępu z Isindeterminate = True do swojej strony - wyświetli to" kropkowany " pasek postępu. Wtedy po pierwszym załadowaniu ViewModel można ustawić właściwość "ProgressBarVisible" na Visible (i podnieść powiązane Zdarzenie właściwości). Powiązać widoczność paska postępu z właściwością ViewModel. Kiedy Właściwości bazy danych wywołane zdarzenie, Ustaw widoczność na zwiniętą, aby pasek postępu zniknął.

W ten sposób użytkownik zobaczy pasek postępu "IsIndeterminate" u góry ekranu podczas wykonywania deserializacji. Nieźle!

 16
Author: Ben Gracewood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-22 22:29:32

Nie opracowałem wcześniej dla WP7, ale znalazłem Ten artykuł, który może być przydatny !

Oto przykładowy kod z artykułu, który powinien dać ci dobry pomysł, jak przenieść wydarzenie do interfejsu użytkownika z innego wątku:

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);
        });
}

Mam nadzieję, że to pomoże :).

Jedna rzecz jest myląca: powiedziałeś, że gdy używasz helpera do wywołania zdarzenia, VS2010 ulega awarii... co dokładnie widzisz, gdy się rozbija? Czy dostajesz wyjątek?

 0
Author: Kiril,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-20 19:41:40

Jeff, sam wciąż nad tym pracuję. Opublikowałem podobne pytanie, a skończyło się na tym, że sam na nie odpowiedziałem, budując prostą próbkę. Tutaj:

Super prosta próbka MVVM-Light WP7?

Podsumowanie to:

1) wyprowadziłem mój Model (Tak mój model) z ViewModelBase. To daje mi implementację Mvvm-Light do przesyłania wiadomości i INotifyPropertyChanged, co jest przydatne. Można argumentować, że to nie jest "czyste", ale nie sądzę, że to ma znaczenie.

2) użyłem Mvvm-Light DispatcherHelper.CheckBeginInvokeOnUIhelper tak jak zrobiłeś (od mojej modelki, nie od mojej modelki).

Mam nadzieję, że to pomoże.
 0
Author: tig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:48:23