如何防止CommandManager在每次UI交互时调用CanExecute?

3
我们的 UI 目前有很多控件,这些控件都绑定了一些复杂的 CanExecute 命令。我们面临的问题是,每当 CommandManager 确定需要重新评估 UI 时,所有命令都会运行 CanExecute,这反过来又导致特定情况下的性能损失。
阅读此帖子:CommandManager.RequerySuggested 如何工作? 看起来,CommandManager 将在简单的按键按下、鼠标移动事件等上重新评估。有没有办法防止这种情况发生,而是在手动调用时重新评估命令管理器?

2
我认为你不能这样做。CanExecute 不应包含任何复杂的功能。也许你可以创建一个布尔变量来告诉你它是否可以执行,并在条件改变时更改此变量? - vesan
你会在什么时候/地方调用它? - Dzyann
@Dzyann,一个好的场景是当特定事件完成运行(或被触发)时,让所有命令“重新评估”。 - d.moncada
我会保留现有的一切,并在导致问题的特定位置添加额外的验证。请考虑到将来更改CommandManager的默认行为可能会引起各种问题。一个不知情的程序员在某些地方添加逻辑并永远不被解雇。我想只有你代码的某些部分会导致性能问题。 - Dzyann
1个回答

1

一个解决方案可能是实现一个更简单的RelayCommand类,它仅存储事件处理程序本身并公开一个公共方法,在适当时触发它们:

public class RelayCommand : ICommand
{
    public event EventHandler CanExecuteChanged;

    // Further ICommand implementation omitted...

    public void Invalidate()
    {
        var handler = this.CanExecuteChanged;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

您可以在viewModel中调用以下内容重新评估命令:
fooCommand.Invalidate();

当然,这会带来相反的问题,你现在必须手动重新评估所有命令...
编辑
为了解释这些评论,大多数RelayCommand像这样实现CanExecuteChanged事件:
public event EventHandler CanExecuteChanged
{
    add { CommandManager.RequerySuggested += value; }
    remove { CommandManager.RequerySuggested -= value; }
}

当UI订阅命令的CanExecuteChanged事件时,实际上是间接订阅了CommandManager.RequerySuggested事件,这就是为什么您的CanExecute方法每次CommandManager建议重新查询时都会被调用。
我建议使用更简单的RelayCommand来避免这个问题,它不会订阅CommandManager.RequerySuggested事件。

Ben,我可以看出这将提供手动调用机制,但我不明白这如何防止CommandManager根据UI交互自动调用。 - d.moncada
它不会停止CommandManager,但这并不重要。简化的RelayCommand不会将任何已注册的事件处理程序传递到CommandManager,这意味着每次触发RequerySuggested事件时,UI都不会重新评估命令。 - Benjamin Gale
Ben,这个提供了解决我的问题的机制。谢谢! - d.moncada

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接