[XSG] Add heuristic to support bindable properties generated by other source generators#32597
Conversation
|
@copilot Seems alot of code changes here not related with fixes, can you just commit the changes for the issue in question. |
Removed the formatting commit (bc7ccd0). The PR now contains only the 3 files directly related to the fix:
Commit 52d2097 |
simonrozsival
left a comment
There was a problem hiding this comment.
@copilot you only added tests for AutoPropertyAttribute. I would also like to see tests for BindablePropertyAttribute. Change those tests into [Theory], ideally just one, and add parameters to test all the relevant inputs and their expected outputs.
src/Controls/tests/SourceGen.UnitTests/InitializeComponent/BindablePropertyHeuristic.cs
Outdated
Show resolved
Hide resolved
src/Controls/tests/SourceGen.UnitTests/InitializeComponent/BindablePropertyHeuristic.cs
Outdated
Show resolved
Hide resolved
Consolidated tests into a Theory with 6 InlineData test cases covering all combinations of BindablePropertyAttribute/AutoPropertyAttribute with properties/fields and with/without PropertyName parameter. Added 3 additional Fact tests for edge cases (missing attribute, mismatched names, literal values). All tests verify the correct SetBinding code is generated. Commit 7d9969c |
simonrozsival
left a comment
There was a problem hiding this comment.
@copilot please make one more iteration
src/Controls/tests/SourceGen.UnitTests/InitializeComponent/BindablePropertyHeuristic.cs
Outdated
Show resolved
Hide resolved
src/Controls/tests/SourceGen.UnitTests/InitializeComponent/BindablePropertyHeuristic.cs
Outdated
Show resolved
Hide resolved
src/Controls/tests/SourceGen.UnitTests/InitializeComponent/BindablePropertyHeuristic.cs
Outdated
Show resolved
Hide resolved
src/Controls/tests/SourceGen.UnitTests/InitializeComponent/BindablePropertyHeuristic.cs
Outdated
Show resolved
Hide resolved
- Split Theory test into separate tests for properties and fields for better clarity - Merge duplicate if statements for BindablePropertyAttribute and AutoPropertyAttribute checks - All 64 tests passing (53 original + 11 new) Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
- Always declare PropertyName as string? in attribute definitions - Add parameter to test field name variations (_balance, balance) - All 66 tests passing (53 original + 13 new) Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
- Add null/empty checks to DerivePotentialPropertyNames and DerivePotentialFieldNames to prevent IndexOutOfRangeException - Support field names with uppercase first letter (uncommon but now supported) - All 66 tests passing Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
…erties - Modified heuristic logic to handle both BindingBase and literal values - Added SetValueWithHeuristic method to generate SetValue calls - Updated test to verify SetValue is used instead of property setter - Added test to ensure property setter is still used when no attribute present - All 67 tests passing (53 original + 14 heuristic tests) Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
- Heuristic now evaluated after BP checks but before property setter - Ensures source-generated BPs use SetBinding/SetValue instead of setter - Prevents heuristic from being applied too early - All 67 tests passing Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
- Replace suffix matching with full name checking - Support recognized attributes: CommunityToolkit.Maui, SQuan.Helpers.Maui.Mvvm, Maui.BindableProperty.Generator.Core - Add flexible suffix matching for other libraries (e.g., Test.BindablePropertyAttribute) - All 67 tests passing (53 original + 14 heuristic tests) Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
- Only check exact full attribute names from the recognized list - Add Test.BindablePropertyAttribute and Test.AutoPropertyAttribute for unit tests - All 67 tests passing (53 original + 14 heuristic tests) Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
- Move heuristic logic from separate block in SetPropertyValue into Can* methods - CanSetBinding now checks heuristic when BP is null and value is BindingBase - CanSetValue now checks heuristic when BP is null and value is not BindingBase - SetPropertyValue structure unchanged - still just calls Can* methods - SetBinding and SetValue methods updated to handle heuristic case - All existing tests passing Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
… in tests - Remove IsBindingBaseNode method - inline the check in CanSetBinding - Simplify SetBinding and SetValue to reduce duplication using ternary expressions - Replace Test.BindablePropertyAttribute with CommunityToolkit.Maui.BindablePropertyAttribute in tests - Replace Test.AutoPropertyAttribute with Maui.BindableProperty.Generator.Core.AutoBindableAttribute in tests - Update recognized attributes list to remove Test.* entries - All 67 tests passing Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
- Fix null reference error on line 320 (use bpName instead of bpFieldSymbol.ToFQDisplayString()) - Move heuristic check in CanSetValue from inside if(bpFieldSymbol != null) to else block - Add null/empty check for localName in CanSetBinding heuristic - Update tests to use real namespace attributes (CommunityToolkit.Maui, Maui.BindableProperty.Generator.Core) Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
- Changed tests to use block-style namespace syntax instead of file-scoped - Changed BalanceView from partial class with partial properties to regular class - Removed partial keyword from properties since that requires C# 13 implementation - All 14 BindablePropertyHeuristic tests pass - All 100 SourceGen.UnitTests pass - All 1746 Xaml.UnitTests pass Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
10b568d to
1cce733
Compare
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
|
/azp run MAUI-UITests-public |
|
Azure Pipelines successfully started running 1 pipeline(s). |
…blePropertyAttribute (#33028) > [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Description of Change Binding source generators now support properties generated by CommunityToolkit.Mvvm's `[ObservableProperty]` attribute. Previously, bindings failed because MAUI generators cannot see properties generated by other source generators. **Implementation** (following PR #32954 pattern for RelayCommand): - **BindingSourceGen/ITypeSymbolExtensions.cs**: Added `TryGetObservablePropertyType()` to detect fields with `[ObservableProperty]` and infer property types. Supports all three documented field naming patterns: `camelCase`, `_camelCase`, and `m_camelCase`. Includes validation for empty property names. - **BindingSourceGen/PathParser.cs**: Enhanced `TryHandleSpecialCases()` to handle ObservableProperty patterns as fallback when symbol resolution fails. - **BindingSourceGen/BindingSourceGenerator.cs**: Updated `GetLambdaReturnType()` to infer types from ObservableProperty fields, enabling C# lambda bindings. - **SourceGen/ITypeSymbolExtensions.cs**: Updated `TryGetProperty()` to check ObservableProperty-inferred properties, enabling XAML string-based bindings. - **Tests**: Added comprehensive unit tests in `BindingSourceGen.UnitTests` (8 tests) to validate ObservableProperty binding support across all field naming patterns and scenarios. Tests verify the generated binding code by checking: - Handler patterns: `new(static source => source, "PropertyName")` - validates property path in generated code - Setter expressions: `source.PropertyName = value;` - validates property assignment **Field Naming Pattern Support:** All three CommunityToolkit.Mvvm documented naming conventions are supported: - `name` → `Name` - `_name` → `Name` - `m_name` → `Name` **Works for both XAML and C# bindings:** ```csharp public class MyViewModel { [ObservableProperty] private string? name; // Generates Name property [ObservableProperty] private ObservableCollection<Tag> _tags = new(); // Generates Tags property [ObservableProperty] private int m_count; // Generates Count property } ``` ```xaml <Label Text="{Binding Name}" /> <ListView ItemsSource="{Binding Tags}" /> ``` ```csharp label.SetBinding(Label.TextProperty, static (MyViewModel vm) => vm.Name); listView.SetBinding(ListView.ItemsSourceProperty, static (MyViewModel vm) => vm.Tags); ``` ### Issues Fixed Fixes #32597 <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>[XSG][BindingSourceGen] Add support for CommunityToolkit.Mvvm ObservablePropertyAttribute source generator</issue_title> > <issue_description>Similar to #32597 > Related to #32953 > > The CommunityToolkit.Mvvm package contains a source generator which generates bindable property bolierplate code based on the `[ObservableProperty]` attribute: https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/observableproperty > > Scenarios: > ```c# > // applied to a field > [ObservableProperty] > private string? name; > > // generated code: > /// <inheritdoc cref="_tags"/> > [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.3.0.0")] > [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] > public global::System.Collections.ObjectModel.ObservableCollection<global::TestSampleContentBindings.Models.Tag> Tags > { > get => _tags; > [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_tags")] > set > { > if (!global::System.Collections.Generic.EqualityComparer<global::System.Collections.ObjectModel.ObservableCollection<global::TestSampleContentBindings.Models.Tag>>.Default.Equals(_tags, value)) > { > OnTagsChanging(value); > OnTagsChanging(default, value); > OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Tags); > _tags = value; > OnTagsChanged(value); > OnTagsChanged(default, value); > OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Tags); > } > } > } > ``` > > Our source generators cannot see properties generated by other source generators, so the binding compilation will fail. > > Our sample content template uses this source generator and we are currently failing over to reflection-based bindings.</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #32955 <!-- START COPILOT CODING AGENT TIPS --> --- 💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey). --------- 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: Simon Rozsival <simon@rozsival.com>
…blePropertyAttribute (#33028) > [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Description of Change Binding source generators now support properties generated by CommunityToolkit.Mvvm's `[ObservableProperty]` attribute. Previously, bindings failed because MAUI generators cannot see properties generated by other source generators. **Implementation** (following PR #32954 pattern for RelayCommand): - **BindingSourceGen/ITypeSymbolExtensions.cs**: Added `TryGetObservablePropertyType()` to detect fields with `[ObservableProperty]` and infer property types. Supports all three documented field naming patterns: `camelCase`, `_camelCase`, and `m_camelCase`. Includes validation for empty property names. - **BindingSourceGen/PathParser.cs**: Enhanced `TryHandleSpecialCases()` to handle ObservableProperty patterns as fallback when symbol resolution fails. - **BindingSourceGen/BindingSourceGenerator.cs**: Updated `GetLambdaReturnType()` to infer types from ObservableProperty fields, enabling C# lambda bindings. - **SourceGen/ITypeSymbolExtensions.cs**: Updated `TryGetProperty()` to check ObservableProperty-inferred properties, enabling XAML string-based bindings. - **Tests**: Added comprehensive unit tests in `BindingSourceGen.UnitTests` (8 tests) to validate ObservableProperty binding support across all field naming patterns and scenarios. Tests verify the generated binding code by checking: - Handler patterns: `new(static source => source, "PropertyName")` - validates property path in generated code - Setter expressions: `source.PropertyName = value;` - validates property assignment **Field Naming Pattern Support:** All three CommunityToolkit.Mvvm documented naming conventions are supported: - `name` → `Name` - `_name` → `Name` - `m_name` → `Name` **Works for both XAML and C# bindings:** ```csharp public class MyViewModel { [ObservableProperty] private string? name; // Generates Name property [ObservableProperty] private ObservableCollection<Tag> _tags = new(); // Generates Tags property [ObservableProperty] private int m_count; // Generates Count property } ``` ```xaml <Label Text="{Binding Name}" /> <ListView ItemsSource="{Binding Tags}" /> ``` ```csharp label.SetBinding(Label.TextProperty, static (MyViewModel vm) => vm.Name); listView.SetBinding(ListView.ItemsSourceProperty, static (MyViewModel vm) => vm.Tags); ``` ### Issues Fixed Fixes #32597 <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>[XSG][BindingSourceGen] Add support for CommunityToolkit.Mvvm ObservablePropertyAttribute source generator</issue_title> > <issue_description>Similar to #32597 > Related to #32953 > > The CommunityToolkit.Mvvm package contains a source generator which generates bindable property bolierplate code based on the `[ObservableProperty]` attribute: https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/observableproperty > > Scenarios: > ```c# > // applied to a field > [ObservableProperty] > private string? name; > > // generated code: > /// <inheritdoc cref="_tags"/> > [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.3.0.0")] > [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] > public global::System.Collections.ObjectModel.ObservableCollection<global::TestSampleContentBindings.Models.Tag> Tags > { > get => _tags; > [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_tags")] > set > { > if (!global::System.Collections.Generic.EqualityComparer<global::System.Collections.ObjectModel.ObservableCollection<global::TestSampleContentBindings.Models.Tag>>.Default.Equals(_tags, value)) > { > OnTagsChanging(value); > OnTagsChanging(default, value); > OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Tags); > _tags = value; > OnTagsChanged(value); > OnTagsChanged(default, value); > OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Tags); > } > } > } > ``` > > Our source generators cannot see properties generated by other source generators, so the binding compilation will fail. > > Our sample content template uses this source generator and we are currently failing over to reflection-based bindings.</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #32955 <!-- START COPILOT CODING AGENT TIPS --> --- 💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey). --------- 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: Simon Rozsival <simon@rozsival.com>
…blePropertyAttribute (#33028) > [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Description of Change Binding source generators now support properties generated by CommunityToolkit.Mvvm's `[ObservableProperty]` attribute. Previously, bindings failed because MAUI generators cannot see properties generated by other source generators. **Implementation** (following PR #32954 pattern for RelayCommand): - **BindingSourceGen/ITypeSymbolExtensions.cs**: Added `TryGetObservablePropertyType()` to detect fields with `[ObservableProperty]` and infer property types. Supports all three documented field naming patterns: `camelCase`, `_camelCase`, and `m_camelCase`. Includes validation for empty property names. - **BindingSourceGen/PathParser.cs**: Enhanced `TryHandleSpecialCases()` to handle ObservableProperty patterns as fallback when symbol resolution fails. - **BindingSourceGen/BindingSourceGenerator.cs**: Updated `GetLambdaReturnType()` to infer types from ObservableProperty fields, enabling C# lambda bindings. - **SourceGen/ITypeSymbolExtensions.cs**: Updated `TryGetProperty()` to check ObservableProperty-inferred properties, enabling XAML string-based bindings. - **Tests**: Added comprehensive unit tests in `BindingSourceGen.UnitTests` (8 tests) to validate ObservableProperty binding support across all field naming patterns and scenarios. Tests verify the generated binding code by checking: - Handler patterns: `new(static source => source, "PropertyName")` - validates property path in generated code - Setter expressions: `source.PropertyName = value;` - validates property assignment **Field Naming Pattern Support:** All three CommunityToolkit.Mvvm documented naming conventions are supported: - `name` → `Name` - `_name` → `Name` - `m_name` → `Name` **Works for both XAML and C# bindings:** ```csharp public class MyViewModel { [ObservableProperty] private string? name; // Generates Name property [ObservableProperty] private ObservableCollection<Tag> _tags = new(); // Generates Tags property [ObservableProperty] private int m_count; // Generates Count property } ``` ```xaml <Label Text="{Binding Name}" /> <ListView ItemsSource="{Binding Tags}" /> ``` ```csharp label.SetBinding(Label.TextProperty, static (MyViewModel vm) => vm.Name); listView.SetBinding(ListView.ItemsSourceProperty, static (MyViewModel vm) => vm.Tags); ``` ### Issues Fixed Fixes #32597 <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>[XSG][BindingSourceGen] Add support for CommunityToolkit.Mvvm ObservablePropertyAttribute source generator</issue_title> > <issue_description>Similar to #32597 > Related to #32953 > > The CommunityToolkit.Mvvm package contains a source generator which generates bindable property bolierplate code based on the `[ObservableProperty]` attribute: https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/observableproperty > > Scenarios: > ```c# > // applied to a field > [ObservableProperty] > private string? name; > > // generated code: > /// <inheritdoc cref="_tags"/> > [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.3.0.0")] > [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] > public global::System.Collections.ObjectModel.ObservableCollection<global::TestSampleContentBindings.Models.Tag> Tags > { > get => _tags; > [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_tags")] > set > { > if (!global::System.Collections.Generic.EqualityComparer<global::System.Collections.ObjectModel.ObservableCollection<global::TestSampleContentBindings.Models.Tag>>.Default.Equals(_tags, value)) > { > OnTagsChanging(value); > OnTagsChanging(default, value); > OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Tags); > _tags = value; > OnTagsChanged(value); > OnTagsChanged(default, value); > OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Tags); > } > } > } > ``` > > Our source generators cannot see properties generated by other source generators, so the binding compilation will fail. > > Our sample content template uses this source generator and we are currently failing over to reflection-based bindings.</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #32955 <!-- START COPILOT CODING AGENT TIPS --> --- 💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey). --------- 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: Simon Rozsival <simon@rozsival.com>
…blePropertyAttribute (#33028) > [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Description of Change Binding source generators now support properties generated by CommunityToolkit.Mvvm's `[ObservableProperty]` attribute. Previously, bindings failed because MAUI generators cannot see properties generated by other source generators. **Implementation** (following PR #32954 pattern for RelayCommand): - **BindingSourceGen/ITypeSymbolExtensions.cs**: Added `TryGetObservablePropertyType()` to detect fields with `[ObservableProperty]` and infer property types. Supports all three documented field naming patterns: `camelCase`, `_camelCase`, and `m_camelCase`. Includes validation for empty property names. - **BindingSourceGen/PathParser.cs**: Enhanced `TryHandleSpecialCases()` to handle ObservableProperty patterns as fallback when symbol resolution fails. - **BindingSourceGen/BindingSourceGenerator.cs**: Updated `GetLambdaReturnType()` to infer types from ObservableProperty fields, enabling C# lambda bindings. - **SourceGen/ITypeSymbolExtensions.cs**: Updated `TryGetProperty()` to check ObservableProperty-inferred properties, enabling XAML string-based bindings. - **Tests**: Added comprehensive unit tests in `BindingSourceGen.UnitTests` (8 tests) to validate ObservableProperty binding support across all field naming patterns and scenarios. Tests verify the generated binding code by checking: - Handler patterns: `new(static source => source, "PropertyName")` - validates property path in generated code - Setter expressions: `source.PropertyName = value;` - validates property assignment **Field Naming Pattern Support:** All three CommunityToolkit.Mvvm documented naming conventions are supported: - `name` → `Name` - `_name` → `Name` - `m_name` → `Name` **Works for both XAML and C# bindings:** ```csharp public class MyViewModel { [ObservableProperty] private string? name; // Generates Name property [ObservableProperty] private ObservableCollection<Tag> _tags = new(); // Generates Tags property [ObservableProperty] private int m_count; // Generates Count property } ``` ```xaml <Label Text="{Binding Name}" /> <ListView ItemsSource="{Binding Tags}" /> ``` ```csharp label.SetBinding(Label.TextProperty, static (MyViewModel vm) => vm.Name); listView.SetBinding(ListView.ItemsSourceProperty, static (MyViewModel vm) => vm.Tags); ``` ### Issues Fixed Fixes #32597 <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>[XSG][BindingSourceGen] Add support for CommunityToolkit.Mvvm ObservablePropertyAttribute source generator</issue_title> > <issue_description>Similar to #32597 > Related to #32953 > > The CommunityToolkit.Mvvm package contains a source generator which generates bindable property bolierplate code based on the `[ObservableProperty]` attribute: https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/observableproperty > > Scenarios: > ```c# > // applied to a field > [ObservableProperty] > private string? name; > > // generated code: > /// <inheritdoc cref="_tags"/> > [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.3.0.0")] > [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] > public global::System.Collections.ObjectModel.ObservableCollection<global::TestSampleContentBindings.Models.Tag> Tags > { > get => _tags; > [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_tags")] > set > { > if (!global::System.Collections.Generic.EqualityComparer<global::System.Collections.ObjectModel.ObservableCollection<global::TestSampleContentBindings.Models.Tag>>.Default.Equals(_tags, value)) > { > OnTagsChanging(value); > OnTagsChanging(default, value); > OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Tags); > _tags = value; > OnTagsChanged(value); > OnTagsChanged(default, value); > OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Tags); > } > } > } > ``` > > Our source generators cannot see properties generated by other source generators, so the binding compilation will fail. > > Our sample content template uses this source generator and we are currently failing over to reflection-based bindings.</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #32955 <!-- START COPILOT CODING AGENT TIPS --> --- 💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey). --------- 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: Simon Rozsival <simon@rozsival.com>
…blePropertyAttribute (#33028) > [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Description of Change Binding source generators now support properties generated by CommunityToolkit.Mvvm's `[ObservableProperty]` attribute. Previously, bindings failed because MAUI generators cannot see properties generated by other source generators. **Implementation** (following PR #32954 pattern for RelayCommand): - **BindingSourceGen/ITypeSymbolExtensions.cs**: Added `TryGetObservablePropertyType()` to detect fields with `[ObservableProperty]` and infer property types. Supports all three documented field naming patterns: `camelCase`, `_camelCase`, and `m_camelCase`. Includes validation for empty property names. - **BindingSourceGen/PathParser.cs**: Enhanced `TryHandleSpecialCases()` to handle ObservableProperty patterns as fallback when symbol resolution fails. - **BindingSourceGen/BindingSourceGenerator.cs**: Updated `GetLambdaReturnType()` to infer types from ObservableProperty fields, enabling C# lambda bindings. - **SourceGen/ITypeSymbolExtensions.cs**: Updated `TryGetProperty()` to check ObservableProperty-inferred properties, enabling XAML string-based bindings. - **Tests**: Added comprehensive unit tests in `BindingSourceGen.UnitTests` (8 tests) to validate ObservableProperty binding support across all field naming patterns and scenarios. Tests verify the generated binding code by checking: - Handler patterns: `new(static source => source, "PropertyName")` - validates property path in generated code - Setter expressions: `source.PropertyName = value;` - validates property assignment **Field Naming Pattern Support:** All three CommunityToolkit.Mvvm documented naming conventions are supported: - `name` → `Name` - `_name` → `Name` - `m_name` → `Name` **Works for both XAML and C# bindings:** ```csharp public class MyViewModel { [ObservableProperty] private string? name; // Generates Name property [ObservableProperty] private ObservableCollection<Tag> _tags = new(); // Generates Tags property [ObservableProperty] private int m_count; // Generates Count property } ``` ```xaml <Label Text="{Binding Name}" /> <ListView ItemsSource="{Binding Tags}" /> ``` ```csharp label.SetBinding(Label.TextProperty, static (MyViewModel vm) => vm.Name); listView.SetBinding(ListView.ItemsSourceProperty, static (MyViewModel vm) => vm.Tags); ``` ### Issues Fixed Fixes #32597 <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>[XSG][BindingSourceGen] Add support for CommunityToolkit.Mvvm ObservablePropertyAttribute source generator</issue_title> > <issue_description>Similar to #32597 > Related to #32953 > > The CommunityToolkit.Mvvm package contains a source generator which generates bindable property bolierplate code based on the `[ObservableProperty]` attribute: https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/observableproperty > > Scenarios: > ```c# > // applied to a field > [ObservableProperty] > private string? name; > > // generated code: > /// <inheritdoc cref="_tags"/> > [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.3.0.0")] > [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] > public global::System.Collections.ObjectModel.ObservableCollection<global::TestSampleContentBindings.Models.Tag> Tags > { > get => _tags; > [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_tags")] > set > { > if (!global::System.Collections.Generic.EqualityComparer<global::System.Collections.ObjectModel.ObservableCollection<global::TestSampleContentBindings.Models.Tag>>.Default.Equals(_tags, value)) > { > OnTagsChanging(value); > OnTagsChanging(default, value); > OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Tags); > _tags = value; > OnTagsChanged(value); > OnTagsChanged(default, value); > OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Tags); > } > } > } > ``` > > Our source generators cannot see properties generated by other source generators, so the binding compilation will fail. > > Our sample content template uses this source generator and we are currently failing over to reflection-based bindings.</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #32955 <!-- START COPILOT CODING AGENT TIPS --> --- 💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey). --------- 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: Simon Rozsival <simon@rozsival.com>
…blePropertyAttribute (#33028) > [!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](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Description of Change Binding source generators now support properties generated by CommunityToolkit.Mvvm's `[ObservableProperty]` attribute. Previously, bindings failed because MAUI generators cannot see properties generated by other source generators. **Implementation** (following PR #32954 pattern for RelayCommand): - **BindingSourceGen/ITypeSymbolExtensions.cs**: Added `TryGetObservablePropertyType()` to detect fields with `[ObservableProperty]` and infer property types. Supports all three documented field naming patterns: `camelCase`, `_camelCase`, and `m_camelCase`. Includes validation for empty property names. - **BindingSourceGen/PathParser.cs**: Enhanced `TryHandleSpecialCases()` to handle ObservableProperty patterns as fallback when symbol resolution fails. - **BindingSourceGen/BindingSourceGenerator.cs**: Updated `GetLambdaReturnType()` to infer types from ObservableProperty fields, enabling C# lambda bindings. - **SourceGen/ITypeSymbolExtensions.cs**: Updated `TryGetProperty()` to check ObservableProperty-inferred properties, enabling XAML string-based bindings. - **Tests**: Added comprehensive unit tests in `BindingSourceGen.UnitTests` (8 tests) to validate ObservableProperty binding support across all field naming patterns and scenarios. Tests verify the generated binding code by checking: - Handler patterns: `new(static source => source, "PropertyName")` - validates property path in generated code - Setter expressions: `source.PropertyName = value;` - validates property assignment **Field Naming Pattern Support:** All three CommunityToolkit.Mvvm documented naming conventions are supported: - `name` → `Name` - `_name` → `Name` - `m_name` → `Name` **Works for both XAML and C# bindings:** ```csharp public class MyViewModel { [ObservableProperty] private string? name; // Generates Name property [ObservableProperty] private ObservableCollection<Tag> _tags = new(); // Generates Tags property [ObservableProperty] private int m_count; // Generates Count property } ``` ```xaml <Label Text="{Binding Name}" /> <ListView ItemsSource="{Binding Tags}" /> ``` ```csharp label.SetBinding(Label.TextProperty, static (MyViewModel vm) => vm.Name); listView.SetBinding(ListView.ItemsSourceProperty, static (MyViewModel vm) => vm.Tags); ``` ### Issues Fixed Fixes #32597 <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>[XSG][BindingSourceGen] Add support for CommunityToolkit.Mvvm ObservablePropertyAttribute source generator</issue_title> > <issue_description>Similar to #32597 > Related to #32953 > > The CommunityToolkit.Mvvm package contains a source generator which generates bindable property bolierplate code based on the `[ObservableProperty]` attribute: https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/observableproperty > > Scenarios: > ```c# > // applied to a field > [ObservableProperty] > private string? name; > > // generated code: > /// <inheritdoc cref="_tags"/> > [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.3.0.0")] > [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] > public global::System.Collections.ObjectModel.ObservableCollection<global::TestSampleContentBindings.Models.Tag> Tags > { > get => _tags; > [global::System.Diagnostics.CodeAnalysis.MemberNotNull("_tags")] > set > { > if (!global::System.Collections.Generic.EqualityComparer<global::System.Collections.ObjectModel.ObservableCollection<global::TestSampleContentBindings.Models.Tag>>.Default.Equals(_tags, value)) > { > OnTagsChanging(value); > OnTagsChanging(default, value); > OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Tags); > _tags = value; > OnTagsChanged(value); > OnTagsChanged(default, value); > OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Tags); > } > } > } > ``` > > Our source generators cannot see properties generated by other source generators, so the binding compilation will fail. > > Our sample content template uses this source generator and we are currently failing over to reflection-based bindings.</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #32955 <!-- START COPILOT CODING AGENT TIPS --> --- 💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey). --------- 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: Simon Rozsival <simon@rozsival.com>
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
The XAML source generator fails with error MAUIX2002 when trying to bind to properties that have their BindableProperty field generated by another source generator (e.g., CommunityToolkit.Maui's
[BindableProperty]attribute), because source generators run independently and cannot see each other's output.This PR implements a heuristic to detect and support such properties, allowing bindings to work correctly.
Implementation Details:
The heuristic detects properties/fields with recognized bindable property attributes and generates appropriate code:
SetBindingcallSetValuecall (not property setter)PropertyNameparameter on attributes for explicit property name specificationCanSetBindingandCanSetValuemethods, keeping SetPropertyValue structure unchangedRecognized Attributes:
The heuristic recognizes these specific attribute types by their full names:
CommunityToolkit.Maui.BindablePropertyAttributeSQuan.Helpers.Maui.Mvvm.BindablePropertyAttributeMaui.BindableProperty.Generator.Core.AutoBindableAttributeThe list of recognized attributes is easily extensible by adding to the
RecognizedBindablePropertyAttributesarray. Only exact full name matches are supported to ensure specificity and prevent false positives.Name Matching Strategy:
The heuristic checks these name patterns:
Balance→ propertyBalanceBalance→ field_balanceBalance→ fieldbalanceBalance→ fieldBalanceFiles Changed (3 files):
src/Controls/src/SourceGen/ITypeSymbolExtensions.Maui.cs- Heuristic detection logic with helper methods and recognized attribute listsrc/Controls/src/SourceGen/SetPropertyHelpers.cs- Integration into CanSetBinding and CanSetValue methodssrc/Controls/tests/SourceGen.UnitTests/InitializeComponent/BindablePropertyHeuristic.cs- Comprehensive test coverage with real-world attribute namespaces (14 new tests)Code Structure:
The heuristic is cleanly integrated into the existing code structure:
CanSetBindingchecks the heuristic when BP is null and value is BindingBaseCanSetValuechecks the heuristic when BP is null and value is not BindingBaseSetBindingandSetValuehandle both normal BP case and heuristic case with minimal duplicationSetPropertyValuestructure unchanged - continues calling Can* methods in orderThis ensures:
Test Results:
Example:
Generated code:
Issues Fixed
Fixes #32596
Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.