Ошибка CS0201 в имплементации ICommand для приложения WPF - PullRequest
0 голосов
/ 27 апреля 2020

Я разрабатываю приложение WPF, очевидно, я использую шаблон MVVM. Без внешней библиотеки (MvvmCross, MvvmLight, ...)

И я попытался реализовать ICommand:

Вариант 1

public class Command : ICommand
{
    private readonly Func<bool> _canExecute;
    private readonly Action _action;

    public Command1(Action action, Func<bool> canExecute)
    {
        _action = action;
        _canExecute = canExecute;
    }

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

    public bool CanExecute(object parameter) => true;

    public void Execute(object parameter) => _action();
}

Вариант 2

public class Command : ICommand
{
    private readonly Func<bool> _canExecute;
    private readonly Action<object> _action;

    public Command1(Action<object> action, Func<bool> canExecute)
    {
        _action = action;
        _canExecute = canExecute;
    }

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

    public bool CanExecute(object parameter) => true;

    public void Execute(object parameter) => _action(parameter);
}

Вариант 3

... с некоторыми делегатами

public class Command : ICommand
{
    private readonly Func<object, bool> _canExecute;
    private readonly Action<object> _execute;

    public Command(Action<object> execute) => _execute = execute ?? throw new ArgumentNullException(nameof(execute));

    public Command(Action execute)
        : this((Action<object>)delegate { execute(); })
    {
        if (execute == null)
        {
            throw new ArgumentNullException(nameof(execute));
        }
    }

    public Command(Action<object> execute, Func<object, bool> canExecute)
        : this(execute) => _canExecute = canExecute ?? throw new ArgumentNullException(nameof(canExecute));

    public Command(Action execute, Func<bool> canExecute)
        : this(delegate
        {
            execute();
        }, (object o) => canExecute())
    {
        if (execute == null)
        {
            throw new ArgumentNullException(nameof(execute));
        }
        if (canExecute == null)
        {
            throw new ArgumentNullException(nameof(canExecute));
        }
    }

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

    public bool CanExecute(object parameter) => _canExecute != null ? _canExecute(parameter) : true;

    public void Execute(object parameter) => _execute(parameter);
}

Во всех случаях:

public class MainViewModel : BaseViewModel
{
    public ICommand MyCommand = new Command(() => MyVoid());
    private void MyVoid()
    {
        // do something
    }
}
public class MainViewModel : BaseViewModel
{
    public ICommand MyCommand = new Command(MyVoid);
    private void MyVoid()
    {
        // do something
    }
}

Я имею ошибка CS0201 (Только операторы присваивания, вызова, приращения, уменьшения, ожидания и новых выражений объектов могут использоваться как оператор).

Я не понимаю, почему.

В других проектах, в которых используется шаблон MVVM (Xamarin.Forms, Xamarin, ...), я использую Xamarin.Forms.Command или MvxCommand (MvvmCross), и он работает ...

1 Ответ

0 голосов
/ 27 апреля 2020

Я не предлагаю называть реализующий класс Command, поскольку вы можете столкнуться с конфликтом имен с некоторыми встроенными классами.

Ближайший к вашему коду попытки:

public ICommand MyCommand => new Command(parameter => { MyVoid(); });

или то же самое в синтаксисе "блока"

public ICommand MyCommand
{
    get
    {
        return new Command(parameter => { MyVoid(); });
    }
}

Но это неправильный подход, поскольку он будет создавать новый экземпляр Command при каждом вызове команды. Дайте сборщику мусора как можно меньше работы. Примеры правильных способов сделать это вы можете найти ниже.

Вы разрабатываете другой велосипед. :) Смотрите здесь .

Вот копия-вставка из моего проекта. Это точно так же, как в приведенной выше ссылке.

public class RelayCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Func<object, bool> _canExecute;

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

    public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
    public void Execute(object parameter) => _execute(parameter);
}

И использование

<Button Contecnt="Click me!" Command="{Binding MyCommand}"/>
private ICommand _myCommand;

public ICommand MyCommand => _myCommand ?? (_myCommand = new RelayCommand(parameter =>
{
    // do here the execution
}));

И с parameter (Binding для CommandParameter также доступно)

<Button Contecnt="Click me!" Command="{Binding MyCommand}" CommandParameter="test value"/>
public ICommand MyCommand => _myCommand ?? (_myCommand = new RelayCommand(parameter =>
{
    if (parameter is string p && p == "test value")
    {
        // do here the execution
    }
}));

И, наконец, использование необязательного CanExecute

public ICommand MyCommand => _myCommand ?? (_myCommand = new RelayCommand(parameter =>
{
    // do here the execution
    // don't put the same condition as in CanExecute here,
    // it was already checked before execution has entered this block
},
parameter => (x > y) && (a + b > c) // any condition or just return a bool
));

, если CanExecute возвращает false, команда не будет выполнена и Button или MenuItem становится автоматически отключенным. Просто проверь это.

...