【问题标题】:Button doesn't become disabled when command CanExecute is false当命令 CanExecute 为 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 库的一部分,不是自产的。

标签: wpf mvvm command 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;
    }
}

【讨论】:

    猜你喜欢
    • 2016-07-20
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多