簡體   English   中英

如何在Windows Phone 7的后台線程上運行函數?

[英]How to run a function on a background thread for Windows Phone 7?

我正在使用MVVM Light來構建WP7(Windows Phone 7)應用程序。 我希望模型執行的所有工作都在后台線程上運行。 然后,完成工作后,引發一個事件,以便ViewModel可以處理數據。

我已經發現我無法從WP7應用程序異步調用委托。

目前我正在嘗試使用ThreadPool.QueueUserWorkItem()在后台線程上運行一些代碼,並使用MVVM Light的DispatcherHelper.CheckBeginInvodeOnUI()在UI線程上引發一個事件,以通知ViewModel數據已被加載(這會導致VS2010崩潰)和Blend 4在嘗試顯示設計時視圖時)。

是否有任何示例代碼在后台線程上運行某些代碼,然后將事件調度回UI線程以獲取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....
    }
}

以下是我如何解決這個問題的方法。

您的ViewModel實現了INotifyPropertyChanged嗎? 沒有必要發送活動。 只需在模型中“裸露”它們,然后在ViewModel中調度RaisePropertyChanged。

是的,你的代碼中應該有一些單例模型/數據庫。 畢竟,如果不是一些巨大的單身人士,什么是SQL數據庫? 由於我們在WP7中沒有數據庫,所以不要害羞地創建單例對象。 我有一個名為“數據庫”:)

我剛試過在那里線程化我的dataloads,並意識到實際上最好的方法就是在模型級別直接實現INotifyPropertyChanged。 這沒有什么可恥的

所以,這就是我在單例數據庫對象中所做的事情,以加載並返回我的Tours“表”(注意thread.sleep使得它需要花費可見的時間來加載,通常是它的子100ms)。 數據庫類現在實現了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列表,一旦我看到Tours表已經改變,我就用linq查詢選擇它。 在ViewModel中監聽數據庫事件可能有點便宜 - 將它封裝在模型中可能“更好”,但是我們不需要做工作嗎?

在Viewmodel的構造函數中鈎住Database事件:

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中,向您的頁面添加帶有IsIndeterminate = True的ProgressBar可能會很好 - 這將顯示“虛線”進度條。 然后你可以做的是當ViewModel首次加載時你可以將“ProgressBarVisible”屬性設置為Visible(並引發相關的PropertyChanged事件)。 將ProgressBar的可見性綁定到此ViewModel屬性。 觸發Database PropertyChanged事件時,將可見性設置為Collapsed以使進度條消失。

這樣,在反序列化運行時,用戶將在其屏幕頂部看到“IsIndeterminate”進度條。 太好了!

我以前沒有為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會崩潰......當你崩潰時你究竟看到了什么? 你有例外嗎?

傑夫,我還在自己搞這個東西。 我發布了一個類似的問題,最后通過構建一個簡單的樣本來回答它。 這里:

一個超級簡單的MVVM-Light WP7樣本?

摘要是:

1)我從ViewModelBase派生了我的模型(是的我的模型)。 這給了我Mvvm-Light的消息傳遞和INotifyPropertyChanged的實現,這很方便。 你可以說這不是“純粹的”,但我認為這不重要。

2)我使用Mvvm-Light DispatcherHelper.CheckBeginInvokeOnUI幫助器(就像我的模型,而不是我的ViewModel)。

希望這可以幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM