diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs index b366e98856..3bc9f0dd46 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs @@ -349,18 +349,21 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions break; case BindingExpressionGrammar.NameNode elementName: IXamlType? elementType = null, dataType = null; + (elementType, dataType) = ScopeRegistrationFinder.GetTargetType(context.ParentNodes().Last(), elementName.Name) ?? default; + foreach (var deferredContent in context.ParentNodes().OfType()) { + if (!(elementType is null)) + { + break; + } + (elementType, dataType) = ScopeRegistrationFinder.GetTargetType(deferredContent, elementName.Name) ?? default; if (!(elementType is null)) { break; } } - if (elementType is null) - { - (elementType, dataType) = ScopeRegistrationFinder.GetTargetType(context.ParentNodes().Last(), elementName.Name) ?? default; - } if (elementType is null) { @@ -506,17 +509,15 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions // Ignore name registrations, if we are inside of the nested namescope. if (_childScopesStack.Count == 0) { - if (node is AvaloniaNameScopeRegistrationXamlIlNode registration + if (TargetType is null + && node is AvaloniaNameScopeRegistrationXamlIlNode registration && registration.Name is XamlAstTextNode text && text.Text == Name) { 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; + DataContextType = _stack + .OfType() + .FirstOrDefault() + ?.DataContextType; } } return node; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs index 9118378dbd..e921ad5461 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Globalization; using System.Linq; using System.Runtime.CompilerServices; +using System.Windows.Input; using Avalonia.Controls; using Avalonia.Data.Converters; using Avalonia.Data.Core; @@ -425,6 +426,54 @@ namespace Avalonia.Markup.Xaml.UnitTests Assert.Equal((IEnumerable)["a", "b", "c"], parsed.MyProp.Select(x => x.Value)); } } + + [Fact] + public void Compiled_Binding_Should_Resolve_Named_Root_DataContext_In_ItemTemplate() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var parsed = (ListBox)AvaloniaRuntimeXamlLoader.Parse(@" + + + + + + +"); + Assert.NotNull(parsed.ItemTemplate); + } + } + + [Fact] + public void Compiled_Binding_Should_Resolve_Root_Command_From_Nested_ItemTemplate_Namescope() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var parsed = (ListBox)AvaloniaRuntimeXamlLoader.Parse(@" + + + + + + + + + + + + +"); + Assert.NotNull(parsed.ItemTemplate); + } + } } public class XamlIlBugTestsEventHandlerCodeBehind : Window @@ -555,4 +604,17 @@ namespace Avalonia.Markup.Xaml.UnitTests } } + + public class CompiledBindingRootMock : UserControl + { + public string Greeting => "Hello"; + public string RootProperty => "RootValue"; + public IReadOnlyList Items { get; } = [new() { Name = "Outer", InnerItems = [new() { Name = "Inner" }] }]; + } + + public class CompiledBindingItemMock + { + public string Name { get; set; } = string.Empty; + public IReadOnlyList InnerItems { get; set; } = Array.Empty(); + } }