- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我刚刚从 Prism 4.1 更新到 5,以前工作正常的代码现在抛出 InvalidOperationExceptions。我怀疑根本原因是更新的异步 DelegateCommand 没有正确编码到 UI 线程。
我需要能够从任何线程调用 command.RaiseCanExecuteChanged() 并为此引发 UI 线程上的 CanExecuteChanged 事件。 Prism 文档说这就是 RaiseCanExecuteChanged() 方法应该做的。但是,随着 Prism 5 更新,这不再有效。 CanExecuteChanged 事件在非 UI 线程上被调用,并且在该非 UI 线程上访问 UI 元素时我得到下游 InvalidOperationExceptions。
这是提供解决方案提示的 Prism 文档:
DelegateCommand includes support for async handlers and has been moved to the Prism.Mvvm portable class library. DelegateCommand and CompositeCommand both use the WeakEventHandlerManager to raise the CanExecuteChanged event. The WeakEventHandlerManager must be first constructed on the UI thread to properly acquire a reference to the UI thread’s 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);
}
这是异常堆栈:
Test method Calypso.Pharos.Commands.Test.PatientSessionCommandsTests.Fails threw exception: System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it. at System.Windows.Threading.Dispatcher.VerifyAccess() at System.Windows.DependencyObject.GetValue(DependencyProperty dp) at System.Windows.Controls.Primitives.ButtonBase.get_Command() at System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute() at System.Windows.Controls.Primitives.ButtonBase.OnCanExecuteChanged(Object sender, EventArgs e) at System.Windows.Input.CanExecuteChangedEventManager.HandlerSink.OnCanExecuteChanged(Object sender, EventArgs e) at Microsoft.Practices.Prism.Commands.WeakEventHandlerManager.CallHandler(Object sender, EventHandler eventHandler) at Microsoft.Practices.Prism.Commands.WeakEventHandlerManager.CallWeakReferenceHandlers(Object sender, List`1 handlers) at Microsoft.Practices.Prism.Commands.DelegateCommandBase.OnCanExecuteChanged() at Microsoft.Practices.Prism.Commands.DelegateCommandBase.RaiseCanExecuteChanged() at Calypso.Pharos.Commands.Test.PatientSessionCommandsTests.<>c__DisplayClass10.b__e() in PatientSessionCommandsTests.cs: line 71 at System.Threading.Tasks.Task.InnerInvoke() at System.Threading.Tasks.Task.Execute() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at Calypso.Pharos.Commands.Test.PatientSessionCommandsTests.d__12.MoveNext() in PatientSessionCommandsTests.cs: line 71 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
最佳答案
我不知道你是否还需要答案,但也许有人会观察到同样的错误。
所以问题是,正如您正确提到的那样,RaiseCanExecuteChanged()
方法并不总是将事件处理程序调用发布到 UI 线程的同步上下文。
如果我们看一下 WeakEventHandlerManager
实现,我们看到两件事。首先,这个静态类有一个私有(private)静态字段:
private static readonly SynchronizationContext syncContext = SynchronizationContext.Current;
其次,有一个私有(private)方法,它应该使用这个同步上下文并将事件处理程序调用实际发布到该上下文:
private static void CallHandler(object sender, EventHandler eventHandler)
{
if (eventHandler != null)
{
if (syncContext != null)
{
syncContext.Post((o) => eventHandler(sender, EventArgs.Empty), null);
}
else
{
eventHandler(sender, EventArgs.Empty);
}
}
}
所以,看起来还不错,但是...
正如我之前所说,这种调用发布“并不总是”发生。 “不总是”是指,例如,这种情况:
在这种情况下,.NET 框架优化了代码执行,现在重要的是,可以初始化静态 syncContext
在任何时候但在第一次使用之前。所以,这发生在我们的例子中——只有当你第一次调用 CallHandler()
时,这个字段才会被初始化。方法(当然是间接调用 RaiseCanExecuteChanged()
)。因为你可以从线程池调用这个方法,在这种情况下没有同步上下文,所以该字段将被设置为 null
和 CallHandler()
方法在当前线程上调用事件处理程序,而不是在 UI 线程上。
在我看来,对此的解决方案是黑客攻击或某种代码味道。反正我不喜欢。您应该只确保 CallHandler()
第一次从 UI 线程调用,例如,通过调用 RaiseCanExecuteChanged()
DelegateCommand
上的方法具有有效 CanExecuteChanged
的实例事件订阅。
希望这对您有所帮助。
关于c# - Prism 5 DelegateCommandBase.RaiseCanExecuteChanged 抛出 InvalidOperationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25751465/
我想将使用 Prism 4 编写的项目迁移到 Prism 6。 在 Prism 4 中 CompositePresentationEvent类型位于Microsoft.Practices.Compos
Pattern of pub-sub events is that the publisher should not know or care if there are any subscribers
我刚刚使用 MVVMLight、PRISM 和 DryIoc 启动了我的第一个 WPF 应用程序。 App.xaml 引用 prism:PrismApplication,如 https://prism
Microsoft 的 Patterns and Practices 提供的示例非常有用: 大约六个更简单的快速入门 其中涉及具体问题 股票交易者引用实现 ,这是一个相当全面的应用程序 但它缺乏更有用
prism 中共享服务的目的和用途是什么? 哪些事情会让我认为我必须使用共享服务而不是 Event Aggregator? 最佳答案 从事件订阅者的角度来看 EventAggregator,它有利于获
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 想改进这个问题?将问题更新为 on-topic对于堆栈溢出。 3年前关闭。 Improve this qu
寻找一个简单的 helloWorld EventAggregator 示例。我试图理解这一点,并且在遵循 RI 示例时遇到了一些困难。 谢谢 N 最佳答案 Prism 4.0 包括 EventAggr
我只是想知道区域的意义是什么。我想我不明白他们解决的问题。 例如,我看到很多人使用区域作为导航区域,但是为什么不将 ItemsControl 绑定(bind)到 ObservableCollectio
最近想将我的 WPF Prism 应用程序迁移到 7.1,这样做时我遇到了有关 Unity.Abstractions 的丢失引用错误。 将 Prism nuget 软件包升级到 Prism7.1,如下
在我的根 View 模型中,我称之为 await _navigationService.NavigateAsync( "/NavigationPage/Page1of2", useMo
在 2.1.x 时间框架中讨论了与 Prism 的集成,我们是 @2.4.x,Prism 4 现已推出,/contrib 中的 RIStockTrader 示例只是一个默认的 XAML 项目。 它应该
我正在使用 Prism4,并且在我的一个模块中,我试图用一个区域注册一个 View ,并处理它的按钮单击事件(当用户单击 View 上的按钮时发布)。 public class MyModule :
几个月来,我一直在将 Prism 2.0 用于个人项目。我最近听说过 Caliburn,我想知道是否有令人信服的理由让我考虑这样做。 我喜欢 Prism 的动态模块加载能力。我打算为我的应用程序构建模
我正在尝试对我的 Windows 应用商店应用程序中的暂停事件使用react。我添加了适当的回调方法,但遇到了问题: App.Current.Suspending += Current_Suspend
我目前正在尝试完成 this tutorial让 Prism 与 Spring.net 一起工作。 通过 NuGet(或手动引用程序集)引用 Prism4 和 Spring.Net 后,设置 Boot
几个月来,我一直在使用带有以下 XAML 命名空间声明的 Prism 6: xmlns:prism="http://www.codeplex.com/prism" 但我注意到这个命名空间 URL(重定
我在 PRISM 中实现应用程序,它需要从 dll 文件中动态导入模块。我设法做到了 - 他们正在导入,但我无法显示它。我决定创建一个特殊的模块来封装它——让我们称之为 ModuleDock。所以我们
我要创建的是一个 Silverlight 应用程序,其中包含几个选项卡/模块,这些选项卡/模块都是单独的 DLL。 我看到 PRISM 具有似乎针对 UI 的 Shell/Module 概念,并且我找
请帮忙 - 我迷路了! 我正在编写一个具有一些控件和一些屏幕的小型桌面应用程序。这应该稍后与一个小型网站集成,也有一些屏幕。这个想法是让用户编辑视频并选择图像,然后与她的 friend 分享他们的结果
我在使用方法时遇到问题 this.regionManager.RegisterViewWithRegion("TextRegion", typeof(TextView)); 如果我以某种方法在 Boo
我是一名优秀的程序员,十分优秀!