Skip to content

Conversation

@StephaneDelcroix
Copy link
Contributor

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

This PR adds support for C# 14 extension properties in XAML for both setting properties directly and using them as binding sources.

What are C# 14 extension properties?

C# 14 introduces extension properties, allowing you to define properties that extend existing types:

public static class LabelExtensions
{
    extension(Label label)
    {
        public string MyTag { get; set; }
        public int MyPriority { get; set; }
    }
}

Changes

Runtime Inflator (ApplyPropertiesVisitor.cs)

  • Added FindExtensionPropertyMethods to search loaded assemblies for extension property getter/setter methods
  • Added TrySetExtensionProperty and TryGetExtensionProperty methods
  • Modified TrySetProperty/TryGetProperty to fallback to extension properties when regular properties are not found

XamlC Build Tasks (SetPropertiesVisitor.cs)

  • Added FindExtensionPropertyMethods using Mono.Cecil to find extension property methods in referenced assemblies
  • Added CanSetExtensionProperty, SetExtensionProperty, CanGetExtensionProperty, and GetExtensionProperty methods
  • Generates IL code to call static extension property getter/setter methods

SourceGen (SetPropertyHelpers.cs, NodeSGExtensions.cs)

  • Added FindExtensionPropertyMethods using Roslyn to find extension property symbols
  • Added CanConvertTo overload for ITypeSymbol
  • Added CanSetExtensionProperty and SetExtensionProperty methods
  • Note: SourceGen support is implemented but needs additional integration work (tests currently use .rtxc.xaml to skip SourceGen)

Usage Examples

Setting Extension Properties from XAML

<Label MyTag="hello from extension property" MyPriority="42" />

Using Extension Properties in Bindings

// ViewModel with extension property
public static class PersonExtensions
{
    extension(PersonModel person)
    {
        public string FullName => $"{person.FirstName} {person.LastName}";
    }
}
<Label Text="{Binding Person.FullName}" />

Tests

  • Added ExtensionProperties.rtxc.xaml tests for setting extension properties from XAML
  • Added ExtensionPropertiesBinding.xaml tests for binding scenarios
  • All 1778 XAML unit tests pass ✅
  • All 99 SourceGen unit tests pass ✅

Issues Fixed

N/A - This is a new feature enabling C# 14 extension properties in XAML.

This PR adds support for C# 14 extension properties in XAML for both
setting properties and using them in binding sources.

## Changes

### Runtime Inflator (ApplyPropertiesVisitor.cs)
- Added FindExtensionPropertyMethods to search loaded assemblies
- Added TrySetExtensionProperty and TryGetExtensionProperty methods
- Modified TrySetProperty/TryGetProperty to fallback to extension properties

### XamlC Build Tasks (SetPropertiesVisitor.cs)
- Added FindExtensionPropertyMethods using Mono.Cecil
- Added CanSetExtensionProperty, SetExtensionProperty methods
- Added CanGetExtensionProperty, GetExtensionProperty methods
- Generates IL code to call static extension property getter/setter

### SourceGen (SetPropertyHelpers.cs, NodeSGExtensions.cs)
- Added FindExtensionPropertyMethods using Roslyn
- Added CanConvertTo overload for ITypeSymbol
- Added CanSetExtensionProperty and SetExtensionProperty methods
- Note: SourceGen support needs additional integration work

## Usage

Extension properties can now be set directly in XAML:
```xml
<Label MyTag="hello" MyPriority="42" />
```

And used in bindings:
```xml
<Label Text="{Binding Person.FullName}" />
```

Where FullName is a C# 14 extension property on PersonModel.

## Tests
- Added ExtensionProperties.rtxc.xaml tests for setting properties
- Added ExtensionPropertiesBinding.xaml tests for binding scenarios
- All 1778 XAML unit tests pass
- All 99 SourceGen unit tests pass
- Remove nested type check from FindExtensionPropertyMethods (not needed)
- Update test comments to explain why SourceGen cannot yet support C# 14
  extension members (Roslyn needs to fully lower extension blocks before
  the semantic model exposes the generated accessor methods)
- Updated FindExtensionProperty to find extension properties directly on
  static classes (Roslyn exposes them as IPropertySymbol with get_X/set_X
  accessor methods that take the target type as the first parameter)
- Removed ExtensionAttribute requirement since C# 14 extension blocks don't
  require it in the current Roslyn implementation
- Updated tests to use all three inflators ([Values] instead of explicit list)
- Renamed ExtensionProperties.rtxc.xaml to ExtensionProperties.xaml

All 1783 XAML unit tests pass
All 100 SourceGen unit tests pass
…rmance

- Cache list of all static types per context (expensive enumeration once)
- Cache individual extension property lookups by (targetType, propertyName)
- Cache extension property method lookups by (targetType, propertyName)
- Use SymbolEqualityComparer for cache key comparison

Performance results (Controls.Xaml.UnitTests build):
- Before: 27.81s (baseline without extension property feature)
- After:  16.78s (~40% faster than baseline)

The caching makes the extension property feature actually improve build times
because the static type enumeration is now shared across all lookups.
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.

2 participants