如何实现没有参数的ICommand

5
在我的项目中,我想使用MVVM(和Commands)。我已经开始学习关于命令和ICommand实现的知识。
我想创建一个没有参数的ICommand实现。 (用于触发数据加载 / 刷新等操作 - 我不需要任何参数来执行它,所以自然而然地尝试创建没有参数的命令)
以下是我正在使用的代码:
using System.Windows.Input;

public class NoParameterCommand : ICommand
{
    private Action executeDelegate = null;
    private Func<bool> canExecuteDelegate = null;
    public event EventHandler CanExecuteChanged = null;

    public NoParameterCommand(Action execute)
    {
        executeDelegate = execute;
        canExecuteDelegate = () => { return true; };
    }
    public NoParameterCommand(Action execute, Func<bool> canExecute)
    {
        executeDelegate = execute;
        canExecuteDelegate = canExecute;
    }

    public bool CanExecute()
    {
        return canExecuteDelegate();
    }
    public void Execute()
    {
        if (executeDelegate != null)
        {
            executeDelegate();
        }
    }
}

但是我遇到了关于未正确实现ICommand接口的错误 ('XXX.YYYY.NoParameterCommand'未实现接口成员'System.Windows.Input.ICommand.Execute(object)')

所以我想改为这样:

(添加了在CanExecuteExecute中缺失的参数)

public class NoParameterCommand : ICommand
{
    ...omitted - no changes here...

    public bool CanExecute(object parameter) //here I added parameter
    {
        return canExecuteDelegate();
    }
    public void Execute(object parameter)    //and here
    {
        if (executeDelegate != null)
        {
            executeDelegate();
        }
    }
}
  1. 这是一个好的做法吗?
  2. 我应该使用其他方法吗?(如果是,我应该使用什么方法代替?)

这是一个不错的方式,当然将字段和事件初始化为null没有意义。字段默认为null,事件的后备字段也是如此。 - Kris Vandermotten
@KrisVandermotten 这只是我无法摆脱的旧习惯。直到你指出来,我甚至都没有意识到它存在 :) - mishan
3个回答

7
  1. 这是一个很好的做法。
  2. 不,你不应该使用其他方法。

额外建议:

重新思考后,我建议通过引入另一层次结构来改进您的架构,其中CanExecute()Execute()abstract。从那个类派生出调用委托的命令类。

这样,您可以在以后决定,是通过委托还是通过子类化基础命令类来为无参数命令提供逻辑。


我不确定是否做得正确。宁愿小心谨慎,也不要后悔。感谢您的快速回复和建议,这次我打算使用委托 - 更适合我,但我会记在心里。 - mishan

5

我不太确定你的关注点在哪里。在ICommand接口中忽略参数是很常见的。

如果你真的想要没有参数的CanExecuteExecute方法,你可以显式地实现该接口(而不是隐式地实现)。ICommand方法仍然存在,但对于从外部查看你的对象的任何人来说,他们将无法看到这些方法:

bool ICommand.CanExecute(object parameter) { this.CanExecute(); }

public bool CanExecute()
{
  //do work
}

你实际上是隐藏了接口的实现。如果有人想直接调用接口中的CanExecute方法,他们必须要将其类型转换为ICommand才能这样做。这种做法并没有什么优势,但如果你关心你的类在外部开发者看来的样子(例如你正在开发一个API),那么这样做可以使它看起来更加清晰,因为你让他们知道你不需要任何参数。


我只是不确定我是否做得正确,所以我问了一下。我是WP8开发的新手,正在学习如何做。网上有成千上万的教程,但我找不到我想要的是否正确。 - mishan

2
我个人更喜欢这种方式:
public class MyCommand : ICommand
{
    private static bool True() { return true; }

    private readonly Action _execute;
    private Func<bool> _canExecute;
    private Func<bool> _isVisible;

    public event EventHandler IsVisibleChanged;
    public event EventHandler CanExecuteChanged;

    public MyCommand(Action execute, Func<bool> canExecute = null, Func<bool> isVisible = null)
    {
        _execute = execute;
        _canExecute = canExecute ?? True;
        _isVisible = isVisible ?? True;
    }

    public void Execute()
    {
        _execute();
    }

    public Func<bool> CanExecute
    {
        set
        {
            _canExecute = value ?? True;
            CanExecuteChanged(this, new EventArgs());
        }
        get { return _canExecute; }
    }

    public Func<bool> IsVisible
    {
        set
        {
            _isVisible = value ?? True;
            IsVisibleChanged(this, new EventArgs());
        }
        get { return _isVisible; }
    }

    bool ICommand.CanExecute(object parameter)
    {
        return CanExecute();
    }

    void ICommand.Execute(object parameter)
    {
        Execute();
    }
}

然而,由于委托通常不会改变,我更喜欢不可变版本:

[ImmutableObject(true)]
public class MyImmutableCommand : ICommand
{
    private static bool True() { return true; }

    private readonly Action _execute;
    private readonly Func<bool> _canExecute;
    private readonly Func<bool> _isVisible;

    [Obsolete("Will not be invoked, because the implementation never changes.")]
    public event EventHandler CanExecuteChanged;

    public MyImmutableCommand(Action execute, Func<bool> canExecute = null, Func<bool> isVisible = null)
    {
        _execute = execute;
        _canExecute = canExecute ?? True;
        _isVisible = isVisible ?? True;
    }

    public bool CanExecute()
    {
        return _canExecute(); 
    }

    public bool IsVisible()
    {
        return _isVisible(); 
    }

    public void Execute()
    {
        _execute();
    }

    bool ICommand.CanExecute(object parameter)
    {
        return CanExecute();
    }

    void ICommand.Execute(object parameter)
    {
        Execute();
    }
}

IsVisible 属性是否被视图自然消耗,还是需要额外的代码/框架支持? - Daniel Möller
1
这是一些不被默认支持的额外代码。更好的方法是使用属性转换器将布尔值IsEnabled转换为Visibility,然后在绑定中使用该转换器。 - MovGP0
顺便说一句:这不再是我现在编写命令的方式了。我转而使用ReactiveUI库中的ReactiveCommand类,将其用于我所有的WPF、UWP和Blazor项目中。强烈建议学习该库。 - MovGP0

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