RelayCommand<T>.CanExecute(null) returns false for value types without a predicate

Open Beginner friendly
#1,206 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
84/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
csharp

Research direction

Start with src/CommunityToolkit.Mvvm/Input/RelayCommand{T}.cs and inspect the null-parameter guard and the optional canExecute predicate. Verify the repro through ICommand.CanExecute(null), then confirm that predicate-free value-type commands return true while commands with predicates retain the documented false behavior.

Written by the indexing model from the issue text.

Description

bug :bug:
Describe the bug

RelayCommand<T>.CanExecute(object?) unconditionally returns false when T is a non-nullable value type (e.g. int, enums) and the parameter is null — even when no canExecute predicate was provided to the constructor.

This violates the documented contract: "The default return value for the CanExecute method is true." A command constructed without a predicate is meant to always be executable, regardless of the parameter value — including null.

In WPF applications on .NET Framework, the framework may call CanExecute(null) during control initialization before the CommandParameter binding has produced a value. With the unconditional return false, commands without predicates (e.g. RelayCommand<SomeEnum>(DoSomething)) are permanently disabled.

Current code (src/CommunityToolkit.Mvvm/Input/RelayCommand{T}.cs):

if (parameter is null && default(T) is not null)
{
    return false;   // unconditional — even when no predicate exists
}
Regression

No response

Steps to reproduce
// 1. Create a RelayCommand without a predicate
var command = new RelayCommand<int>(i => Console.WriteLine(i));

// 2. Call CanExecute with null (as WPF does during initialization)
bool result = ((System.Windows.Input.ICommand)command).CanExecute(null);

// 3. Observe the result
Console.WriteLine(result); // Actual: False, Expected: True
Expected behavior

CanExecute(null) should return true when no canExecute predicate was provided, because:

  1. The class documentation states the default return value is true
  2. Without a predicate, the command has no condition to evaluate — it should always be executable
  3. The null guard's purpose is to prevent a null from being incorrectly mapped to default(T) (e.g., 0 as SelectedIndex), but when there's no predicate, there's nothing to incorrectly map to

For commands with a predicate, returning false is correct — we cannot guess the user's intent. This proposal keeps that behavior unchanged.

Screenshots

No response

IDE and version

Other

IDE version

Insiders [12020.428]

Nuget packages
  • CommunityToolkit.Common
  • CommunityToolkit.Diagnostics
  • CommunityToolkit.HighPerformance
  • CommunityToolkit.Mvvm (aka MVVM Toolkit)
Nuget package version(s)

latest stable

Additional context
  • Related discussion: WindowsCommunityToolkit #3619. That issue correctly identified that mapping null to default(T) for commands with predicates is unsafe. However, the resulting unconditional return false is overly broad — it also disables commands without predicates, which have no such ambiguity.
  • The proposed fix is a one-line change: return false;return this.canExecute is null;
  • TryGetCommandArgumentThrowArgumentExceptionForInvalidCommandArgumentExecute(object?) remain unchanged.
Help us help you

Yes, I'd like to be assigned to work on this item

Dominant language
C#
Stars
3.8k
Forks
400
PR merge metrics
No merged PRs in 30d

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from CommunityToolkit/dotnet

All issues in CommunityToolkit/dotnet

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.