ICommand: disable-enable button during long process run with Task.Run()

1 week ago 17
ARTICLE AD BOX

WPF app using MVVM - this is my first time trying to use CanExecute of an ICommand, so please bear with me.

A button is bound to a Command in the view model. When the button is clicked, it runs a long process (I am calling it with await Task.Run() so that the UI is not blocked).

I then update an ObservableCollection in the view model to show the results in the UI, but I am not including that part here for simplicity.

Now I want to disable the button when it's clicked, and re-enable it after the long process finishes. I have declared a private field and property in the view model, IsBusy, and in the method associated with CanExecute of the Command, I return true or false based on the value of this property.

This is half-working: when the button is clicked, it gets disabled. Then when the process ends, the UI shows the results ok, BUT the button is not re-enabled... only when I click anywhere on the UI after that, the button is enabled again.

This is in my XAML:

<Button Content="Do Process" Command="{Binding DoProcessCommand}" />

Here's the view model:

public class MyViewModel : BaseViewModel { public ICommand DoProcessCommand { get; private set; } private bool _isBusy; public bool IsBusy { get { return _isBusy; } set { _isBusy = value; OnPropertyChanged(nameof(IsBusy)); } } // ctor public MyViewModel() { DoProcessCommand = new RelayCommand<object>(DoProcessCommandMethod, CanDoProcessCommandMethod); IsBusy = false; } // Command Execute private bool CanDoProcessCommandMethod(object parameter) { return !IsBusy; } // Command CanExecute private async void DoProcessCommandMethod(object parameter) { IsBusy = true; var results = await Task.Run(() => MyService.someLongProcess()); // so something with the results... IsBusy = false; // more code here... } }

Any help?

Note: the method that does the long process is not async, I am just calling it inside a await Task.Run() so that it does not block the UI thread... I just mention this in case it affects anything...

UPDATE

I forgot to add my implementation of RelayCommand, just in case:

public class RelayCommand<T> : ICommand { readonly Action<T> _execute = null; readonly Predicate<T> _canExecute = null; public RelayCommand(Action<T> execute) : this(execute, null) { } public RelayCommand(Action<T> execute, Predicate<T> canExecute) { if (execute == null) throw new ArgumentNullException("execute"); _execute = execute; _canExecute = canExecute; } public bool CanExecute(object parameter) { return _canExecute == null ? true : _canExecute((T)parameter); } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public void Execute(object parameter) { _execute((T)parameter); } }
Read Entire Article