DelegateCommand 的 CanExecute 逻辑

发布于 2024-12-03 17:56:02 字数 1222 浏览 1 评论 0原文

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

我在 DelegateCommandCanExecute 方面遇到问题。在我调用 RaiseCanExecuteChanged 之前它不会更新,这是期望的行为吗?

在此处输入图像描述

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

问题是这样的,我有两个这样的 Button 。一种是将 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。这就是事情本来的样子吗?

Update: The focus became MVVM instead of the actual question so I'm updating it.

I'm having a problem with CanExecute for DelegateCommand. It doesn't update before I call RaiseCanExecuteChanged, is this the desired behavior?

enter image description here

I uploaded a simple sample project reproducing this problem here : http://dl.dropbox.com/u/39657172/DelegateCommandProblem.zip

The problem is this, I have two Buttons like this. One is Binding Command to a RelayCommand implementation and the other is binding to the Prism implementation of DelegateCommand

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

The ViewModel ICommands

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

and the CanExecute method/predicate

public bool CanSaveDelegate()
{
    return HasChanges;
}
public bool CanSaveRelay
{
    get { return HasChanges; }
}

Both are using the property HasChanges. When HasChanges is updated, only the CanSaveRelay updates. Is this the way it's meant to be?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦里的微风 2024-12-10 17:56:03

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

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

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));

As it already was mentioned, this is intended behavior of DelagateCommand, not a bug.
DelegateCommand doesn't raise CanExecuteChanged event automatically, you have to raise that event manually by calling RaiseCanExecuteChanged when appropriate. Whereas RelayCommand relays on CommandManager.RequerySuggested event for that. This event is raised every time the user clicks somewhere or presses a button.

For situations when it is not very convenient or there is no appropriate place for calling RaiseCanExecuteChanged (like in your scenario you have to subscribe to PropertyChanged event on the model, etc) I have created the following simple wrapper that ensures that the CanExecute method of the wrapped command is executed automatically on CommandManager.RequerySuggested event:

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; }
    }
}

You can use it like this:

DelegateSaveCommand = new AutoCanExecuteCommandWrapper(new DelegateCommand(Save, CanSaveDelegate));
沉默的熊 2024-12-10 17:56:03

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

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

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

If you want to stick to DelegateCommand you can use ObservesCanExecute:

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

Note that there is also ObservesProperty available if you are using a property for your CanExecute check. But then your property has to call NotifyPropertyChanged.

飘落散花 2024-12-10 17:56:03

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

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

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

Kent B. 有一篇很好的文章:MVVM 基础设施:DelegateCommand

There is a bug in the DelegateCommand provided by Prism which doesn't raise the CanExecute event. I beat my head against the wall for a day until I dove into the DelegateCommand class provided by the Prism framework. I don't have the code with me, but I can post my resolution in a bit.

The alternative is to use one of the other RelayCommand frameworks out there.

Edit
Rather than reposting the code, there are other SO questions that provide resolutions:

And Kent B. has a good article: MVVM Infrastructure: DelegateCommand

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文