@@ -3582,17 +3582,18 @@ private void GenerateInlineEvent(string? closureName, IIndentedStringBuilder wri
3582
3582
// If a binding is inside a DataTemplate, the binding root in the case of an x:Bind is
3583
3583
// the DataContext, not the control's instance.
3584
3584
var template = IsMemberInsideFrameworkTemplate ( member . Owner ) ;
3585
- var eventSource = ( template . isInside , _xClassName ) switch
3585
+ var targetInstance = ( template . isInside , _xClassName ) switch
3586
3586
{
3587
3587
( false , _ ) => "this" ,
3588
3588
( _, not null ) => CurrentResourceOwnerName ,
3589
3589
_ => null
3590
3590
} ;
3591
- if ( eventSource is null )
3591
+ if ( targetInstance is null )
3592
3592
{
3593
3593
GenerateError ( writer , $ "Unable to use event { member . Member . Name } without a backing class (use x:Class)") ;
3594
3594
return ;
3595
3595
}
3596
+ EnsureXClassName ( ) ;
3596
3597
3597
3598
var parentApply = ( writer as XamlLazyApplyBlockIIndentedStringBuilder ) ? . MethodName ;
3598
3599
var parametersWithType = delegateSymbol
@@ -3615,100 +3616,48 @@ private void GenerateInlineEvent(string? closureName, IIndentedStringBuilder wri
3615
3616
3616
3617
CurrentScope . XBindExpressions . Add ( bind ) ;
3617
3618
3618
- var eventTarget = XBindExpressionParser . RestoreSinglePath ( bind . Members . First ( ) . Value ? . ToString ( ) ) ;
3619
-
3620
- if ( eventTarget == null )
3619
+ var path = XBindExpressionParser . RestoreSinglePath ( bind . Members . First ( ) . Value ? . ToString ( ) ) ;
3620
+ if ( path is null )
3621
3621
{
3622
3622
throw new InvalidOperationException ( "x:Bind event path cannot by empty" ) ;
3623
3623
}
3624
3624
3625
- var parts = eventTarget . Split ( '.' ) . ToList ( ) ;
3626
- var isStaticTarget = parts . FirstOrDefault ( ) ? . Contains ( ":" ) ?? false ;
3627
-
3628
- eventTarget = RewriteNamespaces ( eventTarget ) ;
3629
-
3630
- // x:Bind to second-level method generates invalid code
3631
- // sanitizing member.Member.Name so that "ViewModel.SearchBreeds" becomes "ViewModel_SearchBreeds"
3632
- var sanitizedEventTarget = SanitizeResourceName ( eventTarget ) ;
3633
-
3634
- ( string target , string weakReference , IMethodSymbol targetMethod ) buildTargetContext ( )
3625
+ INamedTypeSymbol GetTargetType ( )
3635
3626
{
3636
- IMethodSymbol FindTargetMethodSymbol ( INamedTypeSymbol ? sourceType )
3637
- {
3638
- if ( eventTarget . Contains ( "." ) )
3639
- {
3640
- ITypeSymbol ? currentType = sourceType ;
3641
-
3642
- if ( isStaticTarget )
3643
- {
3644
- // First part is a type for static method binding and should
3645
- // overide the original source type
3646
- currentType = GetType ( RewriteNamespaces ( parts [ 0 ] ) ) ;
3647
- parts . RemoveAt ( 0 ) ;
3648
- }
3649
-
3650
- for ( var i = 0 ; i < parts . Count - 1 ; i ++ )
3651
- {
3652
- var next = currentType . GetAllMembersWithName ( RewriteNamespaces ( parts [ i ] ) ) . FirstOrDefault ( ) ;
3653
-
3654
- currentType = next switch
3655
- {
3656
- IFieldSymbol fs => fs . Type ,
3657
- IPropertySymbol ps => ps . Type ,
3658
- null => throw new InvalidOperationException ( $ "Unable to find member { parts [ i ] } on type { currentType } ") ,
3659
- _ => throw new InvalidOperationException ( $ "The field { next . Name } is not supported for x:Bind event binding")
3660
- } ;
3661
- }
3662
-
3663
- var method = currentType ? . GetFirstMethodWithName ( parts . Last ( ) , includeBaseTypes : true )
3664
- ?? throw new InvalidOperationException ( $ "Failed to find { parts . Last ( ) } on { currentType } ") ;
3665
-
3666
- return method ;
3667
- }
3668
- else
3669
- {
3670
- return sourceType ? . GetFirstMethodWithName ( eventTarget , includeBaseTypes : true )
3671
- ?? throw new InvalidOperationException ( $ "Failed to find { eventTarget } on { sourceType } ") ;
3672
- }
3673
- }
3674
-
3675
3627
if ( template . isInside )
3676
3628
{
3677
3629
var dataTypeObject = FindMember ( template . xamlObject ! , "DataType" , XamlConstants . XamlXmlNamespace ) ;
3678
3630
if ( dataTypeObject ? . Value == null )
3679
3631
{
3680
- throw new Exception ( $ "Unable to find x:DataType in enclosing DataTemplate for x:Bind event") ;
3632
+ throw new Exception ( "Unable to find x:DataType in enclosing DataTemplate for x:Bind event" ) ;
3681
3633
}
3682
3634
3683
- var dataTypeSymbol = GetType ( dataTypeObject . Value . ToString ( ) ?? "" ) ;
3684
-
3685
- return (
3686
- $ "({ member . Member . Name } _{ sanitizedEventTarget } _That.Target as { XamlConstants . Types . FrameworkElement } )?.DataContext as { dataTypeSymbol . GetFullyQualifiedTypeIncludingGlobal ( ) } ",
3687
-
3688
- // Use of __rootInstance is required to get the top-level DataContext, as it may be changed
3689
- // in the current visual tree by the user.
3690
- $ "(__rootInstance as global::Uno.UI.DataBinding.IWeakReferenceProvider).WeakReference",
3691
- FindTargetMethodSymbol ( dataTypeSymbol )
3692
- ) ;
3635
+ return GetType ( dataTypeObject . Value . ToString ( ) ?? "" ) ;
3693
3636
}
3694
- else if ( _xClassName ? . Symbol != null )
3637
+ else if ( _xClassName ? . Symbol is not null )
3695
3638
{
3696
- return (
3697
- $ "{ member . Member . Name } _{ sanitizedEventTarget } _That.Target as { _xClassName } ",
3698
- $ "({ eventSource } as global::Uno.UI.DataBinding.IWeakReferenceProvider).WeakReference",
3699
- FindTargetMethodSymbol ( _xClassName . Symbol )
3700
- ) ;
3639
+ return _xClassName . Symbol ;
3701
3640
}
3702
3641
else
3703
3642
{
3704
3643
throw new Exception ( $ "Unable to find the type { _xClassName ? . Namespace } .{ _xClassName ? . ClassName } ") ;
3705
3644
}
3706
3645
}
3707
3646
3708
- var targetContext = buildTargetContext ( ) ;
3709
- var targetMethodHasParameters = targetContext . targetMethod ? . Parameters . Any ( ) ?? false ;
3647
+ var targetType = GetTargetType ( ) ; // The type of the target object onto which the x:Bind path should be resolved
3648
+ var targetInstanceWeakRef = template . isInside
3649
+ // Use of __rootInstance is required to get the top-level DataContext, as it may be changed in the current visual tree by the user.
3650
+ ? "(__rootInstance as global::Uno.UI.DataBinding.IWeakReferenceProvider).WeakReference"
3651
+ : $ "({ targetInstance } as global::Uno.UI.DataBinding.IWeakReferenceProvider).WeakReference";
3710
3652
3711
- EnsureXClassName ( ) ;
3653
+ var method = ResolveXBindMethod ( targetType , path ) ;
3654
+ var invokeTarget = ( method . isStatic , template . isInside ) switch
3655
+ {
3656
+ ( true , _ ) => method . declaringType . GetFullyQualifiedTypeIncludingGlobal ( ) , // If the method is static, the target of the method is the type itself
3657
+ ( _, true ) => $ "((target.Target as { XamlConstants . Types . FrameworkElement } )?.DataContext as { targetType . GetFullyQualifiedTypeIncludingGlobal ( ) } )",
3658
+ _ => $ "(target.Target as { targetType . GetFullyQualifiedTypeIncludingGlobal ( ) } )"
3659
+ } ;
3660
+ var invoke = $ "{ invokeTarget } ?.{ path } ({ ( method . symbol . Parameters . Any ( ) ? parameters . JoinBy ( ", " ) : "" ) } );";
3712
3661
3713
3662
var handler = RegisterChildSubclass (
3714
3663
$ "{ parentApply } _{ member . Member . Name } _Handler",
@@ -3717,7 +3666,7 @@ public class {{name}}(global::Uno.UI.DataBinding.ManagedWeakReference target)
3717
3666
{
3718
3667
public void Invoke({{ parametersWithType . JoinBy ( ", " ) }} )
3719
3668
{
3720
- (target.Target as {{ _xClassName }} )?. {{ eventTarget }} ( {{ ( targetMethodHasParameters ? parameters . JoinBy ( ", " ) : "" ) }} );
3669
+ {{ invoke }}
3721
3670
}
3722
3671
}
3723
3672
""" ) ) ;
@@ -3736,15 +3685,13 @@ public void Invoke({{parametersWithType.JoinBy(", ")}})
3736
3685
return;
3737
3686
}
3738
3687
3739
- {{ componentDefinition . MemberName }} .{{ member . Member . Name }} += new {{ handler }} ({{ targetContext . weakReference }} ).Invoke;
3688
+ {{ componentDefinition . MemberName }} .{{ member . Member . Name }} += new {{ handler }} ({{ targetInstanceWeakRef }} ).Invoke;
3740
3689
__is{{ name }} d = true;
3741
3690
}
3742
3691
""" ) ) ;
3743
3692
}
3744
3693
else
3745
3694
{
3746
- EnsureXClassName ( ) ;
3747
-
3748
3695
//
3749
3696
// Generate a sub-class that uses a weak ref, so the owner is not being held onto by the delegate.
3750
3697
// We can use the WeakReferenceProvider to get a self reference to avoid adding the cost of the
@@ -3762,11 +3709,54 @@ public void Invoke({{parametersWithType.JoinBy(", ")}})
3762
3709
}
3763
3710
}
3764
3711
""" ) ) ;
3765
- writer . AppendLineIndented ( $ "var { member . Member . Name } _Handler = new { subClass } (({ eventSource } as global::Uno.UI.DataBinding.IWeakReferenceProvider).WeakReference);") ;
3712
+ writer . AppendLineIndented ( $ "var { member . Member . Name } _Handler = new { subClass } (({ targetInstance } as global::Uno.UI.DataBinding.IWeakReferenceProvider).WeakReference);") ;
3766
3713
writer . AppendLineIndented ( $ "/* second level */ { closureName } .{ member . Member . Name } += { member . Member . Name } _Handler.Invoke;") ;
3767
3714
}
3768
3715
}
3769
3716
3717
+ private ( ITypeSymbol declaringType , IMethodSymbol symbol , bool isStatic ) ResolveXBindMethod ( INamedTypeSymbol contextType , string path )
3718
+ {
3719
+ if ( path . Contains ( "." ) )
3720
+ {
3721
+ ITypeSymbol currentType = contextType ;
3722
+
3723
+ var parts = path . Split ( '.' ) . ToList ( ) ;
3724
+ var isStatic = parts . FirstOrDefault ( ) ? . Contains ( ":" ) ?? false ;
3725
+ if ( isStatic )
3726
+ {
3727
+ // First part is a type for static method binding and should
3728
+ // overide the original source type
3729
+ currentType = contextType = GetType ( RewriteNamespaces ( parts [ 0 ] ) ) ;
3730
+ parts . RemoveAt ( 0 ) ;
3731
+ }
3732
+
3733
+ for ( var i = 0 ; i < parts . Count - 1 ; i ++ )
3734
+ {
3735
+ var next = currentType . GetAllMembersWithName ( RewriteNamespaces ( parts [ i ] ) ) . FirstOrDefault ( ) ;
3736
+
3737
+ currentType = next switch
3738
+ {
3739
+ IFieldSymbol fs => fs . Type ,
3740
+ IPropertySymbol ps => ps . Type ,
3741
+ null => throw new InvalidOperationException ( $ "Unable to find member { parts [ i ] } on type { currentType } ") ,
3742
+ _ => throw new InvalidOperationException ( $ "The field { next . Name } is not supported for x:Bind event binding")
3743
+ } ;
3744
+ }
3745
+
3746
+ var method = currentType . GetFirstMethodWithName ( parts . Last ( ) , includeBaseTypes : true )
3747
+ ?? throw new InvalidOperationException ( $ "Failed to find { parts . Last ( ) } on { currentType } ") ;
3748
+
3749
+ return ( contextType , method , isStatic ) ;
3750
+ }
3751
+ else
3752
+ {
3753
+ var method = contextType ? . GetFirstMethodWithName ( path , includeBaseTypes : true )
3754
+ ?? throw new InvalidOperationException ( $ "Failed to find { path } on { contextType } ") ;
3755
+
3756
+ return ( contextType , method , false ) ;
3757
+ }
3758
+ }
3759
+
3770
3760
/// <summary>
3771
3761
/// Build localized properties which have not been set in the xaml.
3772
3762
/// </summary>
0 commit comments