当命令 CanExecute 为 false 时,按钮不会被禁用

Posted

技术标签:

【中文标题】当命令 CanExecute 为 false 时,按钮不会被禁用【英文标题】:Button doesn't become disabled when command CanExecute is false 【发布时间】:2013-02-08 13:45:22 【问题描述】:

我有一个简单的窗口,带有一个按钮,通过命令绑定到 ViewModel。

如果 MyCommand.CanExecute() 为 false,我希望按钮被禁用。但似乎 WPF 只会在第一次绘制窗口时设置 IsEnabled 属性。任何后续操作都不会影响按钮的可见状态。我正在使用 Prism 的 DelegateCommand。

我的观点:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Button Content="Click Here" Command="Binding MyCommand" Width="100" Height="50"/>
</Grid>

还有我的 ViewModel:

public class MyVM : NotificationObject

    public MyVM()
    
        _myCommand = new DelegateCommand(DoStuff, CanDoStuff);
    

    private void DoStuff()
    
        Console.WriteLine("Command Executed");
        _myCommand.RaiseCanExecuteChanged();
    

    private bool CanDoStuff()
    
        var result =  DateTime.Now.Second % 2 == 0;
        Console.WriteLine("CanExecute is 0", result);
        return result;
    

    private DelegateCommand _myCommand;

    public ICommand MyCommand
    
        get
        
            return _myCommand;
        
    

50% 的时间,当我的应用程序加载时,该按钮被正确禁用。但是,如果它在窗口加载时启用,并且我单击按钮执行命令,我预计 50% 的时间按钮会被禁用,但它永远不会。该命令不执行,但我仍然可以单击该按钮。如何让 WPF 了解当 CanExecute() 为 false 时应禁用该按钮?

【问题讨论】:

您需要做的第一件事是打开数据绑定的调试消息:i.stack.imgur.com/MF8i5.png 接下来,重新运行并检查输出窗口,看看有什么错误。如果没有与您的命令绑定相关,那么您的 RaiseCanExecuteChanged() 实现不正确/有问题。 你的 CanDOStuff 方法真的很奇怪!它可能会使您的按钮被禁用,但在下一秒您的命令可以执行,但按钮被禁用......真的很奇怪。但如果您的 CanExecute 可能会更改并且 UI 未更新,因为 CanExecuteChanged 未引发,您应该致电 CommandManager.InvalidateRequerySuggested()。 @Viktor 我知道这很奇怪,它应该是一个随机返回真/假的愚蠢示例。 CommandManager.InvalidateRequerySuggested 无效。 @Will 输出中没有错误,RaiseCanExecuteChanged 是 Microsoft Prism 库的一部分,不是自产的。 【参考方案1】:

我看到您正在使用 Prism 及其 NotificationObjectDelegateCommand,因此我们应该期望 RaiseCanExecuteChanged() 中不会出现错误。

但是,该行为的原因是 Prism 的 RaiseCanExecuteChanged 同步运行,因此在我们仍在 ICommand.Execute() 的实现中时调用了 CanDoStuff(),然后结果似乎被忽略了。

如果您使用自己的命令创建另一个按钮并从该命令/按钮调用_myCommand.RaiseCanExecuteChanged(),则第一个按钮将按照您的预期启用/禁用。

或者,如果您使用 MVVM Light 和 RelayCommand 尝试相同的操作,您的代码将工作,因为 MVVM Light 的 RaiseCanExecuteChanged 调用 CommandManager.InvalidateRequerySuggested(),它使用 Dispatcher.CurrentDispatcher.BeginInvoke 异步调用对 CanDoStuff 的回调,避免了您的行为看看 Prism 的实现。

【讨论】:

感谢您的解释。我尝试了您的两个建议,并且它们都是正确的。似乎是 Prism 的不幸限制。【参考方案2】:

你可以试试这个(Microsoft.Practices.Prism.dll是必须的)

public class ViewModel

    public DelegateCommand ExportCommand  get; 

    public ViewModel()
    
        ExportCommand = new DelegateCommand(Export, CanDoExptor);
    

    private void Export()
    
        //logic
    

    private bool _isCanDoExportChecked;

    public bool IsCanDoExportChecked
    
        get  return _isCanDoExportChecked; 
        set
        
            if (_isCanDoExportChecked == value) return;

            _isCanDoExportChecked = value;
            ExportCommand.RaiseCanExecuteChanged();
        
    

    private bool CanDoExptor()
    
        return IsCanDoExportChecked;
    

【讨论】:

以上是关于当命令 CanExecute 为 false 时,按钮不会被禁用的主要内容,如果未能解决你的问题,请参考以下文章

WPF - 如果命令的 CanExecute 为假,如何隐藏菜单项?

WPF - 如何强制命令通过其 CommandBindings 重新评估“CanExecute”

WPF MVVM Command CanExecute,仅在焦点更改时重新评估

Prism DelegateCommand 中的 CanExecute 不起作用

如何在 WPF 中使用 ICommand 中的 CanExecute 方法

WPF 的命令的自动刷新时机——当你 CanExecute 会返回 true 但命令依旧不可用时可能是这些原因