2009-08-27 69 views
111

我有一个Menu其中层次结构中的每个MenuItem都有Command属性设置为RoutedCommand我定义了通过其化CommandBindings重新评估“CanExecute”。相关联的CommandBinding为评估CanExecute提供了回调,该回调控制每个MenuItem的启用状态。WPF - 如何强制命令

这个差不多的作品。菜单项最初出现正确的启用和禁用状态。但是,当我的回调使用更改的数据时,我需要该命令重新请求我的回调中的结果,以便将此新状态反映到UI中。

对此,在RoutedCommandCommandBinding上似乎没有任何公开方法。

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

回答

149

不是在书的最漂亮的,但你可以使用CommandManager所有无效的CommandBinding:

CommandManager.InvalidateRequerySuggested(); 

查看MSDN

+0

感谢这工作得很好。用户界面略有延迟,但我并不担心这一点。另外,我立即投了你的答案,然后投票回去看它是否有效。现在它正在工作,我无法再重新申请投票。不知道为什么SO有这个规则。 – 2009-08-27 11:17:15

+4

我编辑了你的答案,以便重新申请我的投票。我没有改变编辑中的任何内容。再次感谢。 – 2009-08-27 11:18:14

+0

哈哈OK :)谢谢! – Arcturus 2009-08-27 11:49:02

72

更多信息对于任何人碰到这个以后谁上台;如果您碰巧使用MVVM和Prism,则Prism的DelegateCommand实现ICommand提供了一个.RaiseCanExecuteChanged()方法来执行此操作。

+10

此模式也可以在其他MVVM库中找到,例如MVVM Light。 – 2011-06-30 08:58:35

+1

与Prism不同,MVVM Light v5的源代码指示其''RaiseCanExecuteChanged()'只是调用'CommandManager.InvalidateRequerySuggested()'。 – Peter 2015-08-21 14:18:25

+3

对WPF中MVVM Light的一个注意事项,你需要使用命名空间GalaSoft.MvvmLight.CommandWpf,因为GalaSoft.MvvmLight.Command会造成麻烦http://www.mvvmlight.net/installing/changes#v5_0_2 – fuchs777 2016-01-29 12:49:26

21

我无法使用CommandManager.InvalidateRequerySuggested();,因为我正在获得性能。

我已经使用MVVM Helper的Delegating命令,它看起来像下面(我已经调整了一下我们的req)。你必须调用command.RaiseCanExecuteChanged()从VM

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); 
} 
+0

这样做效果更好为了我。谢谢。 – 2015-02-24 18:46:08

+2

只是一个FYI我注释掉了CommandManager.RequerySuggested + = value;出于某种原因,我得到了一个对我的CanExecute代码进行近乎恒定/循环的评估。否则解决方案按预期工作。谢谢! – robaudas 2015-12-28 21:04:03

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 } 
); 
4

如果您已经推出自己的类实现ICommand你可以失去很多逼着你去依靠手工清爽超过应所需的自动状态更新。它也可以打破InvalidateRequerySuggested()。问题是一个简单的ICommand实现无法将新命令链接到CommandManager

的解决方案是使用以下命令:

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

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

这样用户连接到CommandManager,而不是你的类可以适当参与指挥状态的变化。

+0

直截了当,让人们可以控制其ICommand实现。 – 2017-05-22 19:56:38