Skip to content

Fix CanExecute(null) for value types without predicate#1207

Open
apachezy wants to merge 2 commits into
CommunityToolkit:mainfrom
apachezy:patch-1
Open

Fix CanExecute(null) for value types without predicate#1207
apachezy wants to merge 2 commits into
CommunityToolkit:mainfrom
apachezy:patch-1

Conversation

@apachezy

@apachezy apachezy commented Jul 26, 2026

Copy link
Copy Markdown

Closes #1206

When RelayCommand<T>.CanExecute(object?) receives null for a non-nullable value type T, it unconditionally returns false — even when no canExecute predicate was provided. This violates the documented contract: "The default return value for the CanExecute method is true."

This single-line fix distinguishes between the two cases:

  • No predicate → return true (the command is always executable, as documented)
  • Has predicate → return false (cannot meaningfully evaluate null as T, safe fallback — unchanged from current behavior)

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 are permanently disabled, as the initial false result is cached and never re-evaluated.

This fix is fully compatible with the reasoning in WindowsCommunityToolkit #3619: for commands with a predicate, mapping null to default(T) would conflate "no value" with a legitimate value (e.g., 0 as SelectedIndex). The conclusion that return false is safe for that scenario is preserved here. The case of no predicate was not explicitly considered in that discussion — a command without a predicate should simply always be executable, as the class documentation states.

PR Checklist

  • Created a feature/dev branch in your fork (vs. submitting directly from a commit on main)
  • Based off latest main branch of toolkit
  • PR doesn't include merge commits (always rebase on top of our main, if needed)
  • Tested code with current supported SDKs
  • New component
    • Pull Request has been submitted to the documentation repository instructions. Link:
    • Added description of major feature to project description for NuGet package (4000 total character limit, so don't push entire description over that)
  • Tests for the changes have been added (for bug fixes / features) (if applicable)
  • Header has been added to all new source files (run build/UpdateHeaders.bat)
  • Contains NO breaking changes
  • Every new API (including internal ones) has full XML docs
  • Code follows all style conventions

Other information

Change in src/CommunityToolkit.Mvvm/Input/RelayCommand{T}.cs:

 if (parameter is null && default(T) is not null)
 {
-    return false;
+    return this.canExecute is null;
 }
Scenario canExecute Before After
RelayCommand<int>(Do) null false true
RelayCommand<int>(Do, i => i >= 0) predicate false false (unchanged)

TryGetCommandArgumentThrowArgumentExceptionForInvalidCommandArgumentExecute(object?) 均未改动。

@apachezy

Copy link
Copy Markdown
Author

@dotnet-policy-service agree [company="None"]
@dotnet-policy-service agree
@dotnet-policy-service agree company="Microsoft"

@apachezy

Copy link
Copy Markdown
Author

@dotnet-policy-service agree company="no company"

@apachezy
apachezy marked this pull request as draft July 26, 2026 11:14
@apachezy
apachezy marked this pull request as ready for review July 26, 2026 11:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant