问题描述
当尝试将数据添加到 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?
推荐答案
您同时提到了拉动"和事件".事件本质上是推送"系统 - 事件被推送到您的代码.但是,有些系统会通过事件产生异步结果,因此我假设这就是您在此处处理的内容,因为您指定了拉取"数据.
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 的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!