diff --git a/src/Markup/Perspex.Markup/DefaultValueConverter.cs b/src/Markup/Perspex.Markup/DefaultValueConverter.cs
index 9f535081a7..8a24b53612 100644
--- a/src/Markup/Perspex.Markup/DefaultValueConverter.cs
+++ b/src/Markup/Perspex.Markup/DefaultValueConverter.cs
@@ -5,6 +5,7 @@ using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
+using Perspex.Data;
using Perspex.Logging;
using Perspex.Utilities;
@@ -42,12 +43,8 @@ namespace Perspex.Markup
if (value != null)
{
- Logger.Error(
- LogArea.Binding,
- this,
- "Could not convert {Value} to {Type}",
- value,
- targetType);
+ var message = $"Could not convert {value} to {targetType}";
+ return new BindingError(new InvalidCastException(message));
}
return PerspexProperty.UnsetValue;
diff --git a/src/Perspex.Base/Data/BindingError.cs b/src/Perspex.Base/Data/BindingError.cs
index d29b06fea9..b9330bd278 100644
--- a/src/Perspex.Base/Data/BindingError.cs
+++ b/src/Perspex.Base/Data/BindingError.cs
@@ -10,8 +10,12 @@ namespace Perspex.Data
///
///
/// When produced by a binding source observable, informs the binding system that an error
- /// occurred. It causes a binding error to be logged: the value of the bound property will not
- /// change.
+ /// occurred. It can also provide an optional fallback value to be pushed to the binding
+ /// target.
+ ///
+ /// Instead of using , one could simply not push a value (in the
+ /// case of a no fallback value) or push a fallback value, but BindingError also causes an
+ /// error to be logged with the correct binding target.
///
public class BindingError
{
@@ -24,9 +28,32 @@ namespace Perspex.Data
Exception = exception;
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// An exception describing the binding error.
+ /// The fallback value.
+ public BindingError(Exception exception, object fallbackValue)
+ {
+ Exception = exception;
+ FallbackValue = fallbackValue;
+ UseFallbackValue = true;
+ }
+
///
/// Gets the exception describing the binding error.
///
public Exception Exception { get; }
+
+ ///
+ /// Get the fallback value.
+ ///
+ public object FallbackValue { get; }
+
+ ///
+ /// Get a value indicating whether the fallback value should be pushed to the binding
+ /// target.
+ ///
+ public bool UseFallbackValue { get; }
}
}
diff --git a/src/Perspex.Base/PerspexObject.cs b/src/Perspex.Base/PerspexObject.cs
index 86ebb45921..34bf428cc1 100644
--- a/src/Perspex.Base/PerspexObject.cs
+++ b/src/Perspex.Base/PerspexObject.cs
@@ -672,6 +672,11 @@ namespace Perspex
}
else
{
+ if (error.UseFallbackValue)
+ {
+ SetValue(property, error.FallbackValue);
+ }
+
Logger.Error(
LogArea.Binding,
this,
diff --git a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs
index 097614013d..78292a5940 100644
--- a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs
+++ b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs
@@ -267,6 +267,32 @@ namespace Perspex.Base.UnitTests
Assert.Equal("first", target2.GetValue(Class1.FooProperty));
}
+ [Fact]
+ public void BindingError_Does_Not_Cause_Target_Update()
+ {
+ var target = new Class1();
+ var source = new Subject