在 UWP 中从后台线程访问 UI 的正确方法 [英] Correct way to access UI from background thread in UWP

查看:29
本文介绍了在 UWP 中从后台线程访问 UI 的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当尝试将数据添加到 ObservableCollection(x:Binded in XAML 到 UI 上的 ListView)时,我收到此错误:应用程序调用了为不同线程编组的接口(来自 HRESULT 的异常: 0x8001010E (RPC_E_WRONG_THREAD))

When trying to add data to an ObservableCollection, which is x:Binded in XAML to a ListView on the UI, I get this error:The application called an interface that was marshalled for a different thread (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

我的应用实现了 MVVM.VieModel 使用事件处理程序提取数据.我尝试了各种 SO 和其他解决方案,例如:

My app implements MVVM. VieModel pulls data with an eventhandler. I have tried various SO and other solutions, such as:

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

这给出了错误:Current.Get == null

无法直接访问 Dispatcher,因为它在 ViewModel 中被调用.也没有 .Invoke 或 .BeginInvoke 访问权限,我可以找到建议的几种解决方案.

Can't access Dispatcher directly, as it is being called in the ViewModel. Also no .Invoke or .BeginInvoke access that I could find which several solutions suggested.

我尝试使用 DispatcherTimer,根据:从非 UI 线程更新 ObservableCollection.尝试在 ViewModel 中实例化 DispatcherTimer 以访问 UI 线程时,我收到 "WRONG_THREAD" 错误消息:

I tried using DispatcherTimer, as accoring to: Updating ObservableCollection from non UI thread. I get the "WRONG_THREAD" error message when trying to instantiate DispatcherTimer in the ViewModel, to access the UI thread:

disPatchTimer = new DispatcherTimer();

有一个建议行得通,很多人都赞成:该应用程序调用了一个为不同线程编组的接口 - Windows 应用商店应用程序

There is one suggestion that works, which many have upvoted here: The application called an interface that was marshalled for a different thread - Windows Store App

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

哪个至少看起来不像创造者"为了正确使用代码而想到的优雅解决方案?

Which does not, at least, look like an elegant solution the "creators" had in mind to properly use code?

推荐答案

VieModel 通过订阅事件将数据从模型中提取到它的 ObservableCollecton 中.

The VieModel pulls data from the model into it's ObservableCollecton via a subscribed event.

您同时提到了拉动"和事件".事件本质上是推送"系统 - 事件被推送到您的代码.但是,有些系统会通过事件产生异步结果,因此我假设这就是您在此处处理的内容,因为您指定了拉取"数据.

You mention both "pull" and "event". Events are by nature "push" systems - the event is pushed to your code. However, there are some systems that produce asynchronous results via an event, so I assume that is what you're dealing with here since you specify the data is "pulled".

如果这是正确的,那么最好的解决方案是首先为基于事件的异步编写一个包装器,使其成为基于任务的异步.如果您的数据服务如下所示:

If this is correct, then the best solution is to first write a wrapper for the event-based asynchrony so it becomes task-based asynchrony. If you have a data service that looks like this:

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

然后包装器看起来像这样:

then the wrapper would look something like this:

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

一旦你有了一个基于任务的异步模式方法,那么使用它并更新你的视图模型就很简单了:

Once you have a Task-based asynchronous pattern method, then consuming it and updating your viewmodel is straightforward:

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

这种方法允许您使用 await 的上下文捕获特性,这样您就不必自己进行任何线程转换.

This approach allows you to use the context-capturing nature of await so that you don't have to do any thread transitions yourself.

这篇关于在 UWP 中从后台线程访问 UI 的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆