Search code examples
c#wpfxamlrouted-commands

Prevent button command from executing from OnClick when a condition is met


I have a RoutedUI Command that is bound as a Command property for a button and an OnClick event. Whenever I evaluate some condition from the OnClick I want to prevent the command from executing. I referred to this post but dosen't help much Prevent command execution. One quick fix is to get the sender of button on click and set its command to null. But I want to know if there is an other way. Please help.

 <Button DockPanel.Dock="Right"
                        Name="StartRunButtonZ"
                        VerticalAlignment="Top"
                        Style="{StaticResource GreenGreyButtonStyle}"
                        Content="{StaticResource StartARun}"
                        Width="{StaticResource NormalEmbeddedButtonWidth}"
                        Click="StartRunButton_Click"
                        Command="{Binding StartRunCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl},AncestorLevel=2}}" 
                    />

Here is the code behind

private void StartRunButton_Click(object sender, RoutedEventArgs e)
        {
         if(SomeCondition){
//Prevent the Command from executing.
}
        }

Solution

  • Assuming your StartRun() method follows the async / await pattern, then replace your ICommand implementation with the following. It will set CanExecute to false while the task is running, which will automatically disable the button. You don't need to mix commands and click event handlers.

    public class perRelayCommandAsync : ViewModelBase, ICommand
    {
        private readonly Func<Task> _execute;
        private readonly Func<bool> _canExecute;
    
        public perRelayCommandAsync(Func<Task> execute) : this(execute, () => true) { }
    
        public perRelayCommandAsync(Func<Task> execute, Func<bool> canExecute)
        {
            _execute = execute ?? throw new ArgumentNullException(nameof(execute));
            _canExecute = canExecute;
        }
    
        private bool _isExecuting;
    
        public bool IsExecuting
        {
            get => _isExecuting;
            set
            {
                if(Set(nameof(IsExecuting), ref _isExecuting, value))
                    RaiseCanExecuteChanged();
            }
        }
    
        public event EventHandler CanExecuteChanged;
    
        public bool CanExecute(object parameter) => !IsExecuting 
                                                    && (_canExecute == null || _canExecute());
    
        public async void Execute(object parameter)
        {
            if (!CanExecute(parameter))
                return;
    
            IsExecuting = true;
            try
            { 
                await _execute().ConfigureAwait(true);
            }
            finally
            {
                IsExecuting = false;
            }
        }
    
        public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
    

    More details at my blog post.