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

Posted

技术标签:

【中文标题】WPF - 如何强制命令通过其 CommandBindings 重新评估“CanExecute”【英文标题】:WPF - How to force a Command to re-evaluate 'CanExecute' via its CommandBindings 【发布时间】:2010-11-23 08:12:28 【问题描述】:

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

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

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

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

【问题讨论】:

【参考方案1】:

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

CommandManager.InvalidateRequerySuggested();

在MSDN上查看更多信息

【讨论】:

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

【讨论】:

以上是关于WPF - 如何强制命令通过其 CommandBindings 重新评估“CanExecute”的主要内容,如果未能解决你的问题,请参考以下文章

如何强制WPF启动窗口到特定的屏幕?

如何在 WindowStyle="None" 的 WPF 窗口中强制执行 MinWidth 和 MinHeight?

如何强制 WPF 绑定刷新?

如何通过 MVVM 在 WPF WebBrowser 控件上使用 Javascript

窗口最小化时如何强制刷新缩略图? (C#WPF)

如何强制 WPF 应用程序以管理员模式运行