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


130

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

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

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

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

Answers:


172

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

CommandManager.InvalidateRequerySuggested();

MSDN上查看更多信息


1
谢谢,这很好。用户界面略有延迟,但是我对此并不担心。另外,我立即投票赞成您的答案,然后重新投票以查看是否有效。现在它可以正常工作了,我无法再次重新投票。不知道为什么SO有该规则。
Drew Noakes

5
我修改了您的答案,以便重新申请投票。我没有在编辑中进行任何更改。再次感谢。
德鲁·诺阿克斯

当我从背后的代码更改Texbox的内容时,我遇到了同样的问题。如果您手动编辑它会起作用。在此应用程序中,他们的texbox由将弹出的控件编辑,当您保存弹出窗口时,它将更改Texbox.Text属性。这样就解决了问题!感谢@Arcturus
Dzyann

10
请注意其他答案(stackoverflow.com/questions/783104/refresh-wpf-command)上的“必须在UI线程上调用”
Samvel Siradeghyan,2016年

84

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


12
该模式也可以在其他MVVM库中找到,例如MVVM Light。
Peter Lillevold 2011年

2
与Prism不同,MVVM Light v5的源代码指示其RaiseCanExecuteChanged() 简单调用CommandManager.InvalidateRequerySuggested()
彼得

4
WPF中MVVM Light的旁注,您需要使用命名空间GalaSoft.MvvmLight.CommandWpf,因为GalaSoft.MvvmLight.Command会引起麻烦mvvmlight.net/installing/changes#v5_0_2
fuchs777 '16

((RelayCommand)MyCommand).RaiseCanExecuteChanged();为我工作,使用GalaSoft.MvvmLight.Command-更改为CommandWPF,但不需要调用任何东西即可工作。感谢@ fuchs777
罗宾·贝内特

1
如果您不使用第三方库怎么办?
维达尔

28

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

我已经使用了MVVM Helper的Delegating命令,如下所示(我对我们的要求做了一些调整)。你必须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);
}

3
仅供参考,我注释掉CommandManager.RequerySuggested + =值;由于某种原因,我对CanExecute代码的评价接近恒定/循环。否则,解决方案将按预期工作。谢谢!
robaudas

15

如果您自己开发了自己的实现类,则ICommand可能会丢失很多自动状态更新,从而使您不得不依靠手动刷新,而不是需要的更多时间。它也可能破裂InvalidateRequerySuggested()。问题是简单的ICommand实现无法将新命令链接到CommandManager

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

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void RaiseCanExecuteChanged()
    {
        CommandManager.InvalidateRequerySuggested();
    }

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


2
直截了当,并允许人们控制其ICommand实现。
Akoi Meexx

2

我已经实现了一种解决方案来处理命令的属性依赖关系,此处链接为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 }
 );

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.