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:
- The class documentation states the default return value is
true
- Without a predicate, the command has no condition to evaluate — it should always be executable
- 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
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;
TryGetCommandArgument、ThrowArgumentExceptionForInvalidCommandArgument、Execute(object?) remain unchanged.
Help us help you
Yes, I'd like to be assigned to work on this item
Describe the bug
RelayCommand<T>.CanExecute(object?)unconditionally returnsfalsewhenTis a non-nullable value type (e.g.int, enums) and theparameterisnull— even when nocanExecutepredicate 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 — includingnull.In WPF applications on .NET Framework, the framework may call
CanExecute(null)during control initialization before theCommandParameterbinding has produced a value. With the unconditionalreturn false, commands without predicates (e.g.RelayCommand<SomeEnum>(DoSomething)) are permanently disabled.Current code (
src/CommunityToolkit.Mvvm/Input/RelayCommand{T}.cs):Regression
No response
Steps to reproduce
Expected behavior
CanExecute(null)should returntruewhen nocanExecutepredicate was provided, because:truenullguard's purpose is to prevent anullfrom being incorrectly mapped todefault(T)(e.g., 0 as SelectedIndex), but when there's no predicate, there's nothing to incorrectly map toFor commands with a predicate, returning
falseis 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
Nuget package version(s)
latest stable
Additional context
nulltodefault(T)for commands with predicates is unsafe. However, the resulting unconditionalreturn falseis overly broad — it also disables commands without predicates, which have no such ambiguity.return false;→return this.canExecute is null;TryGetCommandArgument、ThrowArgumentExceptionForInvalidCommandArgument、Execute(object?)remain unchanged.Help us help you
Yes, I'd like to be assigned to work on this item