在PropertyChanged之后ICommand CanExecute不会触发?

nab*_*lke 40 c# wpf mvvm-light

我有一个WPF应用程序,它显示一个绑定到命令的按钮:

<Button Command="{Binding Path=TestrunStartCommand}" Content="GO!">
Run Code Online (Sandbox Code Playgroud)

该命令定义如下:

public ICommand TestrunStartCommand
{
    get { return new RelayCommand(TestrunStartExecute, () => !IsTestrunInProgress); }
}

public bool IsTestrunInProgress
{
    get{
        return _isTestrunInProgress;
    }
    set{
        _isTestrunInProgress = value;
        RaisePropertyChanged(IsTestrunInProgressPropertyName);
    }
}   
Run Code Online (Sandbox Code Playgroud)

问题是,我设置IsTestrunInProgress为false 后不会立即启用该按钮,但只有在我单击应用程序窗口后才会启用.

你能帮我理解这个行为并告诉我如何解决这个问题吗?

进一步阅读: wpf命令模式 - 何时查询canexecute

Hel*_*iac 50

这是非常重要且容易错过,我在重复@Samir在评论中所说的内容.Laurent Bugnion先生在他的博客中写道:

但是,在WPF 4和WPF 4.5中,有一个问题:在将MVVM Light升级到V5后,CommandManager将停止工作.您将观察到当RelayCommand的CanExecute委托返回false时,您的UI元素(按钮等)将停止被禁用/启用.

如果你赶时间,这里有修复:在使用RelayCommand的任何类中,替换行说:

using GalaSoft.MvvmLight.Command;
Run Code Online (Sandbox Code Playgroud)

有:

using GalaSoft.MvvmLight.CommandWpf;
Run Code Online (Sandbox Code Playgroud)

  • 哦,好主啊。现在是 2020 年,距离最初的答案已经过去 5 年了,我刚刚花了两个小时的大部分时间试图弄清楚为什么这在一个 View/ViewModel 中不起作用,但在另一个 View/ViewModel 中却起作用......两个命令代码都是完全相同...除了一个使用的是“GalaSoft.MvvmLight.Command”而不是“GalaSoft.MvvmLight.CommandWpf”...天啊,这太令人沮丧了。 (3认同)

Luk*_*oid 48

ICommand接口公开了一个事件ICommand.CanExecuteChanged,该事件用于通知UI何时重新确定IsEnabled命令驱动的UI组件的状态.

根据RelayCommand您使用的实施情况,您可能需要提出此事件; 许多实现都公开了一种方法RelayCommand.RaiseCanExecuteChanged(),您可以调用该方法来强制UI刷新.

在一些实现RelayCommand化妆用的 CommandManager.RequerySuggested,在这种情况下,你将需要调用CommandManager.InvalidateRequerySuggested()迫使UI刷新.

简而言之,您需要从属性设置器中调用其中一种方法.

更新

由于当活动焦点正在改变时确定按钮的状态,我相信CommandManager正在使用.因此,在属性的setter中,在分配后备字段后,调用CommandManager.InvalidateRequerySuggested().

更新2

RelayCommand实施是从MVVM光工具包.从WPF/.NET使用时,实现将包装从中公开的方法和事件CommandManager.这意味着这些命令在大多数情况下(UI被更改,或者焦点元素被更改)自动运行.但在少数情况下,例如这个,你需要手动强制命令重新查询.使用这个库执行此操作的正确方法是调用该RaiseCanExecuteChanged()方法RelayCommand.

  • 如果您使用的是WPF 4.5或更高版本,则需要使用命名空间GalaSoft.MvvmLight.CommandWpf中的RelayCommand而不是GalaSoft.MvvmLight.Command.这将重新启用RelayCommand以使用CommandManager. (18认同)
  • 我注意到的另一件重要的事情是,如果您从非UI线程设置(导致更改)属性,则需要通过Dispatcher调用上述方法。否则,WPF只会吞下该通知。 (3认同)
  • 您说得对:我的`RelayCommand`中有`RaiseCanExecuteChanged`方法。但是我不明白的是,我有很多可用的按钮,并且我从不手动引发此事件,并且某种程度上它就像是在魔术般起作用。在这种情况下有什么不同?我认为这个谜题仍然缺少一些东西。 (2认同)

Kla*_*s78 5

您可以尝试使用CommandManager.InvalidateRequerySuggested

不管怎样,这在过去有时对我没有帮助。对我来说,最好的解决方案是将布尔属性绑定到Button.IsEnabled依赖属性。

在你的情况下类似

IsEnabled={Binding IsTestrunInProgress}
Run Code Online (Sandbox Code Playgroud)