Async Programming
ICommand Interface
C#
Asynchronous Commands
.NET Development

ICommand.CanExecute async

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

ICommand.CanExecute is a synchronous method that returns bool, so it cannot itself be truly asynchronous. In MVVM applications, the correct pattern is to keep CanExecute fast, compute expensive state elsewhere, cache the result, and raise CanExecuteChanged when that cached result changes.

Why CanExecute Should Not Do Async Work

The WPF command system may call CanExecute frequently and at times you do not control. UI elements use it to decide whether buttons, menu items, and other command sources should be enabled.

Because the signature is:

csharp
bool CanExecute(object parameter)

there is nowhere to await naturally. If you try to block on asynchronous work inside CanExecute, you risk:

  • freezing the UI thread
  • causing deadlocks
  • doing expensive work repeatedly

That is why CanExecute should behave like a quick state check, not like a network request or database query.

Use Cached State Plus CanExecuteChanged

The common design is:

  1. compute availability asynchronously elsewhere
  2. store the result in a field or property
  3. return that cached value from CanExecute
  4. raise CanExecuteChanged when the value changes

Here is a simple async command implementation:

csharp
1using System;
2using System.Threading.Tasks;
3using System.Windows.Input;
4
5public sealed class AsyncRelayCommand : ICommand
6{
7    private readonly Func<Task> _execute;
8    private bool _isRunning;
9    private bool _canRun = true;
10
11    public AsyncRelayCommand(Func<Task> execute)
12    {
13        _execute = execute;
14    }
15
16    public event EventHandler? CanExecuteChanged;
17
18    public bool CanExecute(object? parameter) => _canRun && !_isRunning;
19
20    public async void Execute(object? parameter)
21    {
22        if (!CanExecute(parameter))
23            return;
24
25        _isRunning = true;
26        RaiseCanExecuteChanged();
27
28        try
29        {
30            await _execute();
31        }
32        finally
33        {
34            _isRunning = false;
35            RaiseCanExecuteChanged();
36        }
37    }
38
39    public void SetCanRun(bool canRun)
40    {
41        _canRun = canRun;
42        RaiseCanExecuteChanged();
43    }
44
45    private void RaiseCanExecuteChanged() =>
46        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
47}

CanExecute stays synchronous and cheap, while the rest of the application can still update the allowed state asynchronously.

Perform the Expensive Check Outside CanExecute

Suppose whether a command can run depends on a server call. Do that work separately and then update the command.

csharp
1public async Task RefreshAvailabilityAsync()
2{
3    bool allowed = await _permissionService.CanUploadAsync();
4    UploadCommand.SetCanRun(allowed);
5}

This pattern is much safer than trying to turn CanExecute into an async method. The UI remains responsive, and the command system still gets a plain boolean when it asks.

A second benefit is predictability. The expensive check runs when you decide it should run, not whenever WPF happens to reevaluate commands.

Keep Execute Async-Aware Too

If Execute performs asynchronous work, make sure the command disables itself while running if re-entry should be prevented. That is why the example tracks _isRunning. Otherwise, the user may click repeatedly and start overlapping operations.

Some projects also define an IAsyncCommand interface that exposes Task ExecuteAsync(...) in addition to the ICommand members. That can make testing and composition easier, but the WPF binding surface still ultimately relies on synchronous CanExecute.

The practical split is clean: asynchronous work belongs in command execution and in background refresh logic, while the actual CanExecute answer stays as a cheap cached boolean for the binding system.

Common Pitfalls

  • Trying to make CanExecute itself async, which does not fit the ICommand contract.
  • Blocking on async work inside CanExecute with .Result or .Wait().
  • Forgetting to raise CanExecuteChanged after the cached executable state changes.
  • Allowing multiple overlapping Execute calls when the command should be single-run.
  • Recomputing availability on every UI query instead of deciding explicitly when the async check should run.

Summary

  • 'ICommand.CanExecute is synchronous and should remain fast.'
  • Do expensive checks asynchronously outside CanExecute.
  • Cache the result and return that cached value from CanExecute.
  • Raise CanExecuteChanged whenever the cached state changes.
  • Treat async command support as a coordination pattern around ICommand, not as a different CanExecute signature.

Course illustration
Course illustration

All Rights Reserved.