【问题标题】:WPF - How to force a Command to re-evaluate 'CanExecute' via its CommandBindingsWPF - 如何强制命令通过其 CommandBindings 重新评估“CanExecute”
【发布时间】:2023-03-25 13:02:01
【问题描述】:

我有一个Menu,其中层次结构中的每个MenuItem 都将其Command 属性设置为我定义的RoutedCommand。关联的CommandBindingCanExecute 的评估提供回调,控制每个MenuItem 的启用状态。

几乎有效。菜单项最初带有正确的启用和禁用状态。但是,当我的 CanExecute 回调使用的数据发生变化时,我需要该命令从我的回调中重新请求结果,以便在 UI 中反映这个新状态。

RoutedCommandCommandBinding 上似乎没有任何公共方法。

请注意,当我单击或输入控件时再次使用回调(我猜它是在输入时触发的,因为鼠标悬停不会导致刷新)。

【问题讨论】:

    标签: .net wpf command commandbinding


    【解决方案1】:

    不是书中最漂亮的,但您可以使用 CommandManager 使所有命令绑定无效:

    CommandManager.InvalidateRequerySuggested();
    

    MSDN上查看更多信息

    【讨论】:

    • 谢谢这工作得很好。 UI 有一点延迟,但我并不太担心。另外,我立即对您的答案进行了投票,然后将投票退回以查看它是否有效。现在它正在工作,我不能再次重新申请投票。不知道为什么 SO 有这个规则。
    • 我编辑了您的答案以便重新申请我的投票。我没有在编辑中更改任何内容。再次感谢。
    • 当我从代码隐藏中更改 Texbox 的内容时,我遇到了同样的问题。如果您手动编辑它会起作用。在这个应用程序中,他们让 texbox 被一个会弹出的控件编辑,当您保存弹出窗口时,它会更改 Texbox.Text 属性。这解决了问题!谢谢@Arcturus
    • 请注意其他答案 (stackoverflow.com/questions/783104/refresh-wpf-command) “它必须在 UI 线程上调用”
    【解决方案2】:

    对于以后遇到此问题的任何人;如果您碰巧在使用 MVVM 和 Prism,那么 Prism 的 DelegateCommand 实现 ICommand 提供了一个 .RaiseCanExecuteChanged() 方法来执行此操作。

    【讨论】:

    • 这种模式也可以在其他 MVVM 库中找到,例如MVVM 灯。
    • 与 Prism 不同,MVVM Light v5 的源代码表明它的RaiseCanExecuteChanged() 只是调用CommandManager.InvalidateRequerySuggested()
    • WPF中MVVM Light的旁注,你需要使用命名空间GalaSoft.MvvmLight.CommandWpf,因为GalaSoft.MvvmLight.Command会导致麻烦mvvmlight.net/installing/changes#v5_0_2
    • ((RelayCommand)MyCommand).RaiseCanExecuteChanged(); 为我工作,使用 GalaSoft.MvvmLight.Command - 但是在更改为 CommandWPF 后,它无需调用任何东西即可工作。谢谢@fuchs777
    • 如果您不使用第 3 方库怎么办?
    【解决方案3】:

    我无法使用CommandManager.InvalidateRequerySuggested();,因为我的性能受到了影响。

    我使用了MVVM Helper 的委派命令,如下所示(我为我们的需求做了一些调整)。您必须从 VM 调用 command.RaiseCanExecuteChanged()

    public event EventHandler CanExecuteChanged
    {
        add
        {
            _internalCanExecuteChanged += value;
            CommandManager.RequerySuggested += value;
        }
        remove
        {
            _internalCanExecuteChanged -= value;
            CommandManager.RequerySuggested -= value;
        }
    }
    
    /// <summary>
    /// This method can be used to raise the CanExecuteChanged handler.
    /// This will force WPF to re-query the status of this command directly.
    /// </summary>
    public void RaiseCanExecuteChanged()
    {
        if (canExecute != null)
            OnCanExecuteChanged();
    }
    
    /// <summary>
    /// This method is used to walk the delegate chain and well WPF that
    /// our command execution status has changed.
    /// </summary>
    protected virtual void OnCanExecuteChanged()
    {
        EventHandler eCanExecuteChanged = _internalCanExecuteChanged;
        if (eCanExecuteChanged != null)
            eCanExecuteChanged(this, EventArgs.Empty);
    }
    

    【讨论】:

    • 仅供参考,我注释掉了 CommandManager.RequerySuggested += value;由于某种原因,我对我的 CanExecute 代码进行了近乎恒定/循环的评估。否则,解决方案按预期工作。谢谢!
    【解决方案4】:

    我已经实现了一个处理命令属性依赖的解决方案,这里是链接https://stackoverflow.com/a/30394333/1716620

    多亏了你最终会得到这样的命令:

    this.SaveCommand = new MyDelegateCommand<MyViewModel>(this,
        //execute
        () => {
          Console.Write("EXECUTED");
        },
        //can execute
        () => {
          Console.Write("Checking Validity");
           return PropertyX!=null && PropertyY!=null && PropertyY.Length < 5;
        },
        //properties to watch
        (p) => new { p.PropertyX, p.PropertyY }
     );
    

    【讨论】:

      【解决方案5】:

      如果您已经推出了自己的实现ICommand 的类,您可能会失去很多自动状态更新,迫使您依赖手动刷新而不是需要。它也可以破坏InvalidateRequerySuggested()。问题是简单的ICommand 实现无法将新命令链接到CommandManager

      解决方案是使用以下方法:

          public event EventHandler CanExecuteChanged
          {
              add { CommandManager.RequerySuggested += value; }
              remove { CommandManager.RequerySuggested -= value; }
          }
      
          public void RaiseCanExecuteChanged()
          {
              CommandManager.InvalidateRequerySuggested();
          }
      

      这样订阅者将附加到CommandManager 而不是您的班级,并且可以正确参与命令状态更改。

      【讨论】:

      • 直截了当,让人们能够控制他们的 ICommand 实现。
      【解决方案6】:

      这对我有用:将 CanExecute 放在 XAML 中的命令之前。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-28
        • 2013-08-15
        • 2015-09-22
        • 1970-01-01
        • 2017-04-07
        • 1970-01-01
        • 2014-12-29
        • 1970-01-01
        相关资源
        最近更新 更多