簡體   English   中英

在 UWP 中從后台線程訪問 UI 的正確方法

[英]Correct way to access UI from background thread in UWP

當嘗試將數據添加到 ObservableCollection 時,它是 x:Binded in XAML 到 UI 上的 ListView,我收到此錯誤: The application called an interface that was marshalled for a different thread (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

我的應用程序實現了 MVVM。 VieModel 使用事件處理程序提取數據。 我嘗試了各種 SO 和其他解決方案,例如:

var ignored = Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,() =>ViewList.Add(msg));

這給出了錯誤: Current.Get == null

無法直接訪問 Dispatcher,因為它在 ViewModel 中被調用。 也沒有 .Invoke 或 .BeginInvoke 訪問,我可以找到幾個解決方案建議。

我嘗試使用 DispatcherTimer,根據: 更新 ObservableCollection from non UI thread 嘗試在 ViewModel 中實例化 DispatcherTimer 以訪問 UI 線程時,我收到"WRONG_THREAD"錯誤消息:

disPatchTimer = new DispatcherTimer();

有一個建議行得通,很多人都在這里贊成: 該應用程序調用了一個接口,該接口為不同的線程編組 - Windows 應用商店應用程序

Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
    {
        // Your UI update code goes here!
    }
);

至少,哪個看起來不像“創建者”為了正確使用代碼而想到的優雅解決方案?

您應該做的是使用具有RunOnUIThreadAsync方法或類似方法的接口注入您的視圖模型。

然后,您將創建一個實現此接口的類,並在 UWP 應用中調用Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync ,您可以假設CoreApplicationView始終可用。

在針對沒有CoreApplicationView的視圖模型的單元測試中,您可以簡單地模擬界面。

另一種選擇是查看BindingOperations.EnableCollectionSynchronization API,它允許您從多個線程訪問集合。

VieModel 通過訂閱事件將數據從模型中提取到它的 ObservableCollecton 中。

你提到了“拉”和“事件”。 事件本質上是“推送”系統 - 事件被推送到您的代碼。 但是,有些系統會通過事件產生異步結果,因此我假設這就是您在此處處理的內容,因為您指定了“拉取”數據。

如果這是正確的,那么最好的解決方案是首先為基於事件的異步編寫一個包裝器,使其成為基於任務的異步 如果您有一個如下所示的數據服務:

class MyDataService
{
  public void DownloadData();
  public event MyDataArrivedEventHandler MyDataArrived;
}

那么包裝器看起來像這樣:

public static Task<MyData> GetMyDataAsync(this MyDataService service)
{
  var tcs = new TaskCompletionSource<MyData>();
  MyDataArrivedEventHandler handler = null;
  handler = (s,e) =>
  {
    service.MyDataArrived -= handler;
    if (e.Error != null) 
      tcs.TrySetException(e.Error);
    else 
      tcs.TrySetResult(e.Data);
  };
  service.MyDataArrived += handler;
  service.DownloadData();
  return tcs.Task;
}

一旦你有了一個基於任務的異步模式方法,那么使用它並更新你的視圖模型就很簡單了:

// From UI thread.
var data = await service.GetMyDataAsync();
viewModel.AddRange(data); // or whatever

這種方法允許您使用await的上下文捕獲特性,這樣您就不必自己進行任何線程轉換。

暫無
暫無

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

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