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

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

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

RoutedCommandCommandBinding上似乎没有任何用于此目的的公共方法。

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

92093 次浏览

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

CommandManager.InvalidateRequerySuggested();

请参阅 MSDN的更多信息

对于稍后遇到这个问题的任何人,如果您正好使用 MVVM 和 Prism,那么 Prism 的 ICommandDelegateCommand实现提供了一个 .RaiseCanExecuteChanged()方法来完成这个任务。

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

我使用了 MVVM 辅助工具的“委派”命令,如下所示(为了满足我们的要求,我稍微调整了一下)。你必须从 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);
}

我已经实现了一个解决方案来处理对命令的属性依赖,这里是链接 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 }
);

如果您已经使用了自己的实现 ICommand的类,那么您可能会丢失大量的自动状态更新,从而迫使您过多地依赖手动刷新。它也可以破坏 InvalidateRequerySuggested()。问题是,一个简单的 ICommand实现无法将新命令链接到 CommandManager

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

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


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

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

这对我来说很有用: 在 XAML 中将 CanExecute 置于 Command 之前。