@ -197,8 +197,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
if ( avaloniaPropertyFieldMaybe ! = null )
if ( avaloniaPropertyFieldMaybe ! = null )
{
{
nodes . Add ( new XamlIlAvaloniaPropertyPropertyPathElementNode ( avaloniaPropertyFieldMaybe ,
var isDataContextProperty = avaloniaPropertyFieldMaybe . Name = = "DataContextProperty" & & Equals ( avaloniaPropertyFieldMaybe . DeclaringType , context . GetAvaloniaTypes ( ) . StyledElement ) ;
XamlIlAvaloniaPropertyHelper . GetAvaloniaPropertyType ( avaloniaPropertyFieldMaybe , context . GetAvaloniaTypes ( ) , lineInfo ) ) ) ;
var propertyType = isDataContextProperty
? ( nodes . LastOrDefault ( ) as IXamlIlBindingPathNodeWithDataContextType ) ? . DataContextType
: null ;
propertyType ? ? = XamlIlAvaloniaPropertyHelper . GetAvaloniaPropertyType ( avaloniaPropertyFieldMaybe , context . GetAvaloniaTypes ( ) , lineInfo ) ;
nodes . Add ( new XamlIlAvaloniaPropertyPropertyPathElementNode ( avaloniaPropertyFieldMaybe , propertyType ) ) ;
}
}
else if ( GetAllDefinedProperties ( targetType ) . FirstOrDefault ( p = > p . Name = = propName . PropertyName ) is IXamlProperty clrProperty )
else if ( GetAllDefinedProperties ( targetType ) . FirstOrDefault ( p = > p . Name = = propName . PropertyName ) is IXamlProperty clrProperty )
{
{
@ -297,34 +302,55 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
nodes . Add ( new TemplatedParentPathElementNode ( templatedParentType . GetClrType ( ) ) ) ;
nodes . Add ( new TemplatedParentPathElementNode ( templatedParentType . GetClrType ( ) ) ) ;
break ;
break ;
case BindingExpressionGrammar . AncestorNode ancestor :
case BindingExpressionGrammar . AncestorNode ancestor :
if ( ancestor . Namespace is null & & ancestor . TypeName is null )
var styledElement = context . GetAvaloniaTypes ( ) . StyledElement ;
var ancestorTypeFilter = ! ( ancestor . Namespace is null & & ancestor . TypeName is null ) ? GetType ( ancestor . Namespace , ancestor . TypeName ) : null ;
var ancestorNode = context
. ParentNodes ( )
. OfType < XamlAstConstructableObjectNode > ( )
. Where ( x = > styledElement . IsAssignableFrom ( x . Type . GetClrType ( ) ) )
. Skip ( 1 )
. Where ( x = > ancestorTypeFilter is not null
? ancestorTypeFilter . IsAssignableFrom ( x . Type . GetClrType ( ) ) : true )
. ElementAtOrDefault ( ancestor . Level ) ;
IXamlType ? dataContextType = null ;
if ( ancestorNode is not null )
{
{
var styledElementType = context . GetAvaloniaTypes ( ) . StyledElement ;
var isSkipping = true ;
var ancestorType = context
foreach ( var node in context . ParentNodes ( ) )
. ParentNodes ( )
. OfType < XamlAstConstructableObjectNode > ( )
. Where ( x = > styledElementType . IsAssignableFrom ( x . Type . GetClrType ( ) ) )
. Skip ( 1 )
. ElementAtOrDefault ( ancestor . Level )
? . Type . GetClrType ( ) ;
if ( ancestorType is null )
{
{
throw new XamlX . XamlTransformException ( "Unable to resolve implicit ancestor type based on XAML tree." , lineInfo ) ;
if ( node = = ancestorNode )
isSkipping = false ;
if ( node is AvaloniaNameScopeRegistrationXamlIlNode )
break ;
if ( ! isSkipping & & node is AvaloniaXamlIlDataContextTypeMetadataNode metadataNode )
{
dataContextType = metadataNode . DataContextType ;
break ;
}
}
}
nodes . Add ( new FindAncestorPathElementNode ( ancestorType , ancestor . Level ) ) ;
}
}
else
// We need actual ancestor for a correct DataContextType,
// but since in current design bindings do a double-work by enumerating the tree,
// we want to keep original ancestor type filter, if it was present.
var bindingAncestorType = ancestorTypeFilter is not null
? ancestorTypeFilter
: ancestorNode ? . Type . GetClrType ( ) ;
if ( bindingAncestorType is null )
{
{
nodes . Add ( new FindAncestorPathElementNode ( GetType ( ancestor . Namespace , ancestor . TypeName ) , ancestor . Level ) ) ;
throw new XamlX . XamlTransformException ( "Unable to resolve implicit ancestor type based on XAML tree." , lineInfo ) ;
}
}
nodes . Add ( new FindAncestorPathElementNode ( bindingAncestorType , ancestor . Level , dataContextType ) ) ;
break ;
break ;
case BindingExpressionGrammar . NameNode elementName :
case BindingExpressionGrammar . NameNode elementName :
IXamlType ? elementType = null ;
IXamlType ? elementType = null , dataType = null ;
foreach ( var deferredContent in context . ParentNodes ( ) . OfType < NestedScopeMetadataNode > ( ) )
foreach ( var deferredContent in context . ParentNodes ( ) . OfType < NestedScopeMetadataNode > ( ) )
{
{
elementType = ScopeRegistrationFinder . GetTargetType ( deferredContent , elementName . Name ) ;
( elementType , dataType ) = ScopeRegistrationFinder . GetTargetType ( deferredContent , elementName . Name ) ? ? default ;
if ( ! ( elementType is null ) )
if ( ! ( elementType is null ) )
{
{
break ;
break ;
@ -332,14 +358,14 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
}
if ( elementType is null )
if ( elementType is null )
{
{
elementType = ScopeRegistrationFinder . GetTargetType ( context . ParentNodes ( ) . Last ( ) , elementName . Name ) ;
( elementType , dataType ) = ScopeRegistrationFinder . GetTargetType ( context . ParentNodes ( ) . Last ( ) , elementName . Name ) ? ? default ;
}
}
if ( elementType is null )
if ( elementType is null )
{
{
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 ) ;
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 ) ;
}
}
nodes . Add ( new ElementNamePathElementNode ( elementName . Name , elementType ) ) ;
nodes . Add ( new ElementNamePathElementNode ( elementName . Name , elementType , dataType ) ) ;
break ;
break ;
case BindingExpressionGrammar . TypeCastNode typeCastNode :
case BindingExpressionGrammar . TypeCastNode typeCastNode :
var castType = GetType ( typeCastNode . Namespace , typeCastNode . TypeName ) ;
var castType = GetType ( typeCastNode . Namespace , typeCastNode . TypeName ) ;
@ -420,8 +446,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
string Name { get ; }
string Name { get ; }
IXamlType ? TargetType { get ; set ; }
IXamlType ? TargetType { get ; set ; }
IXamlType ? DataContextType { get ; set ; }
public static IXamlType ? GetTargetType ( IXamlAstNode namescopeRoot , string name )
public static ( IXamlType Target , IXamlType ? DataContextType ) ? GetTargetType ( IXamlAstNode namescopeRoot , string name )
{
{
// If we start from the nested scope - skip it.
// If we start from the nested scope - skip it.
if ( namescopeRoot is NestedScopeMetadataNode scope )
if ( namescopeRoot is NestedScopeMetadataNode scope )
@ -431,7 +458,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
var finder = new ScopeRegistrationFinder ( name ) ;
var finder = new ScopeRegistrationFinder ( name ) ;
namescopeRoot . Visit ( finder ) ;
namescopeRoot . Visit ( finder ) ;
return finder . TargetType ;
return finder . TargetType is not null ? ( finder . TargetType , DataType : finder . DataContextType ) : null ;
}
}
void IXamlAstVisitor . Pop ( )
void IXamlAstVisitor . Pop ( )
@ -455,12 +482,20 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
IXamlAstNode IXamlAstVisitor . Visit ( IXamlAstNode node )
IXamlAstNode IXamlAstVisitor . Visit ( IXamlAstNode node )
{
{
// Ignore name registrations, if we are inside of the nested namescope.
// Ignore name registrations, if we are inside of the nested namescope.
if ( _ childScopesStack . Count = = 0 & & node is AvaloniaNameScopeRegistrationXamlIlNode registration )
if ( _ childScopesStack . Count = = 0 )
{
{
if ( registration . Name is XamlAstTextNode text & & text . Text = = Name )
if ( node is AvaloniaNameScopeRegistrationXamlIlNode registration
& & registration . Name is XamlAstTextNode text & & text . Text = = Name )
{
{
TargetType = registration . TargetType ;
TargetType = registration . TargetType ;
}
}
// We are visiting nodes top to bottom.
// If we have already found target type by its name,
// it means all next nodes will be below, and not applicable for data context inheritance.
else if ( TargetType is null & & node is AvaloniaXamlIlDataContextTypeMetadataNode dataContextTypeMetadata )
{
DataContextType = dataContextTypeMetadata . DataContextType ;
}
}
}
return node ;
return node ;
}
}
@ -473,6 +508,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
void Emit ( XamlIlEmitContext context , IXamlILEmitter codeGen ) ;
void Emit ( XamlIlEmitContext context , IXamlILEmitter codeGen ) ;
}
}
interface IXamlIlBindingPathNodeWithDataContextType
{
IXamlType ? DataContextType { get ; }
}
class XamlIlNotPathElementNode : IXamlIlBindingPathElementNode
class XamlIlNotPathElementNode : IXamlIlBindingPathElementNode
{
{
public XamlIlNotPathElementNode ( IXamlType boolType )
public XamlIlNotPathElementNode ( IXamlType boolType )
@ -533,17 +573,19 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
}
}
}
class FindAncestorPathElementNode : IXamlIlBindingPathElementNode
class FindAncestorPathElementNode : IXamlIlBindingPathElementNode , IXamlIlBindingPathNodeWithDataContextType
{
{
private readonly int _l evel ;
private readonly int _l evel ;
public FindAncestorPathElementNode ( IXamlType ancestorType , int level )
public FindAncestorPathElementNode ( IXamlType ancestorType , int level , IXamlType ? dataContextType )
{
{
Type = ancestorType ;
Type = ancestorType ;
_l evel = level ;
_l evel = level ;
DataContextType = dataContextType ;
}
}
public IXamlType Type { get ; }
public IXamlType Type { get ; }
public IXamlType ? DataContextType { get ; }
public void Emit ( XamlIlEmitContext context , IXamlILEmitter codeGen )
public void Emit ( XamlIlEmitContext context , IXamlILEmitter codeGen )
{
{
@ -573,17 +615,19 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
}
}
}
class ElementNamePathElementNode : IXamlIlBindingPathElementNode
class ElementNamePathElementNode : IXamlIlBindingPathElementNode , IXamlIlBindingPathNodeWithDataContextType
{
{
private readonly string _ name ;
private readonly string _ name ;
public ElementNamePathElementNode ( string name , IXamlType elementType )
public ElementNamePathElementNode ( string name , IXamlType elementType , IXamlType ? dataType )
{
{
_ name = name ;
_ name = name ;
Type = elementType ;
Type = elementType ;
DataContextType = dataType ;
}
}
public IXamlType Type { get ; }
public IXamlType Type { get ; }
public IXamlType ? DataContextType { get ; }
public void Emit ( XamlIlEmitContext context , IXamlILEmitter codeGen )
public void Emit ( XamlIlEmitContext context , IXamlILEmitter codeGen )
{
{