我刚刚从Prism 4.1更新到5,过去工作正常的代码现在抛出InvalidOperationExceptions.我怀疑根本原因是更新的异步DelegateCommands没有正确地编组到UI线程.
我需要能够从任何线程调用command.RaiseCanExecuteChanged()并为此在UI线程上引发CanExecuteChanged事件.Prism文档说这就是RaiseCanExecuteChanged()方法应该做的事情.然而,随着Prism 5的更新,它不再有效.CanExecuteChanged事件在非UI线程上调用,我获得下游InvalidOperationExceptions,因为在这个非UI线程上访问UI元素.
这是Prism文档,它提供了一个解决方案的提示:
DelegateCommand包括对异步处理程序的支持,并已移至Prism.Mvvm可移植类库.DelegateCommand和CompositeCommand都使用WeakEventHandlerManager来引发CanExecuteChanged事件.必须首先在UI线程上构造WeakEventHandlerManager,以正确获取对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);
}
Run Code Online (Sandbox Code Playgroud)
这是异常堆栈:
测试方法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(对象发件人,EventArgs e)上System.Windows.Input.CanExecuteChangedEventManager.HandlerSink.OnCanExecuteChanged(对象发件人,EventArgs e)上Microsoft.Practices.Prism.Commands.WeakEventHandlerManager .CallHandler(对象发件人,事件处理程序事件处理程序)在Microsoft.Practices.Prism.Commands.WeakEventHandlerManager.CallWeakReferenceHandlers(对象发件人,List`1处理程序)在Microsoft.Practices.Prism.Commands.DelegateCommandBase.OnCanExecuteChanged()在Microsoft.Practices.Prism .Commands.DelegateCommandBase.RaiseCanExecuteChanged()在Calypso.Pharos.Commands.Test.PatientSessionCommandsTests <> c__DisplayClass10.b__e()中PatientSessionCommandsTests.cs:线71在SYSTE m.Threading.Tasks.Task.InnerInvoke()在System.Threading.Tasks.Task.Execute()---从先前位置栈跟踪其中抛出异常---在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess完(任务任务)位于Patient.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)的System.Runtime.CompilerServices.TaskAwaiter.GetResult(),位于PatientSessionCommandsTests.cs中的Calypso.Pharos.Commands.Test.PatientSessionCommandsTests.d__12.MoveNext() :行71 ---从先前的位置堆栈跟踪其中的例外是在系统抛出---在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(工作任务)在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(工作任务)结束.Runtime.CompilerServices.TaskAwaiter.GetResult()