c# – 即使设置了CommandParameter,ICommand.CanExecute也会传递null

我有一个棘手的问题,我将ContextMenu绑定到一组ICommand派生的对象,并通过样式在每个MenuItem上设置Command和CommandParameter属性
<ContextMenu
    ItemsSource="{Binding Source={x:Static OrangeNote:Note.MultiCommands}}">
    <ContextMenu.Resources>
        <Style
            targettype="MenuItem">
            <Setter
                Property="Header"
                Value="{Binding Path=Title}" />
            <Setter
                Property="Command"
                Value="{Binding}" />
            <Setter
                Property="CommandParameter"
                Value="{Binding Source={x:Static OrangeNote:App.Screen},Path=Selectednotes}" />
...

但是,当ICommand.Execute(object)按原样传递选定的注释集时,ICommand.CanExecute(object)(在创建菜单调用)将被传递为null.我已经检查过并且在调用之前正确地实例化了所选的注释集合(实际上它在其声明中被赋值,因此它永远不会为null).我无法弄清楚CanEvaluate为何被传递为null.

解决方法

我已经确定ContextMenu中至少有两个错误会导致其CanExecute调用在不同情况下不可靠.它在设置命令时立即调用CanExecute.后来的电话是不可预测的,当然不可靠.

我花了整整一夜的时间试图找出失败的确切条件并寻找解决方法.最后我放弃并切换到触发了所需命令的Click处理程序.

我确定我的一个问题是更改ContextMenu的DataContext可能导致在绑定新的Command或CommandParameter之前调用CanExecute.

我知道这个问题的最佳解决方案是使用自己的Command和CommandBinding附加属性,而不是使用内置的属性

>设置附加的Command属性后,订阅MenuItem上的Click和DataContextChanged事件,并订阅CommandManager.RequerySuggested.
>当DataContext更改,RequerySuggested进入,或者您的两个附加属性发生更改时,使用dispatcher.BeginInvoke计划调度程序操作,该调用调用CanExecute()并更新MenuItem上的IsEnabled.
>当Click事件触发时,执行CanExecute事件,如果它通过,则调用Execute().

用法就像常规的Command和CommandParameter一样,但使用附加的属性

<Setter Property="my:ContexrMenuFixer.Command" Value="{Binding}" />
<Setter Property="my:ContextMenuFixer.CommandParameter" Value="{Binding Source=... }" />

解决方案可以解决ContextMenu的CanExecute处理中的错误所带来的所有问题.

希望有一天微软将解决ContextMenu的问题,这种解决方法将不再是必要的.我有一个备案例坐在这里,我打算提交给Connect.也许我应该接受球并且实际上做到了.

什么是RequerySuggested,为什么要使用它?

RequerySuggested机制是RoutedCommand有效处理ICommand.CanExecuteChanged的方法.在非RoutedCommand世界中,每个ICommand都有自己的CanExecuteChanged订户列表,但对于RoutedCommand,任何订阅ICommand.CanExecuteChanged的客户端实际上都会订阅CommandManager.RequerySuggested.这个更简单的模型意味着只要RoutedCommand的CanExecute可能发生变化,所有必要的就是调用CommandManager.InvalidateRequerySuggested(),它将执行与触发ICommand.CanExecuteChanged相同的操作,但同时为所有RoutedCommands和后台线程执行此操作.此外,将RequerySuggested调用组合在一起,以便在发生许多更改时,只需要调用一次CanExecute.

我建议您订阅CommandManager.RequerySuggested而不是ICommand.CanExecuteChanged的原因是:1.每次Command附加属性的值更改时,您不需要代码删除订阅添加订阅,以及2. CommandManager.RequerySuggested内置了一个弱引用功能,允许您设置事件处理程序并仍然是垃圾回收.对ICommand执行相同操作需要您实现自己的弱引用机制.

另一方面,如果您订阅CommandManager.RequerySuggested而不是ICommand.CanExecuteChanged,那么您将只获得RoutedCommands的更新.我只使用RoutedCommands这对我来说不是问题,但我应该提到,如果你经常使用常规ICommands,你应该考虑做一些弱订阅ICommand.CanExecutedChanged的额外工作.请注意,如果这样做,您也不需要订阅RequerySuggested,因为RoutedCommand.add_CanExecutedChanged已经为您执行此操作.

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...