diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index 95be67c98c..e82af4c834 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -2,6 +2,7 @@
netstandard1.1
false
+ Avalonia
true
diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index 1bdf1eb5e3..7b8da28f53 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/src/Avalonia.Base/AvaloniaObject.cs
@@ -664,16 +664,7 @@ namespace Avalonia
if (notification != null)
{
- if (notification.ErrorType == BindingErrorType.Error)
- {
- Logger.Error(
- LogArea.Binding,
- this,
- "Error in binding to {Target}.{Property}: {Message}",
- this,
- property,
- ExceptionUtilities.GetMessage(notification.Error));
- }
+ notification.LogIfError(this, property);
if (notification.HasValue)
{
diff --git a/src/Avalonia.Base/Data/BindingNotification.cs b/src/Avalonia.Base/Data/BindingNotification.cs
index ecaf59e174..3394bc4f1a 100644
--- a/src/Avalonia.Base/Data/BindingNotification.cs
+++ b/src/Avalonia.Base/Data/BindingNotification.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
+using Avalonia.Logging;
namespace Avalonia.Data
{
diff --git a/src/Avalonia.Base/Logging/LoggerExtensions.cs b/src/Avalonia.Base/Logging/LoggerExtensions.cs
new file mode 100644
index 0000000000..24e44bf9de
--- /dev/null
+++ b/src/Avalonia.Base/Logging/LoggerExtensions.cs
@@ -0,0 +1,53 @@
+using System;
+using Avalonia.Data;
+
+namespace Avalonia.Logging
+{
+ internal static class LoggerExtensions
+ {
+ public static void LogIfError(
+ this BindingNotification notification,
+ object source,
+ AvaloniaProperty property)
+ {
+ if (notification.ErrorType == BindingErrorType.Error)
+ {
+ if (notification.Error is AggregateException aggregate)
+ {
+ foreach (var inner in aggregate.InnerExceptions)
+ {
+ LogError(source, property, inner);
+ }
+ }
+ else
+ {
+ LogError(source, property, notification.Error);
+ }
+ }
+ }
+
+ private static void LogError(object source, AvaloniaProperty property, Exception e)
+ {
+ var level = LogEventLevel.Warning;
+
+ if (e is BindingChainException b &&
+ !string.IsNullOrEmpty(b.Expression) &&
+ string.IsNullOrEmpty(b.ExpressionErrorPoint))
+ {
+ // The error occurred at the root of the binding chain: it's possible that the
+ // DataContext isn't set up yet, so log at Information level instead of Warning
+ // to prevent spewing hundreds of errors.
+ level = LogEventLevel.Information;
+ }
+
+ Logger.Log(
+ level,
+ LogArea.Binding,
+ source,
+ "Error in binding to {Target}.{Property}: {Message}",
+ source,
+ property,
+ e.Message);
+ }
+ }
+}
diff --git a/src/Avalonia.Base/PriorityValue.cs b/src/Avalonia.Base/PriorityValue.cs
index c33d50ff0e..3726fb7ae5 100644
--- a/src/Avalonia.Base/PriorityValue.cs
+++ b/src/Avalonia.Base/PriorityValue.cs
@@ -189,14 +189,7 @@ namespace Avalonia
/// The binding error.
public void LevelError(PriorityLevel level, BindingNotification error)
{
- Logger.Log(
- LogEventLevel.Error,
- LogArea.Binding,
- Owner,
- "Error in binding to {Target}.{Property}: {Message}",
- Owner,
- Property,
- error.Error.Message);
+ error.LogIfError(Owner, Property);
}
///
diff --git a/src/Avalonia.Base/Utilities/ExceptionUtilities.cs b/src/Avalonia.Base/Utilities/ExceptionUtilities.cs
deleted file mode 100644
index fa8c5be788..0000000000
--- a/src/Avalonia.Base/Utilities/ExceptionUtilities.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Avalonia.Utilities
-{
- internal static class ExceptionUtilities
- {
- public static string GetMessage(Exception e)
- {
- var aggregate = e as AggregateException;
-
- if (aggregate != null)
- {
- return string.Join(" | ", aggregate.InnerExceptions.Select(x => x.Message));
- }
-
- return e.Message;
- }
- }
-}
diff --git a/src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs b/src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs
index 51afe1ffbf..ddfcf531eb 100644
--- a/src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs
+++ b/src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs
@@ -32,10 +32,12 @@ namespace Avalonia.Markup.Data
public void Commit(string expression)
{
Expression = expression;
- ExpressionErrorPoint = string.Join(".", _nodes.Reverse())
- .Replace(".!", "!")
- .Replace(".[", "[")
- .Replace(".^", "^");
+ ExpressionErrorPoint = _nodes != null ?
+ string.Join(".", _nodes.Reverse())
+ .Replace(".!", "!")
+ .Replace(".[", "[")
+ .Replace(".^", "^") :
+ string.Empty;
_nodes = null;
}
}
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
index ecb555252d..6f6a675b0e 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
@@ -389,7 +389,7 @@ namespace Avalonia.Base.UnitTests
LogCallback checkLogMessage = (level, area, src, mt, pv) =>
{
- if (level == LogEventLevel.Error &&
+ if (level == LogEventLevel.Warning &&
area == LogArea.Binding &&
mt == "Error in binding to {Target}.{Property}: {Message}" &&
pv.Length == 3 &&
diff --git a/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Property.cs b/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Property.cs
index bdcd39d997..959dce7181 100644
--- a/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Property.cs
+++ b/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Property.cs
@@ -58,43 +58,63 @@ namespace Avalonia.Markup.UnitTests.Data
}
[Fact]
- public async void Should_Return_UnsetValue_For_Root_Null()
+ public async void Should_Return_BindingNotification_Error_For_Root_Null()
{
var data = new Class3 { Foo = "foo" };
var target = new ExpressionObserver(default(object), "Foo");
var result = await target.Take(1);
- Assert.Equal(AvaloniaProperty.UnsetValue, result);
+ Assert.Equal(
+ new BindingNotification(
+ new MarkupBindingChainException("Null value", "Foo", string.Empty),
+ BindingErrorType.Error,
+ AvaloniaProperty.UnsetValue),
+ result);
}
[Fact]
- public async void Should_Return_UnsetValue_For_Root_UnsetValue()
+ public async void Should_Return_BindingNotification_Error_For_Root_UnsetValue()
{
var data = new Class3 { Foo = "foo" };
var target = new ExpressionObserver(AvaloniaProperty.UnsetValue, "Foo");
var result = await target.Take(1);
- Assert.Equal(AvaloniaProperty.UnsetValue, result);
+ Assert.Equal(
+ new BindingNotification(
+ new MarkupBindingChainException("Null value", "Foo", string.Empty),
+ BindingErrorType.Error,
+ AvaloniaProperty.UnsetValue),
+ result);
}
[Fact]
- public async void Should_Return_UnsetValue_For_Observable_Root_Null()
+ public async void Should_Return_BindingNotification_Error_For_Observable_Root_Null()
{
var data = new Class3 { Foo = "foo" };
var target = new ExpressionObserver(Observable.Return(default(object)), "Foo");
var result = await target.Take(1);
- Assert.Equal(AvaloniaProperty.UnsetValue, result);
+ Assert.Equal(
+ new BindingNotification(
+ new MarkupBindingChainException("Null value", "Foo", string.Empty),
+ BindingErrorType.Error,
+ AvaloniaProperty.UnsetValue),
+ result);
}
[Fact]
- public async void Should_Return_UnsetValue_For_Observable_Root_UnsetValue()
+ public async void Should_Return_BindingNotification_Error_For_Observable_Root_UnsetValue()
{
var data = new Class3 { Foo = "foo" };
var target = new ExpressionObserver(Observable.Return(AvaloniaProperty.UnsetValue), "Foo");
var result = await target.Take(1);
- Assert.Equal(AvaloniaProperty.UnsetValue, result);
+ Assert.Equal(
+ new BindingNotification(
+ new MarkupBindingChainException("Null value", "Foo", string.Empty),
+ BindingErrorType.Error,
+ AvaloniaProperty.UnsetValue),
+ result);
}
[Fact]
@@ -492,7 +512,10 @@ namespace Avalonia.Markup.UnitTests.Data
{
"foo",
"bar",
- AvaloniaProperty.UnsetValue,
+ new BindingNotification(
+ new MarkupBindingChainException("Null value", "Foo", string.Empty),
+ BindingErrorType.Error,
+ AvaloniaProperty.UnsetValue)
},
result);
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs
index 0c2151850f..a508c21747 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs
@@ -37,7 +37,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
LogCallback checkLogMessage = (level, area, src, mt, pv) =>
{
- if (level == LogEventLevel.Error &&
+ if (level == LogEventLevel.Warning &&
area == LogArea.Binding &&
mt == "Error in binding to {Target}.{Property}: {Message}" &&
pv.Length == 3 &&