在PropertyChanged之后ICommand CanExecute不会触发?

问题描述 投票:37回答:4

我有一个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

c# wpf mvvm-light
4个回答
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方法。


43
投票

这是非常重要且容易错过,我在重复@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;

4
投票

你可以试试CommandManager.InvalidateRequerySuggested

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

在你的情况下像

IsEnabled={Binding IsTestrunInProgress}

1
投票

问题是,ICommand Property TestrunStartCommand在访问时始终返回一个新的命令对象。

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.