如何在异步操作后返回UI线程
本文关键字:返回 UI 线程 异步操作 | 更新日期: 2023-09-27 18:03:30
我正在使用MVVM模式为Windows Store和Windows Phone 8开发同一应用程序的两个版本。每个应用程序都有自己的视图。模型和视图模型在可移植类库中共享。我通过使用TPL任务在模型内进行异步操作。由于可移植类库的限制,我不能使用async和await关键字。
任务完成后,我想回到UI线程并更新一些属性(这将导致ViewModel和View也更新)。
这对我来说似乎是一种很常见的情况,所以我有点困惑为什么它变得如此困难。
我尝试了两种不同的方法:
一个(不工作)
在开始操作之前保存对调度程序的引用
TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext();
然后将其传递给ContinueWith
方法。
myTask.ContinueWith(t => myTaskCompleted(t.Result), scheduler);
这对我来说似乎是一个很好的解决方案,但不起作用。myTaskCompleted仍然在另一个线程中执行。
第二
现在我试着使用
Dispatcher.RunAsync(CoreDispatcherPriority.Normal, handler);
因为我不能直接使用PCL中的Dispatcher,所以我将对它的引用(隐藏在包装器中)传递给模型中的几乎每个对象。(就像在这个答案)这终于工作了,但它相当复杂和丑陋。
我的问题是:
- 在可移植类库中返回UI线程的推荐方法是什么?
- 我在尝试One时犯了什么错误?
我知道这个话题已经有很多问题了,但不幸的是没有一个能真正解决我的问题。
TPL将使用线程池中的线程,UI线程是'主线程',它不在线程池中,并且永远无法在其上运行任务。使用ContinueWith函数将从线程池中抓取另一个线程来执行您的代码。你所面临的问题的核心是Windows Phone不排队属性更改,并将直接尝试更新视图。在代码的某个地方,你应该有一个Changed函数来广播属性更改。我将使用我的:
public void Changed(string Key) {
// Check if the property changed has subscribers.
if (PropertyChanged != null) {
// Invoke the property changed.
PropertyChanged(this, new PropertyChangedEventArgs(Key));
}
}
这个Changed函数在WPF应用程序下工作得很好,因为WPF会将属性更改排队,并在下一次UI帧更新时处理它们。由于Windows Phone没有,我们需要在运行时建立一个模式来改变这种行为。我创建了一个名为Dispatcher的属性,允许在运行时设置它。我所有的广播现在已经从 changed 更改为Dispatcher。
private Action<string> _Dispatcher;
public Action<string> Dispatcher {
get {
if (_Dispatcher == null) {
return Changed;
}
return _Dispatcher;
}
set {
_Dispatcher = value;
}
}
所以现在我们可以在Windows Phone应用程序的运行时更改Dispatcher。我们需要编写一个函数来延迟更改,直到UI线程处于活动状态以广播更改。我在一个扩展中做到了这一点,所以在ViewModel上附加UI线程安全更容易一些。运行时的更改将简单地使用Windows Phone Dispatcher来调度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组件,比如视图。现在,您真正需要做的就是在视图模型上调用attach。
ProviderViewModel.Attach(this); // This is inside a Page.
所有的广播都没有委托给UI线程,并在下一帧UI进来并相应地更新所有内容时调用。你不必担心这样的线程安全,但你需要记住在Windows Phone应用程序中附加一个视图模型的新实例。如果还有其他问题,请告诉我,祝你好运!