Могу ли я расширить класс команд, чтобы кнопка, которая к ним привязывалась, автоматически отключалась? - PullRequest
0 голосов
/ 04 мая 2020

В своем приложении я привязал свои кнопки к командам с помощью «MVVM». Моя команда реализована следующим образом:

        public Command CommandLoadStuff
        {
            get
            {
                return new Command(async () =>
                {
                    await DoLongStuff();
                });
            }
        }

Проблема состоит в том, что эти команды асин c, и пользователь может щелкнуть их несколько раз, в результате чего код также будет выполняться несколько раз.

В качестве первого подхода я использовал CanExecute:

        public Command CommandLoadStuff
        {
            get
            {
                return new Command(async () =>
                {
                    AppIsBusy = true;
                    await DoLongStuff();
                    AppIsBusy = false;
                },() => !AppIsBusy);
            }
        }

Теперь мне интересно, нет ли лучшего способа, чем обрабатывать CanExecute для каждой команды в отдельности.

Поскольку я инициализирую команду каждый раз время с "новым" интересно, если класс "Command" не может быть соответственно расширен. Он должен блокировать второе нажатие кнопки в течение срока службы с помощью CanExecute (возможно, в конструкторе?) И отпускать его после завершения выполнения команды. (Возможно, в функции Dispose?)

Есть ли способ достичь этого?

Ответы [ 2 ]

1 голос
/ 04 мая 2020

Расширение команды класса таким способом невозможно, насколько я могу судить, потому что Execute не является виртуальным, и вы должны передать действие execute конструктору. Во всяком случае, есть еще способ. Command происходит от ICommand, который имеет следующий интерфейс

public interface ICommand
{
    event EventHandler CanExecuteChanged;

    void Execute(object data);

    bool CanExecute(object data);
}

. Вы можете создать класс AsyncBlockingCommand (или любой другой), который будет возвращать соответствующее значение из CanExecute в зависимости от того, Метод asyn c все еще работает (я знаю, что есть проблемы с async void методами, поэтому обращайтесь с осторожностью)

public class AsyncBlockingCommand : ICommand
{
    bool _canExecute = true;
    Func<Task> _toExecute;

    public AsyncBlockingCommand(Func<Task> toExecute)
    {
        _toExecute = toExecute;
    }

    public event EventHandler CanExecuteChanged;

    public async void Execute(object data)
    {
        _canExecute = false;
        RaiseCanExecuteChanged();
        await _toExecute();
        _canExecute = true;
        RaiseCanExecuteChanged();
    }

    public bool CanExecute(object data) => _canExecute; 

    private void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

Перед выполнением вашего метода asyn c, устанавливается _canExecute до false и CanExecuteChanged повышен. Таким образом, ваш Button получит уведомление об изменении CanExecute и отключится сам. И наоборот после вызова метода asyn c. (Возможно, RaiseCanExecuteChanged нужно будет вызывать в главном потоке.)

0 голосов
/ 04 мая 2020

Вы можете использовать свойство IsEnabled, чтобы Button нельзя было щелкнуть. Как в следующем коде.

<Button 
    Text="click"
    Command={Binding Button1Command}
    IsEnabled={Binding AreButtonsEnabled} />

Если значение IsEnabled ложно, вы можете увидеть эту кнопку, она серого цвета, если вы нажмете ее, она не будет выполнять никаких команд.

enter image description here

Вот код MyViewModel.

private bool _areButtonsEnabled = true;

public bool AreButtonsEnabled
{
    get => _areButtonsEnabled;
    set
    {
        if (_areButtonsEnabled != value)
        {
            _areButtonsEnabled = value;
            OnPropertyChanged(nameof(AreButtonsEnabled)); // assuming your view model implements INotifyPropertyChanged
        }
    }
}

public ICommand Button1Command { get; protected set; }

public MyViewModel()
{
    Button1Command = new Command(HandleButton1Tapped);
}

private void HandleButton1Tapped()
{
    // Run on the main thread, to make sure that it is getting/setting the proper value for AreButtonsEnabled
    // And note that calls to Device.BeginInvokeOnMainThread are queued, therefore
    // you can be assured that AreButtonsEnabled will be set to false by one button's command
    // before the value of AreButtonsEnabled is checked by another button's command.
    // (Assuming you don't change the value of AreButtonsEnabled on another thread)
    Device.BeginInvokeOnMainThread(async() => 
    {
        if (AreButtonsEnabled)
        {
            AreButtonsEnabled = false;
            // DoLongStuff code
            await  Task.Delay(2000);
            AreButtonsEnabled = true;
        }
    });
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...