-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Fix #31939: CommandParameter TemplateBinding lost during reparenting #32961
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix #31939: CommandParameter TemplateBinding lost during reparenting #32961
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR fixes issue #31939 where CommandParameter TemplateBinding is lost during control template reparenting, causing CanExecute to be called with a null parameter before CommandParameter resolves. The fix introduces a BindableProperty dependency mechanism that delays CanExecute evaluation until dependent bindings have resolved.
Key changes:
- Added a dependency mechanism via
BindableProperty.DependsOn()to establish property dependencies - Modified
CommandElement.GetCanExecute()to check for pending bindings on dependencies before callingCanExecute - Registered
Command → CommandParameterdependencies in Button, ImageButton, CheckBox, and SearchBar controls - Added comprehensive unit tests to verify the fix works for both initial binding and reparenting scenarios
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Controls/src/Core/BindableProperty.cs | Adds Dependencies property and DependsOn() method to establish property dependencies |
| src/Controls/src/Core/BindableObject.cs | Adds HasPendingBindingWithNullValue() helper to detect unresolved bindings |
| src/Controls/src/Core/CommandElement.cs | Updates GetCanExecute() to check dependencies for pending bindings before calling CanExecute |
| src/Controls/src/Core/Button/ButtonElement.cs | Registers Command → CommandParameter dependency in static constructor and restructures property initialization |
| src/Controls/src/Core/Button/Button.cs | Passes CommandProperty to GetCanExecute() for dependency checking |
| src/Controls/src/Core/ImageButton/ImageButton.cs | Passes CommandProperty to GetCanExecute() for dependency checking |
| src/Controls/src/Core/CheckBox/CheckBox.cs | Passes CommandProperty to GetCanExecute() for dependency checking |
| src/Controls/src/Core/CheckBox/CheckBox.Mapper.cs | Registers Command → CommandParameter dependency in static constructor |
| src/Controls/src/Core/SearchBar/SearchBar.cs | Passes SearchCommandProperty to GetCanExecute() for dependency checking |
| src/Controls/src/Core/SearchBar/SearchBar.Mapper.cs | Registers SearchCommand → SearchCommandParameter dependency in static constructor |
| src/Controls/tests/Xaml.UnitTests/Issues/Maui31939.xaml | XAML test page with custom control and ControlTemplate demonstrating the bug scenario |
| src/Controls/tests/Xaml.UnitTests/Issues/Maui31939.xaml.cs | Comprehensive unit tests verifying initial binding and reparenting scenarios, including custom control and command implementations |
cd23217 to
c5e0099
Compare
When a Button inside a ControlTemplate has both Command and CommandParameter with TemplateBinding, the async binding application path can cause Command to be evaluated before CommandParameter resolves, resulting in CanExecute being called with null parameter. ## Solution Add a BindableProperty dependency mechanism via DependsOn() method. When CommandProperty.DependsOn(CommandParameterProperty) is registered, the CommandElement.GetCanExecute() forces the CommandParameter binding to apply before calling CanExecute. This ensures the parameter value is available. ## Changes - BindableProperty: Add Dependencies property and DependsOn() method - BindableObject: Add ForceBindingApply() to force a binding to apply immediately - CommandElement: Force dependency bindings to apply before calling CanExecute - ButtonElement, CheckBox, SearchBar, MenuItem, RefreshView, TextCell: Register Command -> CommandParameter dependency - All ICommandElement implementations: Pass CommandProperty to GetCanExecute() ## Testing Added unit tests that verify: 1. Initial template binding works correctly 2. CommandParameter is preserved after reparenting (the bug scenario)
c5e0099 to
5485a34
Compare
793fc12 to
4310846
Compare
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Description
Fixes #31939
When a Button inside a ControlTemplate has both Command and CommandParameter with TemplateBinding, the async binding application path can cause Command to be evaluated before CommandParameter resolves, resulting in
CanExecutebeing called with a null parameter.Solution
Add a BindableProperty dependency mechanism via
DependsOn()method. WhenCommandProperty.DependsOn(CommandParameterProperty)is registered, theCommandElement.GetCanExecute()method checks if CommandParameter has a pending binding with null value. If so, it returnstrue(assumes command can execute) instead of callingCanExecute(null). When the binding resolves,OnCommandParameterChangedfires andCanExecuteis properly re-evaluated.Changes
Dependenciesproperty andDependsOn()methodHasPendingBindingWithNullValue()helperCanExecuteCommandPropertytoGetCanExecute()Testing
Added unit tests that verify:
Verified: Tests fail without fix, pass with fix.