From 2f0e075d96d1b8a4ad4042c1a28014bd8cbaa0cd Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 20 Oct 2023 17:37:56 +0200 Subject: [PATCH] Don't log errors for named control bindings... ...on elements which aren't yet rooted. --- .../Core/ExpressionNodes/NamedElementNode.cs | 11 ++++- .../Data/BindingTests_Logging.cs | 47 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Data/Core/ExpressionNodes/NamedElementNode.cs b/src/Avalonia.Base/Data/Core/ExpressionNodes/NamedElementNode.cs index daf1b21506..71066d5888 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionNodes/NamedElementNode.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionNodes/NamedElementNode.cs @@ -1,11 +1,12 @@ using System; using System.Text; using Avalonia.Controls; +using Avalonia.LogicalTree; using Avalonia.Reactive; namespace Avalonia.Data.Core.ExpressionNodes; -internal class NamedElementNode : ExpressionNode +internal class NamedElementNode : SourceNode { private readonly WeakReference _nameScope; private readonly string _name; @@ -23,10 +24,18 @@ internal class NamedElementNode : ExpressionNode builder.Append(_name); } + public override bool ShouldLogErrors(object target) + { + // We don't log errors when the target element isn't rooted. + return target is not ILogical logical || logical.IsAttachedToLogicalTree; + } + protected override void OnSourceChanged(object source) { if (_nameScope.TryGetTarget(out var scope)) _subscription = NameScopeLocator.Track(scope, _name).Subscribe(SetValue); + else + SetError("NameScope not found."); } protected override void Unsubscribe(object oldSource) diff --git a/tests/Avalonia.Markup.UnitTests/Data/BindingTests_Logging.cs b/tests/Avalonia.Markup.UnitTests/Data/BindingTests_Logging.cs index 5792b98653..4d215ccc3b 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/BindingTests_Logging.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/BindingTests_Logging.cs @@ -6,6 +6,7 @@ using Avalonia.Data.Core; using Avalonia.Data.Core.Plugins.Reflection; using Avalonia.Input; using Avalonia.Logging; +using Avalonia.LogicalTree; using Avalonia.Markup.Xaml.MarkupExtensions; using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings; using Avalonia.Reactive; @@ -120,6 +121,52 @@ namespace Avalonia.Markup.UnitTests.Data } } + public class NamedElement + { + [Fact] + public void Should_Log_NameScope_Not_Found() + { + var target = new Decorator { }; + var root = new TestRoot(target); + var binding = new Binding("#source") { TypeResolver = ResolveType }; + + using (AssertLog(target, binding.Path, "NameScope not found.", "#source")) + { + target.Bind(Control.TagProperty, binding); + } + } + + [Fact] + public void Should_Not_Log_Element_Property_Null_For_Unrooted_Control() + { + var ns = new NameScope(); + var source = new Canvas { Name = "source" }; + var target = new Decorator { }; + var binding = new Binding("#source.DataContext.Foo") { TypeResolver = ResolveType, NameScope = new(ns) }; + var container = new StackPanel + { + [NameScope.NameScopeProperty] = ns, + Children = { source, target } + }; + + ns.Register(source.Name, source); + + using (AssertNoLog()) + { + target.Bind(Control.TagProperty, binding); + } + + // Sanity check to that the binding works when rooted: make sure that we're not just testing a broken + // binding! + using (AssertNoLog()) + { + var root = new TestRoot(container); + root.DataContext = new { Foo = "foo" }; + Assert.Equal("foo", target.Tag); + } + } + } + public class VisualAncestor { [Fact]