Skip to content

Commit 5fcca7d

Browse files
committed
Fix XAML source gen for source-generated bindable properties in Style Setters
When the XAML source generator encounters a Style Setter that references a source-generated bindable property (e.g., from [BindableProperty] attribute), the GetBindableProperty method returns null because the property metadata isn't available in the same way as for manually-defined bindable properties. This change adds a fallback mechanism in GetBindablePropertyNameAndType that: 1. Parses the property name from the Setter's Property attribute 2. Uses HasBindablePropertyHeuristic to detect if a bindable property exists 3. Constructs the fully-qualified property name directly This fixes issue where using source-generated bindable properties in ResourceDictionary styles would fail with 'MAUIG1001: The method or operation is not implemented' error. Fixes #xxxxx
1 parent cc11d9b commit 5fcca7d

1 file changed

Lines changed: 58 additions & 5 deletions

File tree

src/Controls/src/SourceGen/SetterValueProvider.cs

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ public bool TryProvideValue(ElementNode node, IndentedTextWriter writer, SourceG
4343
}
4444

4545
var bpNode = (ValueNode)node.Properties[new XmlName("", "Property")];
46-
var bpRef = bpNode.GetBindableProperty(context);
46+
IFieldSymbol? bpRef = bpNode.GetBindableProperty(context);
47+
var (bpName, bpType) = GetBindablePropertyNameAndType(bpRef, bpNode, context);
4748

4849
string targetsetter;
4950
if (node.Properties.TryGetValue(new XmlName("", "TargetName"), out var targetNode))
@@ -53,18 +54,21 @@ public bool TryProvideValue(ElementNode node, IndentedTextWriter writer, SourceG
5354

5455
if (valueNode is ValueNode vn)
5556
{
56-
value = $"new global::Microsoft.Maui.Controls.Setter {{{targetsetter}Property = {bpRef.ToFQDisplayString()}, Value = {vn.ConvertTo(bpRef, writer, context)}}}";
57+
var valueString = bpRef != null
58+
? vn.ConvertTo(bpRef, writer, context)
59+
: (bpType != null ? vn.ConvertTo(bpType, null, writer, context) : string.Empty);
60+
value = $"new global::Microsoft.Maui.Controls.Setter {{{targetsetter}Property = {bpName}, Value = {valueString}}}";
5761
return true;
5862
}
5963
else if (getNodeValue != null)
6064
{
61-
var lvalue = getNodeValue(valueNode, bpRef.Type);
62-
value = $"new global::Microsoft.Maui.Controls.Setter {{{targetsetter}Property = {bpRef.ToFQDisplayString()}, Value = {lvalue.ValueAccessor}}}";
65+
var lvalue = getNodeValue(valueNode, bpType ?? context.Compilation.ObjectType);
66+
value = $"new global::Microsoft.Maui.Controls.Setter {{{targetsetter}Property = {bpName}, Value = {lvalue.ValueAccessor}}}";
6367
return true;
6468
}
6569
else if (context.Variables.TryGetValue(valueNode, out var variable))
6670
{
67-
value = $"new global::Microsoft.Maui.Controls.Setter {{{targetsetter}Property = {bpRef.ToFQDisplayString()}, Value = {variable.ValueAccessor}}}";
71+
value = $"new global::Microsoft.Maui.Controls.Setter {{{targetsetter}Property = {bpName}, Value = {variable.ValueAccessor}}}";
6872
return true;
6973
}
7074

@@ -73,6 +77,55 @@ public bool TryProvideValue(ElementNode node, IndentedTextWriter writer, SourceG
7377
return false;
7478
}
7579

80+
private static (string bpName, ITypeSymbol? bpType) GetBindablePropertyNameAndType(IFieldSymbol? bpRef, ValueNode bpNode, SourceGenContext context)
81+
{
82+
if (bpRef != null)
83+
return (bpRef.ToFQDisplayString(), bpRef.GetBPTypeAndConverter(context)?.type);
84+
85+
static ITypeSymbol? GetTargetTypeSymbol(INode node, SourceGenContext context)
86+
{
87+
var ttnode = (node as ElementNode)?.Properties[new XmlName("", "TargetType")];
88+
if (ttnode is ValueNode { Value: string tt })
89+
return XmlTypeExtensions.GetTypeSymbol(tt, context, node);
90+
if (ttnode != null && context.Types.TryGetValue(ttnode, out var typeSymbol))
91+
return typeSymbol;
92+
return null;
93+
}
94+
95+
var propertyText = bpNode.Value as string ?? string.Empty;
96+
var parts = propertyText.Split('.');
97+
98+
ITypeSymbol? targetType = null;
99+
string propertyName;
100+
101+
if (parts.Length == 1)
102+
{
103+
propertyName = parts[0];
104+
var parent = bpNode.Parent?.Parent as ElementNode ?? (bpNode.Parent?.Parent as IListNode)?.Parent as ElementNode;
105+
if (parent?.XmlType.IsOfAnyType("Trigger", "DataTrigger", "MultiTrigger", "Style") == true)
106+
targetType = GetTargetTypeSymbol(parent, context);
107+
}
108+
else if (parts.Length == 2)
109+
{
110+
targetType = XmlTypeExtensions.GetTypeSymbol(parts[0], context, bpNode);
111+
propertyName = parts[1];
112+
}
113+
else
114+
{
115+
return ("null", null);
116+
}
117+
118+
if (targetType != null && targetType.HasBindablePropertyHeuristic(propertyName, context, out var explicitPropertyName))
119+
{
120+
var bpFieldName = explicitPropertyName ?? $"{propertyName}Property";
121+
var bpName = $"{targetType.ToFQDisplayString()}.{bpFieldName}";
122+
var bpType = targetType.GetAllProperties(propertyName, context).FirstOrDefault()?.Type;
123+
return (bpName, bpType);
124+
}
125+
126+
return ("null", null);
127+
}
128+
76129
/// <summary>
77130
/// Shared helper to get the value node from a Setter element.
78131
/// Checks properties first, then collection items.

0 commit comments

Comments
 (0)