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();