我正在使用MVVM模式为Windows Store和Windows Phone 8开发相同应用程序的两个版本。每个应用都有自己的视图。 Model和ViewModel在Portable Class Libraray中共享。我正在使用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,所以我将对它的引用(隐藏在包装器中)传递给模型中的几乎每个对象。 (就像在这个answer)这最终有效,但它非常复杂和丑陋。
所以我的问题是:
我知道这个话题已经有很多问题,但遗憾的是没有什么能真正解决我的问题。
答案 0 :(得分:2)
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 的属性,我允许在运行时设置它。我的所有广播现已从已更改更改为 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线程上进行scheduele广播。实施如下:
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应用程序中附加视图模型的新实例。如果还有其他问题,请告诉我,祝你好运!