@@ -197,8 +197,13 @@ private static XamlIlBindingPathNode TransformBindingPath(AstTransformationConte
197
197
198
198
if ( avaloniaPropertyFieldMaybe != null )
199
199
{
200
- nodes . Add ( new XamlIlAvaloniaPropertyPropertyPathElementNode ( avaloniaPropertyFieldMaybe ,
201
- XamlIlAvaloniaPropertyHelper . GetAvaloniaPropertyType ( avaloniaPropertyFieldMaybe , context . GetAvaloniaTypes ( ) , lineInfo ) ) ) ;
200
+ var isDataContextProperty = avaloniaPropertyFieldMaybe . Name == "DataContextProperty" && Equals ( avaloniaPropertyFieldMaybe . DeclaringType , context . GetAvaloniaTypes ( ) . StyledElement ) ;
201
+ var propertyType = isDataContextProperty
202
+ ? ( nodes . LastOrDefault ( ) as IXamlIlBindingPathNodeWithDataContextType ) ? . DataContextType
203
+ : null ;
204
+ propertyType ??= XamlIlAvaloniaPropertyHelper . GetAvaloniaPropertyType ( avaloniaPropertyFieldMaybe , context . GetAvaloniaTypes ( ) , lineInfo ) ;
205
+
206
+ nodes . Add ( new XamlIlAvaloniaPropertyPropertyPathElementNode ( avaloniaPropertyFieldMaybe , propertyType ) ) ;
202
207
}
203
208
else if ( GetAllDefinedProperties ( targetType ) . FirstOrDefault ( p => p . Name == propName . PropertyName ) is IXamlProperty clrProperty )
204
209
{
@@ -297,49 +302,70 @@ private static XamlIlBindingPathNode TransformBindingPath(AstTransformationConte
297
302
nodes . Add ( new TemplatedParentPathElementNode ( templatedParentType . GetClrType ( ) ) ) ;
298
303
break ;
299
304
case BindingExpressionGrammar . AncestorNode ancestor :
300
- if ( ancestor . Namespace is null && ancestor . TypeName is null )
305
+ var styledElement = context . GetAvaloniaTypes ( ) . StyledElement ;
306
+ var ancestorTypeFilter = ! ( ancestor . Namespace is null && ancestor . TypeName is null ) ? GetType ( ancestor . Namespace , ancestor . TypeName ) : null ;
307
+
308
+ var ancestorNode = context
309
+ . ParentNodes ( )
310
+ . OfType < XamlAstConstructableObjectNode > ( )
311
+ . Where ( x => styledElement . IsAssignableFrom ( x . Type . GetClrType ( ) ) )
312
+ . Skip ( 1 )
313
+ . Where ( x => ancestorTypeFilter is not null
314
+ ? ancestorTypeFilter . IsAssignableFrom ( x . Type . GetClrType ( ) ) : true )
315
+ . ElementAtOrDefault ( ancestor . Level ) ;
316
+
317
+ IXamlType ? dataContextType = null ;
318
+ if ( ancestorNode is not null )
301
319
{
302
- var styledElementType = context . GetAvaloniaTypes ( ) . StyledElement ;
303
- var ancestorType = context
304
- . ParentNodes ( )
305
- . OfType < XamlAstConstructableObjectNode > ( )
306
- . Where ( x => styledElementType . IsAssignableFrom ( x . Type . GetClrType ( ) ) )
307
- . Skip ( 1 )
308
- . ElementAtOrDefault ( ancestor . Level )
309
- ? . Type . GetClrType ( ) ;
310
-
311
- if ( ancestorType is null )
320
+ var isSkipping = true ;
321
+ foreach ( var node in context . ParentNodes ( ) )
312
322
{
313
- throw new XamlX . XamlTransformException ( "Unable to resolve implicit ancestor type based on XAML tree." , lineInfo ) ;
323
+ if ( node == ancestorNode )
324
+ isSkipping = false ;
325
+ if ( node is AvaloniaNameScopeRegistrationXamlIlNode )
326
+ break ;
327
+ if ( ! isSkipping && node is AvaloniaXamlIlDataContextTypeMetadataNode metadataNode )
328
+ {
329
+ dataContextType = metadataNode . DataContextType ;
330
+ break ;
331
+ }
314
332
}
315
-
316
- nodes . Add ( new FindAncestorPathElementNode ( ancestorType , ancestor . Level ) ) ;
317
333
}
318
- else
334
+
335
+ // We need actual ancestor for a correct DataContextType,
336
+ // but since in current design bindings do a double-work by enumerating the tree,
337
+ // we want to keep original ancestor type filter, if it was present.
338
+ var bindingAncestorType = ancestorTypeFilter is not null
339
+ ? ancestorTypeFilter
340
+ : ancestorNode ? . Type . GetClrType ( ) ;
341
+
342
+ if ( bindingAncestorType is null )
319
343
{
320
- nodes . Add ( new FindAncestorPathElementNode ( GetType ( ancestor . Namespace , ancestor . TypeName ) , ancestor . Level ) ) ;
344
+ throw new XamlX . XamlTransformException ( "Unable to resolve implicit ancestor type based on XAML tree." , lineInfo ) ;
321
345
}
346
+
347
+ nodes . Add ( new FindAncestorPathElementNode ( bindingAncestorType , ancestor . Level , dataContextType ) ) ;
322
348
break ;
323
349
case BindingExpressionGrammar . NameNode elementName :
324
- IXamlType ? elementType = null ;
350
+ IXamlType ? elementType = null , dataType = null ;
325
351
foreach ( var deferredContent in context . ParentNodes ( ) . OfType < NestedScopeMetadataNode > ( ) )
326
352
{
327
- elementType = ScopeRegistrationFinder . GetTargetType ( deferredContent , elementName . Name ) ;
353
+ ( elementType , dataType ) = ScopeRegistrationFinder . GetTargetType ( deferredContent , elementName . Name ) ?? default ;
328
354
if ( ! ( elementType is null ) )
329
355
{
330
356
break ;
331
357
}
332
358
}
333
359
if ( elementType is null )
334
360
{
335
- elementType = ScopeRegistrationFinder . GetTargetType ( context . ParentNodes ( ) . Last ( ) , elementName . Name ) ;
361
+ ( elementType , dataType ) = ScopeRegistrationFinder . GetTargetType ( context . ParentNodes ( ) . Last ( ) , elementName . Name ) ?? default ;
336
362
}
337
363
338
364
if ( elementType is null )
339
365
{
340
366
throw new XamlX . XamlTransformException ( $ "Unable to find element '{ elementName . Name } ' in the current namescope. Unable to use a compiled binding with a name binding if the name cannot be found at compile time.", lineInfo ) ;
341
367
}
342
- nodes . Add ( new ElementNamePathElementNode ( elementName . Name , elementType ) ) ;
368
+ nodes . Add ( new ElementNamePathElementNode ( elementName . Name , elementType , dataType ) ) ;
343
369
break ;
344
370
case BindingExpressionGrammar . TypeCastNode typeCastNode :
345
371
var castType = GetType ( typeCastNode . Namespace , typeCastNode . TypeName ) ;
@@ -420,8 +446,9 @@ private ScopeRegistrationFinder(string name)
420
446
string Name { get ; }
421
447
422
448
IXamlType ? TargetType { get ; set ; }
449
+ IXamlType ? DataContextType { get ; set ; }
423
450
424
- public static IXamlType ? GetTargetType ( IXamlAstNode namescopeRoot , string name )
451
+ public static ( IXamlType Target , IXamlType ? DataContextType ) ? GetTargetType ( IXamlAstNode namescopeRoot , string name )
425
452
{
426
453
// If we start from the nested scope - skip it.
427
454
if ( namescopeRoot is NestedScopeMetadataNode scope )
@@ -431,7 +458,7 @@ private ScopeRegistrationFinder(string name)
431
458
432
459
var finder = new ScopeRegistrationFinder ( name ) ;
433
460
namescopeRoot . Visit ( finder ) ;
434
- return finder . TargetType ;
461
+ return finder . TargetType is not null ? ( finder . TargetType , DataType : finder . DataContextType ) : null ;
435
462
}
436
463
437
464
void IXamlAstVisitor . Pop ( )
@@ -455,12 +482,20 @@ void IXamlAstVisitor.Push(IXamlAstNode node)
455
482
IXamlAstNode IXamlAstVisitor . Visit ( IXamlAstNode node )
456
483
{
457
484
// Ignore name registrations, if we are inside of the nested namescope.
458
- if ( _childScopesStack . Count == 0 && node is AvaloniaNameScopeRegistrationXamlIlNode registration )
485
+ if ( _childScopesStack . Count == 0 )
459
486
{
460
- if ( registration . Name is XamlAstTextNode text && text . Text == Name )
487
+ if ( node is AvaloniaNameScopeRegistrationXamlIlNode registration
488
+ && registration . Name is XamlAstTextNode text && text . Text == Name )
461
489
{
462
490
TargetType = registration . TargetType ;
463
491
}
492
+ // We are visiting nodes top to bottom.
493
+ // If we have already found target type by its name,
494
+ // it means all next nodes will be below, and not applicable for data context inheritance.
495
+ else if ( TargetType is null && node is AvaloniaXamlIlDataContextTypeMetadataNode dataContextTypeMetadata )
496
+ {
497
+ DataContextType = dataContextTypeMetadata . DataContextType ;
498
+ }
464
499
}
465
500
return node ;
466
501
}
@@ -473,6 +508,11 @@ interface IXamlIlBindingPathElementNode
473
508
void Emit ( XamlIlEmitContext context , IXamlILEmitter codeGen ) ;
474
509
}
475
510
511
+ interface IXamlIlBindingPathNodeWithDataContextType
512
+ {
513
+ IXamlType ? DataContextType { get ; }
514
+ }
515
+
476
516
class XamlIlNotPathElementNode : IXamlIlBindingPathElementNode
477
517
{
478
518
public XamlIlNotPathElementNode ( IXamlType boolType )
@@ -533,17 +573,19 @@ public void Emit(XamlIlEmitContext context, IXamlILEmitter codeGen)
533
573
}
534
574
}
535
575
536
- class FindAncestorPathElementNode : IXamlIlBindingPathElementNode
576
+ class FindAncestorPathElementNode : IXamlIlBindingPathElementNode , IXamlIlBindingPathNodeWithDataContextType
537
577
{
538
578
private readonly int _level ;
539
579
540
- public FindAncestorPathElementNode ( IXamlType ancestorType , int level )
580
+ public FindAncestorPathElementNode ( IXamlType ancestorType , int level , IXamlType ? dataContextType )
541
581
{
542
582
Type = ancestorType ;
543
583
_level = level ;
584
+ DataContextType = dataContextType ;
544
585
}
545
586
546
587
public IXamlType Type { get ; }
588
+ public IXamlType ? DataContextType { get ; }
547
589
548
590
public void Emit ( XamlIlEmitContext context , IXamlILEmitter codeGen )
549
591
{
@@ -573,17 +615,19 @@ public void Emit(XamlIlEmitContext context, IXamlILEmitter codeGen)
573
615
}
574
616
}
575
617
576
- class ElementNamePathElementNode : IXamlIlBindingPathElementNode
618
+ class ElementNamePathElementNode : IXamlIlBindingPathElementNode , IXamlIlBindingPathNodeWithDataContextType
577
619
{
578
620
private readonly string _name ;
579
621
580
- public ElementNamePathElementNode ( string name , IXamlType elementType )
622
+ public ElementNamePathElementNode ( string name , IXamlType elementType , IXamlType ? dataType )
581
623
{
582
624
_name = name ;
583
625
Type = elementType ;
626
+ DataContextType = dataType ;
584
627
}
585
628
586
629
public IXamlType Type { get ; }
630
+ public IXamlType ? DataContextType { get ; }
587
631
588
632
public void Emit ( XamlIlEmitContext context , IXamlILEmitter codeGen )
589
633
{
0 commit comments