我刚刚从 Prism 4.1 更新到 5,过去可以正常工作的代码现在抛出 InvalidOperationExceptions。我怀疑根本原因是更新的异步 DelegateCommands 没有正确编组到 UI 线程。
我需要能够从任何线程调用 command.RaiseCanExecuteChanged() 并在 UI 线程上引发 CanExecuteChanged 事件。Prism 文档说这就是 RaiseCanExecuteChanged() 方法应该做的。但是,随着 Prism 5 的更新,这不再有效。CanExecuteChanged 事件在非 UI 线程上调用,并且在此非 UI 线程上访问 UI 元素时,我得到下游 InvalidOperationExceptions。
这是提供解决方案提示的 Prism 文档:
DelegateCommand 包括对异步处理程序的支持,并已移至 Prism.Mvvm 可移植类库。DelegateCommand 和 CompositeCommand 都使用 WeakEventHandlerManager 来引发 CanExecuteChanged 事件。WeakEventHandlerManager 必须首先在 UI 线程上构造,以正确获取对 UI 线程的 SynchronizationContext 的引用。
但是,WeakEventHandlerManager 是静态的,所以我无法构造它......
根据 Prism 文档,有谁知道我如何在 UI 线程上构建 WeakEventHandlerManager ?
这是重现问题的失败单元测试:
[TestMethod]
public async Task Fails()
{
bool canExecute = false;
var command = new DelegateCommand(() => Console.WriteLine(@"Execute"),
() =>
{
Console.WriteLine(@"CanExecute");
return canExecute;
});
var button = new Button();
button.Command = command;
Assert.IsFalse(button.IsEnabled);
canExecute = true;
// Calling RaiseCanExecuteChanged from a threadpool thread kills the test
// command.RaiseCanExecuteChanged(); works fine...
await Task.Run(() => command.RaiseCanExecuteChanged());
Assert.IsTrue(button.IsEnabled);
}
这是异常堆栈:
测试方法 Calypso.Pharos.Commands.Test.PatientSessionCommandsTests.Fails 抛出异常:System.InvalidOperationException:调用线程无法访问此对象,因为不同的线程拥有它。在 System.Windows.Threading.Dispatcher.VerifyAccess() 在 System.Windows.DependencyObject.GetValue(DependencyProperty dp) 在 System.Windows.Controls.Primitives.ButtonBase.get_Command() 在 System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute () 在 System.Windows.Controls.Primitives.ButtonBase.OnCanExecuteChanged(Object sender, EventArgs e) 在 System.Windows.Input.CanExecuteChangedEventManager.HandlerSink.OnCanExecuteChanged(Object sender, EventArgs e) 在 Microsoft.Practices.Prism.Commands.WeakEventHandlerManager .CallHandler(Object sender, EventHandler eventHandler) 在 Microsoft.Practices.Prism。