@@ -129,6 +129,107 @@ public static bool ProvideValueForTypeExtension(ElementNode node, IndentedTextWr
129129 return false ;
130130 }
131131
132+ public static bool ProvideValueForRelativeSourceExtension ( ElementNode markupNode , IndentedTextWriter writer , SourceGenContext context , NodeSGExtensions . GetNodeValueDelegate ? getNodeValue , out ITypeSymbol ? returnType , out string value )
133+ {
134+ returnType = context . Compilation . GetTypeByMetadataName ( "Microsoft.Maui.Controls.RelativeBindingSource" ) ! ;
135+
136+ // Get the Mode property value
137+ string ? modeStr = null ;
138+ if ( markupNode . Properties . TryGetValue ( new XmlName ( "" , "Mode" ) , out INode ? modeNode )
139+ || markupNode . Properties . TryGetValue ( new XmlName ( null , "Mode" ) , out modeNode ) )
140+ {
141+ modeStr = ( modeNode as ValueNode ) ? . Value as string ;
142+ }
143+ else if ( markupNode . CollectionItems . Count == 1 )
144+ {
145+ // ContentProperty is Mode
146+ modeStr = ( markupNode . CollectionItems [ 0 ] as ValueNode ) ? . Value as string ;
147+ }
148+
149+ // Get the AncestorType property (which is an x:Type extension)
150+ ITypeSymbol ? ancestorTypeSymbol = null ;
151+ if ( markupNode . Properties . TryGetValue ( new XmlName ( "" , "AncestorType" ) , out INode ? ancestorTypeNode )
152+ || markupNode . Properties . TryGetValue ( new XmlName ( null , "AncestorType" ) , out ancestorTypeNode ) )
153+ {
154+ if ( ancestorTypeNode is ElementNode typeExtNode && context . Types . TryGetValue ( typeExtNode , out ancestorTypeSymbol ) )
155+ {
156+ // Type was already resolved by ProvideValueForTypeExtension
157+ }
158+ else if ( ancestorTypeNode is ValueNode vnType )
159+ {
160+ // Try to parse as a type name
161+ var typeName = vnType . Value as string ;
162+ if ( ! IsNullOrEmpty ( typeName ) )
163+ {
164+ XmlType xmlType = TypeArgumentsParser . ParseSingle ( typeName ! , markupNode . NamespaceResolver , markupNode as IXmlLineInfo ) ;
165+ xmlType . TryResolveTypeSymbol ( null , context . Compilation , context . XmlnsCache , context . TypeCache , out var resolvedType ) ;
166+ ancestorTypeSymbol = resolvedType ;
167+ }
168+ }
169+ }
170+
171+ // Get the AncestorLevel property (default is 1)
172+ int ancestorLevel = 1 ;
173+ if ( markupNode . Properties . TryGetValue ( new XmlName ( "" , "AncestorLevel" ) , out INode ? ancestorLevelNode )
174+ || markupNode . Properties . TryGetValue ( new XmlName ( null , "AncestorLevel" ) , out ancestorLevelNode ) )
175+ {
176+ if ( ancestorLevelNode is ValueNode vnLevel && int . TryParse ( vnLevel . Value as string , out int level ) )
177+ {
178+ ancestorLevel = level ;
179+ }
180+ }
181+
182+ // Determine the actual mode
183+ if ( ancestorTypeSymbol is not null )
184+ {
185+ string actualMode ;
186+ if ( modeStr == "FindAncestor" || modeStr == "FindAncestorBindingContext" )
187+ {
188+ actualMode = $ "global::Microsoft.Maui.Controls.RelativeBindingSourceMode.{ modeStr } ";
189+ }
190+ else
191+ {
192+ // When Mode is not explicitly FindAncestor or FindAncestorBindingContext,
193+ // determine based on whether the type is an Element
194+ var elementType = context . Compilation . GetTypeByMetadataName ( "Microsoft.Maui.Controls.Element" ) ;
195+ if ( elementType is not null && context . Compilation . HasImplicitConversion ( ancestorTypeSymbol , elementType ) )
196+ {
197+ actualMode = "global::Microsoft.Maui.Controls.RelativeBindingSourceMode.FindAncestor" ;
198+ }
199+ else
200+ {
201+ actualMode = "global::Microsoft.Maui.Controls.RelativeBindingSourceMode.FindAncestorBindingContext" ;
202+ }
203+ }
204+
205+ value = $ "new global::Microsoft.Maui.Controls.RelativeBindingSource({ actualMode } , typeof({ ancestorTypeSymbol . ToFQDisplayString ( ) } ), { ancestorLevel } )";
206+ return true ;
207+ }
208+ else if ( modeStr == "Self" )
209+ {
210+ value = "global::Microsoft.Maui.Controls.RelativeBindingSource.Self" ;
211+ return true ;
212+ }
213+ else if ( modeStr == "TemplatedParent" )
214+ {
215+ value = "global::Microsoft.Maui.Controls.RelativeBindingSource.TemplatedParent" ;
216+ return true ;
217+ }
218+ else if ( modeStr == "FindAncestor" || modeStr == "FindAncestorBindingContext" )
219+ {
220+ // These modes require AncestorType
221+ context . ReportDiagnostic ( Diagnostic . Create ( Descriptors . XamlParserError , null , $ "RelativeSource Mode '{ modeStr } ' requires AncestorType") ) ;
222+ value = "default" ;
223+ return false ;
224+ }
225+ else
226+ {
227+ context . ReportDiagnostic ( Diagnostic . Create ( Descriptors . XamlParserError , null , $ "Invalid RelativeSource Mode '{ modeStr ?? "(none)" } '") ) ;
228+ value = "default" ;
229+ return false ;
230+ }
231+ }
232+
132233 public static bool ProvideValueForDynamicResourceExtension ( ElementNode markupNode , IndentedTextWriter writer , SourceGenContext context , NodeSGExtensions . GetNodeValueDelegate ? getNodeValue , out ITypeSymbol ? returnType , out string value )
133234 {
134235 returnType = context . Compilation . GetTypeByMetadataName ( "Microsoft.Maui.Controls.Internals.DynamicResource" ) ! ;
0 commit comments