WPF - 如何强制命令通过其 CommandBindings 重新评估“CanExecute”
Posted
技术标签:
【中文标题】WPF - 如何强制命令通过其 CommandBindings 重新评估“CanExecute”【英文标题】:WPF - How to force a Command to re-evaluate 'CanExecute' via its CommandBindings 【发布时间】:2010-11-23 08:12:28 【问题描述】:我有一个Menu
,其中层次结构中的每个MenuItem
都将其Command
属性设置为我定义的RoutedCommand
。关联的CommandBinding
为CanExecute
的评估提供回调,控制每个MenuItem
的启用状态。
这几乎有效。菜单项最初带有正确的启用和禁用状态。但是,当我的 CanExecute
回调使用的数据发生变化时,我需要该命令从我的回调中重新请求结果,以便在 UI 中反映这个新状态。
RoutedCommand
或 CommandBinding
上似乎没有任何公共方法。
请注意,当我单击或输入控件时再次使用回调(我猜它是在输入时触发的,因为鼠标悬停不会导致刷新)。
【问题讨论】:
【参考方案1】:不是书中最漂亮的,但您可以使用 CommandManager 使所有命令绑定无效:
CommandManager.InvalidateRequerySuggested();
在MSDN上查看更多信息
【讨论】:
谢谢这工作得很好。 UI 有一点延迟,但我并不太担心。另外,我立即对您的答案进行了投票,然后将投票退回以查看它是否有效。现在它正在工作,我不能再次重新申请投票。不知道为什么 SO 有这个规则。 我编辑了您的答案以便重新申请我的投票。我没有在编辑中更改任何内容。再次感谢。 当我从代码隐藏中更改 Texbox 的内容时,我遇到了同样的问题。如果您手动编辑它会起作用。在这个应用程序中,他们让 texbox 被一个会弹出的控件编辑,当您保存弹出窗口时,它会更改 Texbox.Text 属性。这解决了问题!谢谢@Arcturus 请注意其他答案 (***.com/questions/783104/refresh-wpf-command) “它必须在 UI 线程上调用”【参考方案2】:对于以后遇到此问题的任何人;如果您碰巧在使用 MVVM 和 Prism,那么 Prism 的 DelegateCommand
实现 ICommand
提供了一个 .RaiseCanExecuteChanged()
方法来执行此操作。
【讨论】:
这种模式也可以在其他 MVVM 库中找到,例如MVVM 灯。 与 Prism 不同,MVVM Light v5 的源代码表明它的RaiseCanExecuteChanged()
只是调用CommandManager.InvalidateRequerySuggested()
。
WPF中MVVM Light的旁注,你需要使用命名空间GalaSoft.MvvmLight.CommandWpf,因为GalaSoft.MvvmLight.Command会导致麻烦mvvmlight.net/installing/changes#v5_0_2
((RelayCommand)MyCommand).RaiseCanExecuteChanged();
为我工作,使用 GalaSoft.MvvmLight.Command - 但是在更改为 CommandWPF
后,它无需调用任何东西即可工作。谢谢@fuchs777
如果您不使用第 3 方库怎么办?【参考方案3】:
我无法使用CommandManager.InvalidateRequerySuggested();
,因为我的性能受到了影响。
我使用了MVVM Helper 的委派命令,如下所示(我为我们的需求做了一些调整)。您必须从 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);
【讨论】:
仅供参考,我注释掉了 CommandManager.RequerySuggested += value;由于某种原因,我对我的 CanExecute 代码进行了近乎恒定/循环的评估。否则,解决方案按预期工作。谢谢!【参考方案4】:我已经实现了一个处理命令属性依赖的解决方案,这里是链接https://***.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
);
【讨论】:
【参考方案5】:如果您已经推出了自己的实现ICommand
的类,您可能会失去很多自动状态更新,迫使您依赖手动刷新而不是需要。它也可以破坏InvalidateRequerySuggested()
。问题是简单的ICommand
实现无法将新命令链接到CommandManager
。
解决方案是使用以下方法:
public event EventHandler CanExecuteChanged
add CommandManager.RequerySuggested += value;
remove CommandManager.RequerySuggested -= value;
public void RaiseCanExecuteChanged()
CommandManager.InvalidateRequerySuggested();
这样订阅者将附加到CommandManager
而不是您的班级,并且可以正确参与命令状态更改。
【讨论】:
直截了当,让人们能够控制他们的 ICommand 实现。【参考方案6】:这对我有用:将 CanExecute 放在 XAML 中的命令之前。
【讨论】:
以上是关于WPF - 如何强制命令通过其 CommandBindings 重新评估“CanExecute”的主要内容,如果未能解决你的问题,请参考以下文章
如何在 WindowStyle="None" 的 WPF 窗口中强制执行 MinWidth 和 MinHeight?