Browse Source

Only log binding errors when attached to a tree.

pull/2264/head
Steven Kirk 8 years ago
parent
commit
6961d55e7a
  1. 43
      src/Avalonia.Base/AvaloniaObject.cs
  2. 8
      src/Avalonia.Base/IPriorityValueOwner.cs
  3. 53
      src/Avalonia.Base/Logging/LoggerExtensions.cs
  4. 2
      src/Avalonia.Base/PriorityValue.cs
  5. 5
      src/Avalonia.Base/ValueStore.cs
  6. 29
      src/Avalonia.Visuals/Visual.cs

43
src/Avalonia.Base/AvaloniaObject.cs

@ -415,6 +415,7 @@ namespace Avalonia
internal void BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification)
{
LogIfError(property, notification);
UpdateDataValidation(property, notification);
}
@ -446,6 +447,23 @@ namespace Avalonia
});
}
/// <summary>
/// Logs a binding error for a property.
/// </summary>
/// <param name="property">The property that the error occurred on.</param>
/// <param name="e">The binding error.</param>
protected internal virtual void LogBindingError(AvaloniaProperty property, Exception e)
{
Logger.Log(
LogEventLevel.Warning,
LogArea.Binding,
this,
"Error in binding to {Target}.{Property}: {Message}",
this,
property,
e.Message);
}
/// <summary>
/// Called to update the validation state for properties for which data validation is
/// enabled.
@ -647,7 +665,7 @@ namespace Avalonia
if (notification != null)
{
notification.LogIfError(this, property);
LogIfError(property, notification);
value = notification.Value;
}
@ -779,6 +797,29 @@ namespace Avalonia
return description?.Description ?? o.ToString();
}
/// <summary>
/// Logs a mesage if the notification represents a binding error.
/// </summary>
/// <param name="property">The property being bound.</param>
/// <param name="notification">The binding notification.</param>
private void LogIfError(AvaloniaProperty property, BindingNotification notification)
{
if (notification.ErrorType == BindingErrorType.Error)
{
if (notification.Error is AggregateException aggregate)
{
foreach (var inner in aggregate.InnerExceptions)
{
LogBindingError(property, inner);
}
}
else
{
LogBindingError(property, notification.Error);
}
}
}
/// <summary>
/// Logs a property set message.
/// </summary>

8
src/Avalonia.Base/IPriorityValueOwner.cs

@ -1,6 +1,7 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Data;
using Avalonia.Utilities;
@ -28,6 +29,13 @@ namespace Avalonia
/// <param name="notification">The notification.</param>
void BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification);
/// <summary>
/// Logs a binding error.
/// </summary>
/// <param name="property">The property the error occurred on.</param>
/// <param name="e">The binding error.</param>
void LogError(AvaloniaProperty property, Exception e);
/// <summary>
/// Ensures that the current thread is the UI thread.
/// </summary>

53
src/Avalonia.Base/Logging/LoggerExtensions.cs

@ -1,53 +0,0 @@
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);
}
}
}

2
src/Avalonia.Base/PriorityValue.cs

@ -197,7 +197,7 @@ namespace Avalonia
/// <param name="error">The binding error.</param>
public void LevelError(PriorityLevel level, BindingNotification error)
{
error.LogIfError(Owner, Property);
Owner.LogError(Property, error.Error);
}
/// <summary>

5
src/Avalonia.Base/ValueStore.cs

@ -100,6 +100,11 @@ namespace Avalonia
_owner.PriorityValueChanged(property, priority, oldValue, newValue);
}
public void LogError(AvaloniaProperty property, Exception e)
{
_owner.LogBindingError(property, e);
}
public IDictionary<AvaloniaProperty, object> GetSetValues() => _values;
public object GetValue(AvaloniaProperty property)

29
src/Avalonia.Visuals/Visual.cs

@ -8,6 +8,7 @@ using System.Reactive.Linq;
using Avalonia.Collections;
using Avalonia.Data;
using Avalonia.Logging;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Rendering;
using Avalonia.VisualTree;
@ -448,6 +449,34 @@ namespace Avalonia
RaisePropertyChanged(VisualParentProperty, oldParent, newParent, BindingPriority.LocalValue);
}
protected override sealed void LogBindingError(AvaloniaProperty property, Exception e)
{
// Don't log a binding error unless the control is attached to a logical or visual tree.
// In theory this should only need to check for logical tree attachment, but in practise
// due to ContentControlMixin only taking effect when the template has finished being
// applied, some controls are attached to the visual tree before the logical tree.
if (((ILogical)this).IsAttachedToLogicalTree || ((IVisual)this).IsAttachedToVisualTree)
{
if (e is BindingChainException b &&
string.IsNullOrEmpty(b.ExpressionErrorPoint) &&
DataContext == null)
{
// The error occurred at the root of the binding chain and DataContext is null;
// don't log this - the DataContext probably hasn't been set up yet.
return;
}
Logger.Log(
LogEventLevel.Warning,
LogArea.Binding,
this,
"Error in binding to {Target}.{Property}: {Message}",
this,
property,
e.Message);
}
}
/// <summary>
/// Gets the visual offset from the specified ancestor.
/// </summary>

Loading…
Cancel
Save