mvvmlight icon indicating copy to clipboard operation
mvvmlight copied to clipboard

Could anyone suggest a neat way to Dis-/Enable buttons

Open DeputyOfCopyPaster opened this issue 4 years ago • 3 comments

Hello everyone, could anyone suggest a neat way to dis-/enable buttons? Currently I'm doing such things as follows:

ViewModel:

// Properties
 public bool Save_btn_is_enabled
        {
            get { return _save_btn_is_enabled; }
            set { Set(ref _save_btn_is_enabled, value); }
        }
        private bool _save_btn_is_enabled;

// Constructor
Window_ViewModel()  
{
 DoSomething_command  = new RelayCommand(DoSomethingMethod);
 Save_btn_is_enabled = true;

}

//  do something method
privat async void DoSomethingMethod()
{
   Save_btn_is_enabled = false;

   // do smth here before dealying

 await Task.Delay(clickActionDelay1)

  // do smth here after delaying

 Save_btn_is_enabled = true;
}

DeputyOfCopyPaster avatar Feb 20 '20 16:02 DeputyOfCopyPaster

ICommand and RelayCommand have a Func<bool> canExecute as well as an Action execute. When a button binds to an ICommand the enabled state binds to CanExecute and is updated on the CanExecuteChanged event. See the MVVM light sample RaiseCanExecuteChanged Sample

bobonline19 avatar Feb 21 '20 09:02 bobonline19

IMHO, this is quite bad example on how this works. Also I couldn't open this sample with VS2015. Perhaps does anyone have better ones?

DeputyOfCopyPaster avatar Feb 21 '20 13:02 DeputyOfCopyPaster

@DeputyOfCopyPaster from your example it looks like you are trying to disable access while an async action is in process. If that is the case what you are doing is perfectly fine. Here is an example using ICommand that does not allow concurrent execution. Example

XAML

<Button
    Text="Give me coffee !" 
    Command="{Binding Submit}" />

CODE

public interface IAsyncCommand : ICommand
{
    Task ExecuteAsync();
    bool CanExecute();
}
public class AsyncCommand : IAsyncCommand
{
    public event EventHandler CanExecuteChanged;

    private bool _isExecuting;
    private readonly Func<Task> _execute;
    private readonly Func<bool> _canExecute;
    private readonly IErrorHandler _errorHandler;

    public AsyncCommand(
        Func<Task> execute,
        Func<bool> canExecute = null,
        IErrorHandler errorHandler = null)
    {
        _execute = execute;
        _canExecute = canExecute;
        _errorHandler = errorHandler;
    }

    public bool CanExecute()
    {
        return !_isExecuting && (_canExecute?.Invoke() ?? true);
    }

    public async Task ExecuteAsync()
    {
        if (CanExecute())
        {
            try
            {
                _isExecuting = true;
                await _execute();
            }
            finally
            {
                _isExecuting = false;
            }
        }

        RaiseCanExecuteChanged();
    }

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

#region Explicit implementations
    bool ICommand.CanExecute(object parameter)
    {
        return CanExecute();
    }

    void ICommand.Execute(object parameter)
    {
        ExecuteAsync().FireAndForgetSafeAsync(_errorHandler);
    }
#endregion
}
public class MainViewModel : ViewModelBase
{
    private bool _isBusy;
    public bool IsBusy
    {
        get => _isBusy;
        private set => Set(ref _isBusy, value);
    }

    public IAsyncCommand Submit { get; private set; }

    public MainViewModel()
    {
        Submit = new AsyncCommand(ExecuteSubmitAsync, CanExecuteSubmit);
    }

    private async Task ExecuteSubmitAsync()
    {
        try
        {
            IsBusy = true;
            var coffeeService = new CoffeeService();
            await coffeeService.PrepareCoffeeAsync();
        }
        finally
        {
            IsBusy = false;
        }
    }

    private bool CanExecuteSubmit()
    {
        return !IsBusy;
    }
}

thatsjohnson avatar Jun 30 '21 18:06 thatsjohnson