[XSG][BindingSourceGen] Add support for RelayCommand to compiled bindings#32954
Conversation
Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
… code Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
simonrozsival
left a comment
There was a problem hiding this comment.
@copilot could we move the logic to the path parser so that it's in shared code? I imagine we would just have this as another MemberAccess in the path where we assuem that the property type is global::CommunityToolkit.Mvvm.Input.AsyncRelayCommand.
Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Done! I've moved the logic into a shared helper method Commit: 98ce9c7 |
simonrozsival
left a comment
There was a problem hiding this comment.
@copilot move the logic to PathParser HandleMemberAccessExpression. Add a method for known special cases as a fallback when MemberAccessExpressionSyntax type or symbol are not known at compile time.
Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Done! I've moved the logic to Commit: 3e25912 |
simonrozsival
left a comment
There was a problem hiding this comment.
@copilot add unit tests with mocked class RelayCommandAttribute.
Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Done! Added 3 unit tests with mocked
All tests pass (122 total BindingSourceGen tests). Commit: d16ea2d |
There was a problem hiding this comment.
Pull request overview
This PR adds support for CommunityToolkit.Mvvm's [RelayCommand] attribute to .NET MAUI compiled bindings. The implementation enables both XAML string-based bindings and C# lambda bindings to work with properties generated by the RelayCommand source generator, which were previously invisible to MAUI's binding source generators.
Key changes:
- Adds type inference logic to detect and handle RelayCommand-generated properties when normal symbol resolution fails
- Introduces
TryGetRelayCommandPropertyTypeextension method that searches for methods with[RelayCommand]attribute and infers the corresponding ICommand property type - Implements fallback mechanisms in both BindingSourceGen (for C# lambdas) and SourceGen (for XAML) to handle source-generated properties
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Controls/src/BindingSourceGen/ITypeSymbolExtensions.cs | Adds TryGetRelayCommandPropertyType method to detect RelayCommand methods and infer command property types, plus helper GetAllMethods for searching methods in type hierarchy |
| src/Controls/src/BindingSourceGen/PathParser.cs | Adds TryHandleSpecialCases fallback method that uses RelayCommand inference when normal symbol resolution fails in path parsing |
| src/Controls/src/BindingSourceGen/BindingSourceGenerator.cs | Modifies GetLambdaReturnType to attempt RelayCommand type inference when semantic model cannot resolve lambda return type |
| src/Controls/src/SourceGen/ITypeSymbolExtensions.cs | Adds TryGetProperty helper method that falls back to RelayCommand inference when property lookup fails, integrating BindingSourceGen functionality into XAML source generation |
| src/Controls/src/SourceGen/CompiledBindingMarkup.cs | Updates XAML path parsing to use new TryGetProperty method for RelayCommand support, handles nullable property symbol when setting writable status |
| src/Controls/tests/BindingSourceGen.UnitTests/RelayCommandTests.cs | Adds unit tests for C# lambda binding generation with RelayCommand properties, including direct extension method testing and negative test cases |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
793fc12 to
4310846
Compare
…ings (#32954) * Initial plan * Add RelayCommand support to binding source generators Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Consolidate RelayCommand detection logic into shared BindingSourceGen code Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Remove test files that cannot work in test environment Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Refactor RelayCommand logic into shared helper method Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Move RelayCommand logic to PathParser with special case handling Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Simplify TryGetProperty naming and add RelayCommand unit tests Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Enable expression-based bindings with RelayCommand support Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> Co-authored-by: Šimon Rozsíval <simon@rozsival.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ings (#32954) * Initial plan * Add RelayCommand support to binding source generators Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Consolidate RelayCommand detection logic into shared BindingSourceGen code Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Remove test files that cannot work in test environment Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Refactor RelayCommand logic into shared helper method Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Move RelayCommand logic to PathParser with special case handling Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Simplify TryGetProperty naming and add RelayCommand unit tests Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Enable expression-based bindings with RelayCommand support Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> Co-authored-by: Šimon Rozsíval <simon@rozsival.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ings (#32954) * Initial plan * Add RelayCommand support to binding source generators Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Consolidate RelayCommand detection logic into shared BindingSourceGen code Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Remove test files that cannot work in test environment Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Refactor RelayCommand logic into shared helper method Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Move RelayCommand logic to PathParser with special case handling Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Simplify TryGetProperty naming and add RelayCommand unit tests Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Enable expression-based bindings with RelayCommand support Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> Co-authored-by: Šimon Rozsíval <simon@rozsival.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ings (#32954) * Initial plan * Add RelayCommand support to binding source generators Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Consolidate RelayCommand detection logic into shared BindingSourceGen code Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Remove test files that cannot work in test environment Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Refactor RelayCommand logic into shared helper method Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Move RelayCommand logic to PathParser with special case handling Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Simplify TryGetProperty naming and add RelayCommand unit tests Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Enable expression-based bindings with RelayCommand support Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> Co-authored-by: Šimon Rozsíval <simon@rozsival.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ings (#32954) * Initial plan * Add RelayCommand support to binding source generators Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Consolidate RelayCommand detection logic into shared BindingSourceGen code Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Remove test files that cannot work in test environment Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Refactor RelayCommand logic into shared helper method Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Move RelayCommand logic to PathParser with special case handling Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Simplify TryGetProperty naming and add RelayCommand unit tests Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Enable expression-based bindings with RelayCommand support Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> Co-authored-by: Šimon Rozsíval <simon@rozsival.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ings (#32954) * Initial plan * Add RelayCommand support to binding source generators Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Consolidate RelayCommand detection logic into shared BindingSourceGen code Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Remove test files that cannot work in test environment Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Refactor RelayCommand logic into shared helper method Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Move RelayCommand logic to PathParser with special case handling Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Simplify TryGetProperty naming and add RelayCommand unit tests Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Enable expression-based bindings with RelayCommand support Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> Co-authored-by: Šimon Rozsíval <simon@rozsival.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ings (#32954) * Initial plan * Add RelayCommand support to binding source generators Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Consolidate RelayCommand detection logic into shared BindingSourceGen code Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Remove test files that cannot work in test environment Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Refactor RelayCommand logic into shared helper method Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Move RelayCommand logic to PathParser with special case handling Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Simplify TryGetProperty naming and add RelayCommand unit tests Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Enable expression-based bindings with RelayCommand support Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> Co-authored-by: Šimon Rozsíval <simon@rozsival.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ings (#32954) * Initial plan * Add RelayCommand support to binding source generators Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Consolidate RelayCommand detection logic into shared BindingSourceGen code Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Remove test files that cannot work in test environment Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Refactor RelayCommand logic into shared helper method Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Move RelayCommand logic to PathParser with special case handling Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Simplify TryGetProperty naming and add RelayCommand unit tests Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Enable expression-based bindings with RelayCommand support Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> Co-authored-by: Šimon Rozsíval <simon@rozsival.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
## CollectionView - Fixed the NRE in CarouselViewController on iOS 15.5 & 16.4 by @Ahamed-Ali in #30838 <details> <summary>🔧 Fixes</summary> - [NRE in CarouselViewController on iOS 15.5 & 16.4](#28557) </details> - [iOS, macOS] Fixed CollectionView group header size changes with ItemSizingStrategy by @NanthiniMahalingam in #33161 <details> <summary>🔧 Fixes</summary> - [[NET 10] I6_Grouping - Grouping_with_variable_sized_items changing the 'ItemSizingStrategy' also changes the header size.](#33130) </details> ## Flyout - Add unit tests for TabBar and FlyoutItem navigation ApplyQueryAttributes (#25663) by @StephaneDelcroix in #33006 ## Flyoutpage - Fixed the FlyoutPage.Flyout Disappearing When Maximizing the Window on Mac Platform by @NanthiniMahalingam in #26701 <details> <summary>🔧 Fixes</summary> - [FlyoutPage.Flyout - navigation corrupted when running om mac , on window ok](#22719) </details> ## Mediapicker - [Windows] Fix for PickPhotosAsync throws exception if image is modified by @HarishwaranVijayakumar in #32952 <details> <summary>🔧 Fixes</summary> - [PickPhotosAsync throws exception if image is modified.](#32408) </details> ## Navigation - Fix for TabBar Navigation does not invoke its IQueryAttributable.ApplyQueryAttributes(query) by @SuthiYuvaraj in #25663 <details> <summary>🔧 Fixes</summary> - [Tabs defined in AppShell.xaml does not invoke its view model's IQueryAttributable.ApplyQueryAttributes(query) implementaion](#13537) - [`ShellContent` routes do not call `ApplyQueryAttributes`](#28453) </details> ## ScrollView - Fix ScrollToPosition.Center behavior in ScrollView on iOS and MacCatalyst by @devanathan-vaithiyanathan in #26825 <details> <summary>🔧 Fixes</summary> - [ScrollToPosition.Center Centers the First Item too in iOS and Catalyst](#26760) - [On iOS - ScrollView.ScrollToAsync Element, ScrollToPosition.MakeVisible shifts view to the right, instead of just scrolling vertically](#28965) </details> ## Searchbar - [iOS, Mac, Windows] Fixed CharacterSpacing for SearchBar text and placeholder text by @Dhivya-SF4094 in #30407 <details> <summary>🔧 Fixes</summary> - [[iOS, Mac, Windows] SearchBar CharacterSpacing property is not working as expected](#30366) </details> ## Shell - Update logic for large title display mode on iOS - shell by @kubaflo in #33039 ## TitleView - [iOS] Fixed memory leak with PopToRootAsync when using TitleView by @Vignesh-SF3580 in #28547 <details> <summary>🔧 Fixes</summary> - [NavigationPage.TitleView causes memory leak with PopToRootAsync](#28201) </details> ## Xaml - [C] Fix binding to interface-inherited properties like IReadOnlyList<T>.Count by @StephaneDelcroix in #32912 <details> <summary>🔧 Fixes</summary> - [Compiled Binding to Array.Count provides no result](#13872) </details> - Fix #31939: CommandParameter TemplateBinding lost during reparenting by @StephaneDelcroix in #32961 <details> <summary>🔧 Fixes</summary> - [CommandParameter TemplateBinding Lost During ControlTemplate Reparenting](#31939) </details> <details> <summary>🧪 Testing (4)</summary> - [Testing] Fixed Test case failure in PR 33185 - [12/22/2025] Candidate by @TamilarasanSF4853 in #33257 - [Testing] Re-saved ShouldFlyoutBeVisibleAfterMaximizingWindow test case images in PR 33185 - [12/22/2025] Candidate by @TamilarasanSF4853 in #33271 - [Testing] Fixed Test case failure in PR 33185 - [12/22/2025] Candidate - 2 by @TamilarasanSF4853 in #33299 - [Testing] Fixed Test case failure in PR 33185 - [12/22/2025] Candidate - 3 by @TamilarasanSF4853 in #33311 </details> <details> <summary>📦 Other (2)</summary> - [XSG][BindingSourceGen] Add support for RelayCommand to compiled bindings by @simonrozsival via @Copilot in #32954 <details> <summary>🔧 Fixes</summary> - [Issue #25818](#25818) </details> - Revert "Update logic for large title display mode on iOS - shell (#33039)" in cff7f35 </details> **Full Changelog**: main...inflight/candidate
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 of Change
Both XAML and C# compiled bindings now support properties generated by CommunityToolkit.Mvvm's
[RelayCommand]attribute. Previously, bindings to source-generated command properties failed because the MAUI source generators couldn't see properties generated by other source generators.Implementation:
GetLambdaReturnType()inBindingSourceGenerator.csto infer property type from RelayCommand methods when the semantic model can't resolve it, enabling C# lambda binding supportTryHandleSpecialCases()method inPathParser.csthat serves as a fallback whenMemberAccessExpressionSyntaxtype or symbol are not known at compile timeTryGetRelayCommandPropertyType()inBindingSourceGen/ITypeSymbolExtensions.csthat detects methods with[RelayCommand]attribute and infers the corresponding command property type asICommandPathParser'sHandleMemberAccessExpressionto check for RelayCommand patterns when symbol resolution failsTryGetProperty()helper method inSourceGen/ITypeSymbolExtensions.csfor XAML path parsingCompiledBindingMarkup.cspath parsing to use the shared helper method, treating RelayCommand-inferred properties as regularMemberAccesspath partsRelayCommandAttributeto validate the implementationScope:
Works for both XAML string-based bindings (
{Binding SaveCommand}) and C# lambda bindings (static (VM vm) => vm.SaveCommand).Example:
Issues Fixed
Fixes #25818
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.