为什么人们在ICommands上使用CommandManager.InvalidateRequerySuggested()?

时间:2016-11-14 10:18:00

标签: c# wpf

我正在制作一些我自己的自定义ICommand实现,我看到很多实现都是这样的:

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

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

据我所知,这是优化不佳的代码,因为调用RaiseCanExecuteChanged()将触发UI中的所有命令以检查其ICommand.CanExecute状态,而通常我们只需要其中一个来验证它。

我想我读过一次这是一些WPF ICommands的主要代码,如RoutedCommand,对他们来说这是有道理的,因为他们想要在一些控件失去焦点和类似的事情后自动重新验证所有ICommands,但仍然我不明白为什么人们为自己的ICommand实施重复这种模式。

我想到的代码是一个简单的事件调用,例如:

public event EventHandler CanExecuteChanged;

protected void RaiseCanExecuteChanged()
{        
    CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

我测试了这个,所以为什么网络上的所有示例都不能实现像这样简单的事情?我错过了什么吗?

我已经阅读了常规事件中使用强引用的内存泄漏问题,其中CommandManager仅使用WeakReferences,这在视图是垃圾收集的情况下很好,但仍然是 aren& #39;是否有任何解决方案不会影响性能超过内存占用量?

4 个答案:

答案 0 :(得分:2)

  

为什么网络上的所有示例都不能实现像这样简单的事情?我错过了什么吗?

我猜测它主要是由于懒惰......你提出的建议确实是一个更好(更有效)的实现。但是,它还没有完成:您仍然需要订阅CommandManager.RequerySuggested来提升CanExecuteChanged命令。

答案 1 :(得分:2)

非常简单 - 如果您在ICommand.CanExecute()中执行繁重的工作,那么您使用Commands非常糟糕。如果您遵循该规则,实际上应该没有严重的性能影响来调用CommandManager.InvalidateRequerySuggested()

务实地说,这比你建议的更容易实现。

就个人而言,当某个属性发生变化时,我宁愿在特定的CommandManager.InvalidateRequerySuggested()中调用ViewModel,以便向用户提供即时反馈(即一旦表单完成/有效就启用按钮)。

答案 2 :(得分:1)

这是对this answer的回答。确实,<script src="https://unpkg.com/@observablehq/stdlib@3"></script> <script> const {DOM} = new observablehq.Library; console.log(DOM.uid); </script> 必须由主UI线程调用。

只需将其编写如下:

CanExecuteChanged?.Invoke(this, null);

这解决了您的问题,您可以只查询一个命令。但是,确实应该使public void RaiseCanExecuteChanged() { Application.Current.Dispatcher.Invoke(() => CanExecuteChanged?.Invoke(this, null)); } -Method尽可能快,因为它总是会定期执行。 最好让CanExecute仅包含一个CanExecute,其中return foo;是您可以在调用foo之前设置的字段。

答案 3 :(得分:0)

这个问题已经很老了,现在是2019年,但是我发现了另一个使用CommandManager.InvalidateRequerySuggested()的原因。

我为WPF应用程序编写了自己的自定义ICommand类,在该类中,我首先像这样直接调用了CanExecuteChanged。

public void RaiseCanExecuteChanged()
{
    CanExecuteChanged?.Invoke(this, null); 
}

我的WPF应用程序大量使用不同的线程,并且当从另一个线程然后从主UI线程中调用上述方法时,它不会引发任何错误,只是被忽略了。更糟糕的是,当我发现调用方法中的所有代码行都被跳过时,导致了奇怪的结果。

我不清楚,但我想原因是CanExecuteChanged导致了UI的更改,不能从其他线程更改。

但是-当我将ICommand更改为CommandManager.InvalidateRequerySuggested()时,再也没有问题了。似乎可以从任何线程调用CommandManager.InvalidateRequerySuggested(),并且UI仍会更新。

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

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

我认为这可能是一个有价值的答案,因为在使用此解决方案之前,我已经调试了3个小时,对这个问题进行了调试。发现此问题的问题是,调试时没有引发任何错误。该代码只是被跳过。行为很奇怪。