DelegateCommand 的 CanExecute 逻辑

Posted

技术标签:

【中文标题】DelegateCommand 的 CanExecute 逻辑【英文标题】:CanExecute Logic for DelegateCommand 【发布时间】:2011-11-13 03:08:37 【问题描述】:

更新:焦点变成了 MVVM 而不是实际的问题,所以我正在更新它。

我对@9​​87654323@ 的DelegateCommand 有疑问。在我调用 RaiseCanExecuteChanged 之前它没有更新,这是期望的行为吗?

我在这里上传了一个简单的示例项目来重现这个问题:http://dl.dropbox.com/u/39657172/DelegateCommandProblem.zip

问题是这样的,我有两个这样的Buttons。一种是将Command绑定到RelayCommand实现,另一种是绑定到DelegateCommand的Prism实现

<Button Command="Binding DelegateSaveCommand"/>
<Button Command="Binding RelaySaveCommand"/>

ViewModel ICommands

DelegateSaveCommand = new DelegateCommand(Save, CanSaveDelegate);
RelaySaveCommand = new RelayCommand(param => Save(), param => CanSaveRelay);

CanExecute 方法/谓词

public bool CanSaveDelegate()

    return HasChanges;

public bool CanSaveRelay

    get  return HasChanges; 

两者都使用属性HasChanges。当HasChanges 更新时,只有CanSaveRelay 更新。这就是它的本意吗?

【问题讨论】:

+1 因为我对 PRISM 也有同样的问题...我实际上使用 MVVM Light 的 RelayCommand 而不是 PRISM 的 DelegateCommand。我在某处看到一篇关于重新编译 Prism 的库以添加 CanExecuteChanged 事件的文章,但我现在找不到它(无论如何都认为是 PRISM 2) @Meleak:关于您的问题可能是什么,我做了 2 种不同的猜测,但如果没有看到更多代码,我不可能确切知道原因是什么。 @Rachel:您可能正在和 Meleak 做同样的事情,所以请参考我的解决方案以了解为什么以及如何解决它。 @Meleak:另外,如果问题是您的模型正在更改属性并引发 propertychanged 事件,那么实际上是谁在更改 State 属性? @m-y:PropertyChanged 由实体引发,而实体又从数据库生成。我正在阅读你的答案 【参考方案1】:

正如已经提到的,这是DelagateCommand 的预期行为,而不是错误。 DelegateCommand 不会自动引发 CanExecuteChanged 事件,您必须在适当的时候通过调用 RaiseCanExecuteChanged 手动引发该事件。而RelayCommand 则转发CommandManager.RequerySuggested 事件。每次用户单击某处或按下按钮时都会引发此事件。

对于不太方便或没有合适的地方可以调用RaiseCanExecuteChanged 的情况(例如在您的场景中,您必须在模型上订阅PropertyChanged 事件等)我创建了以下简单的包装器确保包装命令的CanExecute 方法在CommandManager.RequerySuggested 事件上自动执行:

public class AutoCanExecuteCommandWrapper : ICommand

    public ICommand WrappedCommand  get; private set; 

    public AutoCanExecuteCommandWrapper(ICommand wrappedCommand)
    
        if (wrappedCommand == null) 
        
            throw new ArgumentNullException("wrappedCommand");
        

        WrappedCommand = wrappedCommand;
    

    public void Execute(object parameter)
    
        WrappedCommand.Execute(parameter);
    

    public bool CanExecute(object parameter)
    
        return WrappedCommand.CanExecute(parameter);
    

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

你可以这样使用它:

DelegateSaveCommand = new AutoCanExecuteCommandWrapper(new DelegateCommand(Save, CanSaveDelegate));

【讨论】:

请注意,此实现取决于CommandManager.RequerySuggested,仅当用户单击某处或按下某个键时才会触发(如答案中所述)。如果用户没有点击任何地方,该命令将保持非活动状态,这可能会造成混淆。 有一种情况,我想使用您的 Wrapper 类以及 DelegateCommand。因为需要自动和手动 RaiseCanExecuteCommand。自动完成后,您会说需要手动调用它。需要手动调用它,因为我将焦点更改为禁用的按钮。所以,我想通过手动调用 RaiseCanExecuteCommand 来启用按钮。在下一条评论中继续...... 顺便说一句,我尝试将新的 AutoCanExecuteCommandWrapper(new DelegateCommand(Save, CanSaveDelegate)) 分配给 DelegateCommand,但我收到一个错误,即无法将 AutoCanExecuteCommand 类型转换为 DelegateCommand。【参考方案2】:

如果你想坚持DelegateCommand,你可以使用ObservesCanExecute

DelegateSaveCommand = new DelegateCommand(Save, CanSaveDelegate).ObservesCanExecute(CanSaveDelegate);

请注意,如果您使用属性进行 CanExecute 检查,则还可以使用 ObservesProperty。但是你的属性必须调用 NotifyPropertyChanged。

【讨论】:

【参考方案3】:

Prism 提供的 DelegateCommand 中存在一个错误,它不会引发 CanExecute 事件。我用头撞墙了一天,直到我进入 Prism 框架提供的 DelegateCommand 类。我没有代码,但我可以稍后发布我的解决方案。

另一种方法是使用其他 RelayCommand 框架之一。

编辑 除了重新发布代码之外,还有其他提供解决方案的 SO 问题:

WPF-Prism CanExecute method not being called

Kent B. 有一篇好文章:MVVM Infrastructure: DelegateCommand

【讨论】:

我现在实际上正在使用 Josh Smiths MVVM 文章中的RelayCommand 实现(并且已经使用了很长时间),我对此非常满意。只是想我应该使用 Prism 提供的DelegateCommand,因为我现在使用的是 Prism 4.. 期待看到你的修复:) @Metro:不,这不是错误。在绑定方式(prism 的 Click.Command 或 WPF 命令)之间进行选择是您必须调用 RaiseCanExecuteChanged 的​​原因。这样做是为了让程序员更好地控制何时执行 CanExecute 委托。就像我之前所说的,如果您希望您的控件始终 查询 CanExecute 方法,则在您的控件上使用Command 属性。如果您想选择控件何时应查询 CanExecute 方法,请在控件上使用 Click.Command 附加属性。 @m-y 我记得无论使用 Command 属性还是附加属性,我都遇到了相同的行为。不过,已经有一段时间了。在我们将库更新为默认的DelegateCommand 后,我再也没有回头。 @Metro Smurf:如果您有时间,请查看我更新的问题。我上传了一个显示问题的示例项目。这是一个错误还是期望的行为?谢谢 您在更新后的问题中描述的场景与我遇到相同行为时的情况完全相同。我发布的 Kent B 的文章有一个更新的 DelegateCommand (IIRC) 可以解决这个问题。我还将发布用于解决 Prism DelegateCommand 的代码(将需要几个小时 - 在一些按钮混搭 sql 的中间)。

以上是关于DelegateCommand 的 CanExecute 逻辑的主要内容,如果未能解决你的问题,请参考以下文章

WPF customize DelegateCommand

2021-10-11 WPF上位机 62-Prism框架对象DelegateCommand

Prism DelegateCommand 中的 CanExecute 不起作用

WPF之MVVM(Step2)——自己实现DelegateCommand:ICommand

利刃 MVVMLight 6:命令基础

wpf自定义Mvvm框架