1

我正在使用MVVM模式爲Windows Store和Windows Phone 8開發同一應用程序的兩個版本。每個應用都有自己的視圖。 Model和ViewModel在Portable Class Libraray中共享。我使用TPL任務在模型中執行異步操作。由於可移植類庫的限制,我無法使用async和await關鍵字。如何在異步操作後返回到UI線程

任務完成後,我想回到UI線程並更新一些屬性(這會導致ViewModel和View也更新)。

在我看來,這似乎是一個很常見的情況,所以我有點困惑,爲什麼它變得如此艱難。

我嘗試了兩種不同的方法:在開始操作

TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

然後將它傳遞給ContinueWith方法之前

一個(不工作)

保存到調度一個參考。

myTask.ContinueWith(t => myTaskCompleted(t.Result), scheduler); 

在我看來,這似乎是一個很好的解決方案,但不起作用。 myTaskCompleted仍然在不同的線程中執行。

現在我試圖使用

Dispatcher.RunAsync(CoreDispatcherPriority.Normal, handler); 

因爲我不能從PCL使用分派器直接,我傳遞給它(隱藏在一個包裝)的引用,以幾乎所有的對象在模型中。 (就像在這answer)這最後的作品,但它是相當複雜和醜陋。

所以我的問題是:

  1. 這是建議的方式要回UI線程便攜類Libraray裏面?
  2. 我嘗試的錯誤是什麼一個

我知道那裏已經有很多關於這個話題的問題,但不幸的是沒有真正解決我的問題。

+7

第一次嘗試時代碼的上下文是什麼?請記住,如果您目前不在UI線程中,那麼TaskScheduler.FromCurrentSynchronizationContext()不會獲取UI線程的上下文。如果您已經在後臺線程中,則爲時已晚,您需要早一點抓取它。 – Servy 2013-03-12 21:07:23

+0

Duplicate:http://stackoverflow.com/questions/11258164/portable-class-library-equivalent-of-dispatcher-invoke-or-dispatcher-runasync(至少他們看起來是一樣的我) – 2013-03-12 21:07:53

+4

你看着[ Microsoft.Bcl.Async(http://nuget.org/packages/Microsoft.Bcl.Async/)?你的第一種方法對我來說是正確的 - 除非@Servy建議你的'FromCurrentSynchronizationContext'不在UI線程上運行。 – 2013-03-12 21:13:19

回答

2

TPL將使用線程池中的一個線程,並且UI線程是不在線程池中的「主線程」,永遠不可用於運行任務。使用ContinueWith函數將從線程池中獲取另一個線程以執行您的代碼。您遇到的問題的核心在於Windows Phone不會排列屬性更改,並會直接嘗試更新視圖。在代碼中的某處,您應該有一個更改函數,它可以廣播屬性更改。我會用我的:

public void Changed(string Key) { 
    // Check if the property changed has subscribers. 
    if (PropertyChanged != null) { 
     // Invoke the property changed. 
     PropertyChanged(this, new PropertyChangedEventArgs(Key)); 
    } 
} 

改變功能將運行在一個WPF應用程序很好,因爲WPF將排隊性質的變化,並處理他們在接下來的UI框架的更新。由於Windows Phone沒有,我們需要建立一個模式來在運行時改變這種行爲。我做了一個名爲調度程序的屬性,我允許在運行時設置它。我所有的廣播已從更改爲改爲調度員

private Action<string> _Dispatcher; 
public Action<string> Dispatcher { 
    get { 
     if (_Dispatcher == null) { 
      return Changed; 
     } 
     return _Dispatcher; 
    } 
    set { 
     _Dispatcher = value; 
    } 
} 

所以現在我們可以在運行時在我們的Windows Phone應用程序更改調度。我們需要編寫一個推遲更改的函數,直到UI線程處於活動狀態才能廣播更改。我在一個擴展中做了這個,所以在ViewModel上附加UI線程安全性會更容易一些。運行時更改將簡單地使用Windows Phone 調度程序在UI線程上調度廣播。的實現如下:

public static void Attach(this ViewModelStore ViewModelStore, DependencyObject DependencyObject) { 
    // Set the changed event dispatcher. 
    ViewModelStore.Dispatcher = (Key) => { 
     // Begin invoking of an action on the UI dispatcher. 
     DependencyObject.Dispatcher.BeginInvoke(() => { 
      // Raise the changed event. 
      ViewModelStore.Changed(Key); 
     }); 
    }; 
} 

ViewModelStore是我一直在用我所有的視圖模型的通用類,所以這個功能可以讓我的線程安全的廣播機構附接到所有的視圖模型。 DependencyObject是一個UI組件,例如視圖。現在,您真正需要做的就是在視圖模型上調用附加

ProviderViewModel.Attach(this); // This is inside a Page. 

所有廣播都沒有委託給UI線程,並調用了UI進入的下一幀,並相應地更新了所有內容。您不必擔心這樣的線程安全性,但您需要記住在您的Windows Phone應用程序中附加視圖模型的新實例。讓我知道是否還有其他問題,祝你好運!

相關問題