diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/Content/Uno.UI.SourceGenerators.props b/src/SourceGenerators/Uno.UI.SourceGenerators/Content/Uno.UI.SourceGenerators.props
index c999051962a4..28f271772675 100644
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/Content/Uno.UI.SourceGenerators.props
+++ b/src/SourceGenerators/Uno.UI.SourceGenerators/Content/Uno.UI.SourceGenerators.props
@@ -211,6 +211,7 @@
+
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs
index c639cb0aefb2..265fd8df4824 100644
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs
+++ b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs
@@ -3582,17 +3582,18 @@ private void GenerateInlineEvent(string? closureName, IIndentedStringBuilder wri
// If a binding is inside a DataTemplate, the binding root in the case of an x:Bind is
// the DataContext, not the control's instance.
var template = IsMemberInsideFrameworkTemplate(member.Owner);
- var eventSource = (template.isInside, _xClassName) switch
+ var targetInstance = (template.isInside, _xClassName) switch
{
(false, _) => "this",
(_, not null) => CurrentResourceOwnerName,
_ => null
};
- if (eventSource is null)
+ if (targetInstance is null)
{
GenerateError(writer, $"Unable to use event {member.Member.Name} without a backing class (use x:Class)");
return;
}
+ EnsureXClassName();
var parentApply = (writer as XamlLazyApplyBlockIIndentedStringBuilder)?.MethodName;
var parametersWithType = delegateSymbol
@@ -3615,89 +3616,27 @@ private void GenerateInlineEvent(string? closureName, IIndentedStringBuilder wri
CurrentScope.XBindExpressions.Add(bind);
- var eventTarget = XBindExpressionParser.RestoreSinglePath(bind.Members.First().Value?.ToString());
-
- if (eventTarget == null)
+ var path = XBindExpressionParser.RestoreSinglePath(bind.Members.First().Value?.ToString());
+ if (path is null)
{
throw new InvalidOperationException("x:Bind event path cannot by empty");
}
- var parts = eventTarget.Split('.').ToList();
- var isStaticTarget = parts.FirstOrDefault()?.Contains(":") ?? false;
-
- eventTarget = RewriteNamespaces(eventTarget);
-
- // x:Bind to second-level method generates invalid code
- // sanitizing member.Member.Name so that "ViewModel.SearchBreeds" becomes "ViewModel_SearchBreeds"
- var sanitizedEventTarget = SanitizeResourceName(eventTarget);
-
- (string target, string weakReference, IMethodSymbol targetMethod) buildTargetContext()
+ INamedTypeSymbol GetTargetType()
{
- IMethodSymbol FindTargetMethodSymbol(INamedTypeSymbol? sourceType)
- {
- if (eventTarget.Contains("."))
- {
- ITypeSymbol? currentType = sourceType;
-
- if (isStaticTarget)
- {
- // First part is a type for static method binding and should
- // overide the original source type
- currentType = GetType(RewriteNamespaces(parts[0]));
- parts.RemoveAt(0);
- }
-
- for (var i = 0; i < parts.Count - 1; i++)
- {
- var next = currentType.GetAllMembersWithName(RewriteNamespaces(parts[i])).FirstOrDefault();
-
- currentType = next switch
- {
- IFieldSymbol fs => fs.Type,
- IPropertySymbol ps => ps.Type,
- null => throw new InvalidOperationException($"Unable to find member {parts[i]} on type {currentType}"),
- _ => throw new InvalidOperationException($"The field {next.Name} is not supported for x:Bind event binding")
- };
- }
-
- var method = currentType?.GetFirstMethodWithName(parts.Last(), includeBaseTypes: true)
- ?? throw new InvalidOperationException($"Failed to find {parts.Last()} on {currentType}");
-
- return method;
- }
- else
- {
- return sourceType?.GetFirstMethodWithName(eventTarget, includeBaseTypes: true)
- ?? throw new InvalidOperationException($"Failed to find {eventTarget} on {sourceType}");
- }
- }
-
if (template.isInside)
{
var dataTypeObject = FindMember(template.xamlObject!, "DataType", XamlConstants.XamlXmlNamespace);
if (dataTypeObject?.Value == null)
{
- throw new Exception($"Unable to find x:DataType in enclosing DataTemplate for x:Bind event");
+ throw new Exception("Unable to find x:DataType in enclosing DataTemplate for x:Bind event");
}
- var dataTypeSymbol = GetType(dataTypeObject.Value.ToString() ?? "");
-
- return (
- $"({member.Member.Name}_{sanitizedEventTarget}_That.Target as {XamlConstants.Types.FrameworkElement})?.DataContext as {dataTypeSymbol.GetFullyQualifiedTypeIncludingGlobal()}",
-
- // Use of __rootInstance is required to get the top-level DataContext, as it may be changed
- // in the current visual tree by the user.
- $"(__rootInstance as global::Uno.UI.DataBinding.IWeakReferenceProvider).WeakReference",
- FindTargetMethodSymbol(dataTypeSymbol)
- );
+ return GetType(dataTypeObject.Value.ToString() ?? "");
}
- else if (_xClassName?.Symbol != null)
+ else if (_xClassName?.Symbol is not null)
{
- return (
- $"{member.Member.Name}_{sanitizedEventTarget}_That.Target as {_xClassName}",
- $"({eventSource} as global::Uno.UI.DataBinding.IWeakReferenceProvider).WeakReference",
- FindTargetMethodSymbol(_xClassName.Symbol)
- );
+ return _xClassName.Symbol;
}
else
{
@@ -3705,10 +3644,20 @@ IMethodSymbol FindTargetMethodSymbol(INamedTypeSymbol? sourceType)
}
}
- var targetContext = buildTargetContext();
- var targetMethodHasParameters = targetContext.targetMethod?.Parameters.Any() ?? false;
+ var targetType = GetTargetType(); // The type of the target object onto which the x:Bind path should be resolved
+ var targetInstanceWeakRef = template.isInside
+ // Use of __rootInstance is required to get the top-level DataContext, as it may be changed in the current visual tree by the user.
+ ? "(__rootInstance as global::Uno.UI.DataBinding.IWeakReferenceProvider).WeakReference"
+ : $"({targetInstance} as global::Uno.UI.DataBinding.IWeakReferenceProvider).WeakReference";
- EnsureXClassName();
+ var method = ResolveXBindMethod(targetType, path);
+ var invokeTarget = (method.isStatic, template.isInside) switch
+ {
+ (true, _) => method.declaringType.GetFullyQualifiedTypeIncludingGlobal(), // If the method is static, the target of the method is the type itself
+ (_, true) => $"((target.Target as {XamlConstants.Types.FrameworkElement})?.DataContext as {targetType.GetFullyQualifiedTypeIncludingGlobal()})",
+ _ => $"(target.Target as {targetType.GetFullyQualifiedTypeIncludingGlobal()})"
+ };
+ var invoke = $"{invokeTarget}?.{path}({(method.symbol.Parameters.Any() ? parameters.JoinBy(", ") : "")});";
var handler = RegisterChildSubclass(
$"{parentApply}_{member.Member.Name}_Handler",
@@ -3717,7 +3666,7 @@ public class {{name}}(global::Uno.UI.DataBinding.ManagedWeakReference target)
{
public void Invoke({{parametersWithType.JoinBy(", ")}})
{
- (target.Target as {{_xClassName}})?.{{eventTarget}}({{(targetMethodHasParameters ? parameters.JoinBy(", ") : "")}});
+ {{invoke}}
}
}
"""));
@@ -3736,15 +3685,13 @@ public void Invoke({{parametersWithType.JoinBy(", ")}})
return;
}
- {{componentDefinition.MemberName}}.{{member.Member.Name}} += new {{handler}}({{targetContext.weakReference}}).Invoke;
+ {{componentDefinition.MemberName}}.{{member.Member.Name}} += new {{handler}}({{targetInstanceWeakRef}}).Invoke;
__is{{name}}d = true;
}
"""));
}
else
{
- EnsureXClassName();
-
//
// Generate a sub-class that uses a weak ref, so the owner is not being held onto by the delegate.
// 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(", ")}})
}
}
"""));
- writer.AppendLineIndented($"var {member.Member.Name}_Handler = new {subClass}(({eventSource} as global::Uno.UI.DataBinding.IWeakReferenceProvider).WeakReference);");
+ writer.AppendLineIndented($"var {member.Member.Name}_Handler = new {subClass}(({targetInstance} as global::Uno.UI.DataBinding.IWeakReferenceProvider).WeakReference);");
writer.AppendLineIndented($"/* second level */ {closureName}.{member.Member.Name} += {member.Member.Name}_Handler.Invoke;");
}
}
+ private (ITypeSymbol declaringType, IMethodSymbol symbol, bool isStatic) ResolveXBindMethod(INamedTypeSymbol contextType, string path)
+ {
+ if (path.Contains("."))
+ {
+ ITypeSymbol currentType = contextType;
+
+ var parts = path.Split('.').ToList();
+ var isStatic = parts.FirstOrDefault()?.Contains(":") ?? false;
+ if (isStatic)
+ {
+ // First part is a type for static method binding and should
+ // overide the original source type
+ currentType = contextType = GetType(RewriteNamespaces(parts[0]));
+ parts.RemoveAt(0);
+ }
+
+ for (var i = 0; i < parts.Count - 1; i++)
+ {
+ var next = currentType.GetAllMembersWithName(RewriteNamespaces(parts[i])).FirstOrDefault();
+
+ currentType = next switch
+ {
+ IFieldSymbol fs => fs.Type,
+ IPropertySymbol ps => ps.Type,
+ null => throw new InvalidOperationException($"Unable to find member {parts[i]} on type {currentType}"),
+ _ => throw new InvalidOperationException($"The field {next.Name} is not supported for x:Bind event binding")
+ };
+ }
+
+ var method = currentType.GetFirstMethodWithName(parts.Last(), includeBaseTypes: true)
+ ?? throw new InvalidOperationException($"Failed to find {parts.Last()} on {currentType}");
+
+ return (contextType, method, isStatic);
+ }
+ else
+ {
+ var method = contextType?.GetFirstMethodWithName(path, includeBaseTypes: true)
+ ?? throw new InvalidOperationException($"Failed to find {path} on {contextType}");
+
+ return (contextType, method, false);
+ }
+ }
+
///
/// Build localized properties which have not been set in the xaml.
///