在PropertyChanged之后ICommand CanExecute不会触发?

时间:2013-01-23 12:04:25

标签: c# wpf mvvm-light

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

<Button Command="{Binding Path=TestrunStartCommand}" Content="GO!">

命令的定义如下:

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

public bool IsTestrunInProgress
{
    get{
        return _isTestrunInProgress;
    }
    set{
        _isTestrunInProgress = value;
        RaisePropertyChanged(IsTestrunInProgressPropertyName);
    }
}   

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

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

进一步阅读: wpf command pattern - when does it query canexecute

4 个答案:

答案 0 :(得分:48)

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

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

RelayCommand的某些实现使用CommandManager.RequerySuggested,在这种情况下,您需要调用CommandManager.InvalidateRequerySuggested()来强制UI刷新。

长话短说,您需要从属性设置器中调用其中一种方法。

<强>更新

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

更新2

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

答案 1 :(得分:44)

这是非常重要且容易错过的,我正在重复@Samir在评论中所说的话。 Laurent Bugnion先生在他的blog中写道:

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

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

的行
using GalaSoft.MvvmLight.Command;

使用:

using GalaSoft.MvvmLight.CommandWpf;

答案 2 :(得分:4)

您可以尝试使用CommandManager.InvalidateRequerySuggested

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

在你的情况下像

IsEnabled={Binding IsTestrunInProgress}

答案 3 :(得分:1)

问题是,无论何时访问,ICommand Property TestrunStartCommand都会返回一个新的命令对象。

一个简单的解决方法是创建一次ICommand对象并一次又一次地使用它。

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

这是一个非常简单的修复,它对我有用。