diff --git a/src/Markup/Perspex.Markup/Data/ExpressionSubject.cs b/src/Markup/Perspex.Markup/Data/ExpressionSubject.cs index 2a94e8ef53..46df3ce490 100644 --- a/src/Markup/Perspex.Markup/Data/ExpressionSubject.cs +++ b/src/Markup/Perspex.Markup/Data/ExpressionSubject.cs @@ -153,7 +153,9 @@ namespace Perspex.Markup.Data private object ConvertValue(object value) { - var converted = Converter.Convert( + var converted = + value as BindingError ?? + Converter.Convert( value, _targetType, ConverterParameter, diff --git a/src/Markup/Perspex.Markup/Data/Plugins/IPropertyAccessorPlugin.cs b/src/Markup/Perspex.Markup/Data/Plugins/IPropertyAccessorPlugin.cs index 7bbdb0ff68..8a431041d0 100644 --- a/src/Markup/Perspex.Markup/Data/Plugins/IPropertyAccessorPlugin.cs +++ b/src/Markup/Perspex.Markup/Data/Plugins/IPropertyAccessorPlugin.cs @@ -26,7 +26,7 @@ namespace Perspex.Markup.Data.Plugins /// A function to call when the property changes. /// /// An interface through which future interactions with the - /// property will be made, or null if the property was not found. + /// property will be made. /// IPropertyAccessor Start( WeakReference reference, diff --git a/src/Markup/Perspex.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs b/src/Markup/Perspex.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs index ac38ee74f9..64421ddc57 100644 --- a/src/Markup/Perspex.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs +++ b/src/Markup/Perspex.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs @@ -38,7 +38,7 @@ namespace Perspex.Markup.Data.Plugins /// A function to call when the property changes. /// /// An interface through which future interactions with the - /// property will be made, or null if the property was not found. + /// property will be made. /// public IPropertyAccessor Start( WeakReference reference, @@ -58,14 +58,9 @@ namespace Perspex.Markup.Data.Plugins } else { - Logger.Error( - LogArea.Binding, - this, - "Could not find CLR property {Property} on {Source}", - propertyName, - instance); - - return null; + var message = $"Could not find CLR property '{propertyName}' on '{instance}'"; + var exception = new MissingMemberException(message); + return new PropertyError(new BindingError(exception)); } } diff --git a/src/Markup/Perspex.Markup/Data/Plugins/PerspexPropertyAccessorPlugin.cs b/src/Markup/Perspex.Markup/Data/Plugins/PerspexPropertyAccessorPlugin.cs index 4d2f6c5cb0..bc16989a7a 100644 --- a/src/Markup/Perspex.Markup/Data/Plugins/PerspexPropertyAccessorPlugin.cs +++ b/src/Markup/Perspex.Markup/Data/Plugins/PerspexPropertyAccessorPlugin.cs @@ -33,7 +33,7 @@ namespace Perspex.Markup.Data.Plugins /// A function to call when the property changes. /// /// An interface through which future interactions with the - /// property will be made, or null if the property was not found. + /// property will be made. /// public IPropertyAccessor Start( WeakReference reference, @@ -52,14 +52,14 @@ namespace Perspex.Markup.Data.Plugins { return new Accessor(new WeakReference(o), p, changed); } + else if (instance != PerspexProperty.UnsetValue) + { + var message = $"Could not find PerspexProperty '{propertyName}' on '{instance}'"; + var exception = new MissingMemberException(message); + return new PropertyError(new BindingError(exception)); + } else { - Logger.Error( - LogArea.Binding, - this, - "Could not find PerspexProperty {Property} on {Source}", - propertyName, - instance); return null; } } diff --git a/src/Markup/Perspex.Markup/Data/Plugins/PropertyError.cs b/src/Markup/Perspex.Markup/Data/Plugins/PropertyError.cs new file mode 100644 index 0000000000..c147a315d5 --- /dev/null +++ b/src/Markup/Perspex.Markup/Data/Plugins/PropertyError.cs @@ -0,0 +1,39 @@ +using System; +using Perspex.Data; + +namespace Perspex.Markup.Data.Plugins +{ + /// + /// An that represents an error. + /// + public class PropertyError : IPropertyAccessor + { + private BindingError _error; + + /// + /// Initializes a new instance of the class. + /// + /// The error to report. + public PropertyError(BindingError error) + { + _error = error; + } + + /// + public Type PropertyType => null; + + /// + public object Value => _error; + + /// + public void Dispose() + { + } + + /// + public bool SetValue(object value, BindingPriority priority) + { + return false; + } + } +} diff --git a/src/Markup/Perspex.Markup/Data/PropertyAccessorNode.cs b/src/Markup/Perspex.Markup/Data/PropertyAccessorNode.cs index 1aa703ff8f..14883e449a 100644 --- a/src/Markup/Perspex.Markup/Data/PropertyAccessorNode.cs +++ b/src/Markup/Perspex.Markup/Data/PropertyAccessorNode.cs @@ -48,7 +48,7 @@ namespace Perspex.Markup.Data { var instance = reference.Target; - if (instance != null) + if (instance != null && instance != PerspexProperty.UnsetValue) { var plugin = ExpressionObserver.PropertyAccessors.FirstOrDefault(x => x.Match(reference)); diff --git a/src/Markup/Perspex.Markup/DefaultValueConverter.cs b/src/Markup/Perspex.Markup/DefaultValueConverter.cs index 8a24b53612..0cc5f0df8e 100644 --- a/src/Markup/Perspex.Markup/DefaultValueConverter.cs +++ b/src/Markup/Perspex.Markup/DefaultValueConverter.cs @@ -43,7 +43,7 @@ namespace Perspex.Markup if (value != null) { - var message = $"Could not convert {value} to {targetType}"; + var message = $"Could not convert '{value}' to '{targetType}'"; return new BindingError(new InvalidCastException(message)); } diff --git a/src/Markup/Perspex.Markup/Perspex.Markup.csproj b/src/Markup/Perspex.Markup/Perspex.Markup.csproj index f5b61e2cea..5427d97346 100644 --- a/src/Markup/Perspex.Markup/Perspex.Markup.csproj +++ b/src/Markup/Perspex.Markup/Perspex.Markup.csproj @@ -56,6 +56,7 @@ + diff --git a/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Property.cs b/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Property.cs index 8586f89ab6..1a201dcd9c 100644 --- a/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Property.cs +++ b/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Property.cs @@ -7,6 +7,7 @@ using System.Reactive; using System.Reactive.Linq; using System.Reactive.Subjects; using Microsoft.Reactive.Testing; +using Perspex.Data; using Perspex.Markup.Data; using Perspex.UnitTests; using Xunit; @@ -74,13 +75,17 @@ namespace Perspex.Markup.UnitTests.Data } [Fact] - public async void Should_Not_Have_Value_For_Broken_Chain() + public async void Should_Return_BindingError_For_Broken_Chain() { var data = new { Foo = new { Bar = 1 } }; var target = new ExpressionObserver(data, "Foo.Bar.Baz"); var result = await target.Take(1); - Assert.Equal(PerspexProperty.UnsetValue, result); + Assert.IsType(result); + + var error = result as BindingError; + Assert.IsType(error.Exception); + Assert.Equal("Could not find CLR property 'Baz' on '1'", error.Exception.Message); } [Fact] @@ -209,7 +214,10 @@ namespace Perspex.Markup.UnitTests.Data data.Next = breaking; data.Next = new Class2 { Bar = "baz" }; - Assert.Equal(new[] { "bar", PerspexProperty.UnsetValue, "baz" }, result); + Assert.Equal(3, result.Count); + Assert.Equal("bar", result[0]); + Assert.IsType(result[1]); + Assert.Equal("baz", result[2]); sub.Dispose();