diff --git a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs index a0868152f6..aa4fe55f79 100644 --- a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs +++ b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using Avalonia.Data.Core.Plugins; namespace Avalonia.Data.Core @@ -49,6 +50,18 @@ namespace Avalonia.Data.Core var accessor = plugin?.Start(reference, PropertyName); + // We need to handle accessor fallback before handling validation. Validators do not support null accessors. + if (accessor == null) + { + reference.TryGetTarget(out object instance); + + var message = $"Could not find a matching property accessor for '{PropertyName}' on '{instance}'"; + + var exception = new MissingMemberException(message); + + accessor = new PropertyError(new BindingNotification(exception, BindingErrorType.Error)); + } + if (_enableValidation && Next == null) { foreach (var validator in ExpressionObserver.DataValidators) @@ -60,15 +73,9 @@ namespace Avalonia.Data.Core } } - if (accessor == null) + if (accessor is null) { - reference.TryGetTarget(out object instance); - - var message = $"Could not find a matching property accessor for '{PropertyName}' on '{instance}'"; - - var exception = new MissingMemberException(message); - - accessor = new PropertyError(new BindingNotification(exception, BindingErrorType.Error)); + throw new AvaloniaInternalException("Data validators must return non-null accessor."); } _accessor = accessor; diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/ExpressionObserverTests_Property.cs b/tests/Avalonia.Base.UnitTests/Data/Core/ExpressionObserverTests_Property.cs index df871a67b4..784046ac0b 100644 --- a/tests/Avalonia.Base.UnitTests/Data/Core/ExpressionObserverTests_Property.cs +++ b/tests/Avalonia.Base.UnitTests/Data/Core/ExpressionObserverTests_Property.cs @@ -578,6 +578,28 @@ namespace Avalonia.Base.UnitTests.Data.Core Assert.True(true); } + [Fact] + public void Should_Not_Throw_Exception_When_Enabling_Data_Validation_On_Missing_Member() + { + var source = new Class1(); + var target = new PropertyAccessorNode("NotFound", true); + + target.Target = new WeakReference(source); + + var result = new List(); + + target.Subscribe(x => result.Add(x)); + + Assert.Equal( + new object[] + { + new BindingNotification( + new MissingMemberException("Could not find a matching property accessor for 'NotFound' on 'Avalonia.Base.UnitTests.Data.Core.ExpressionObserverTests_Property+Class1'"), + BindingErrorType.Error), + }, + result); + } + private interface INext { int PropertyChangedSubscriptionCount { get; }