diff --git a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.Execute.cs b/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.Execute.cs index bf5bdafcc..239efcf6d 100644 --- a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.Execute.cs +++ b/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.Execute.cs @@ -471,8 +471,10 @@ public static bool IsObjectSetCallbackImplemented(IPropertySymbol propertySymbol /// Gets the XamlBindingHelper.SetPropertyFrom* method name for a given property type, if supported. /// /// The input to check. + /// Whether to use the UWP XAML or WinUI 3 XAML namespaces. + /// The for the current run, used to probe for optional APIs. /// The method name to use, or if the type is not supported. - public static string? GetXamlBindingHelperSetMethodName(ITypeSymbol typeSymbol) + public static string? GetXamlBindingHelperSetMethodName(ITypeSymbol typeSymbol, bool useWindowsUIXaml, Compilation compilation) { // Check for well known primitive types first (these are the most common) switch (typeSymbol.SpecialType) @@ -491,17 +493,93 @@ public static bool IsObjectSetCallbackImplemented(IPropertySymbol propertySymbol default: break; } - // Check for the remaining well known WinRT projected types - if (typeSymbol.HasFullyQualifiedMetadataName("System.DateTimeOffset")) return "SetPropertyFromDateTime"; - if (typeSymbol.HasFullyQualifiedMetadataName("System.TimeSpan")) return "SetPropertyFromTimeSpan"; - if (typeSymbol.HasFullyQualifiedMetadataName("Windows.Foundation.Point")) return "SetPropertyFromPoint"; - if (typeSymbol.HasFullyQualifiedMetadataName("Windows.Foundation.Rect")) return "SetPropertyFromRect"; - if (typeSymbol.HasFullyQualifiedMetadataName("Windows.Foundation.Size")) return "SetPropertyFromSize"; - if (typeSymbol.HasFullyQualifiedMetadataName("System.Uri")) return "SetPropertyFromUri"; + // Check for the remaining well known WinRT projected types (always available on both UWP and WinAppSDK) + foreach ((string fullyQualifiedName, string methodName) in (ReadOnlySpan<(string, string)>)[ + ("System.DateTimeOffset", "SetPropertyFromDateTime"), + ("System.TimeSpan", "SetPropertyFromTimeSpan"), + ("Windows.Foundation.Point", "SetPropertyFromPoint"), + ("Windows.Foundation.Rect", "SetPropertyFromRect"), + ("Windows.Foundation.Size", "SetPropertyFromSize"), + ("System.Uri", "SetPropertyFromUri")]) + { + if (typeSymbol.HasFullyQualifiedMetadataName(fullyQualifiedName)) + { + return methodName; + } + } + + // UWP is not getting any new APIs in 'XamlBindingHelper', so if we didn't hit a match yet, we can stop here + if (useWindowsUIXaml) + { + return null; + } + + // The following types only have a corresponding 'SetPropertyFrom*' method on the WinAppSDK + // 'XamlBindingHelper'. The methods were also only added in newer WinAppSDK versions, so we + // additionally have to probe for their presence on the resolved type before emitting calls + // to them, to avoid breaking codegen for projects targeting older WinAppSDK releases. + foreach ((string fullyQualifiedName, string methodName) in (ReadOnlySpan<(string, string)>)[ + ("Windows.UI.Color", "SetPropertyFromColor"), + ("Microsoft.UI.Xaml.CornerRadius", "SetPropertyFromCornerRadius"), + ("Microsoft.UI.Xaml.Thickness", "SetPropertyFromThickness")]) + { + if (typeSymbol.HasFullyQualifiedMetadataName(fullyQualifiedName) && + HasXamlBindingHelperMethod(compilation, methodName, fullyQualifiedName)) + { + return methodName; + } + } return null; } + /// + /// Checks whether the WinAppSDK XamlBindingHelper type exposes a SetPropertyFrom* static method + /// with the expected (object, DependencyProperty, T) signature. + /// + /// The for the current run. + /// The name of the static method to look for. + /// The fully qualified metadata name of the third (value) parameter. + /// Whether the WinAppSDK XamlBindingHelper exposes a matching static method. + /// + /// This helper intentionally hardcodes the WinAppSDK XamlBindingHelper type, as it is only used + /// to probe for methods that don't exist on the UWP equivalent. Callers must therefore gate on + /// useWindowsUIXaml == false before invoking it. + /// + private static bool HasXamlBindingHelperMethod(Compilation compilation, string methodName, string valueTypeMetadataName) + { + INamedTypeSymbol? xamlBindingHelperType = compilation.GetTypeByMetadataName(WellKnownTypeNames.XamlBindingHelper(useWindowsUIXaml: false)); + + if (xamlBindingHelperType is null) + { + return false; + } + + // Match the expected 'static void Method(object, DependencyProperty, T)' shape. We validate the + // exact parameter types to guard against any future overload with the same name but different shape. + foreach (ISymbol member in xamlBindingHelperType.GetMembers(methodName)) + { + if (member is IMethodSymbol + { + IsStatic: true, + ReturnsVoid: true, + Parameters: + [ + { Type.SpecialType: SpecialType.System_Object }, + { Type: INamedTypeSymbol dependencyPropertyType }, + { Type: INamedTypeSymbol valueType } + ] + } && + dependencyPropertyType.HasFullyQualifiedMetadataName(WellKnownTypeNames.DependencyProperty(useWindowsUIXaml: false)) && + valueType.HasFullyQualifiedMetadataName(valueTypeMetadataName)) + { + return true; + } + } + + return false; + } + /// /// Gathers all forwarded attributes for the generated property. /// diff --git a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.cs b/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.cs index 3f6219eb4..66f9b5360 100644 --- a/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.cs +++ b/components/DependencyPropertyGenerator/CommunityToolkit.DependencyPropertyGenerator.SourceGenerators/DependencyPropertyGenerator.cs @@ -110,7 +110,10 @@ public void Initialize(IncrementalGeneratorInitializationContext context) // Get the optimized XamlBindingHelper method name for the property type, if applicable. // This is only used when the property type is not 'object' (which would gain nothing), // and the user hasn't provided their own 'OnSet(ref object)' implementation. - string? xamlBindingHelperSetMethodName = Execute.GetXamlBindingHelperSetMethodName(propertySymbol.Type); + string? xamlBindingHelperSetMethodName = Execute.GetXamlBindingHelperSetMethodName( + typeSymbol: propertySymbol.Type, + useWindowsUIXaml: useWindowsUIXaml, + compilation: context.SemanticModel.Compilation); if (xamlBindingHelperSetMethodName is not null) {