Browse Source

Improve stream operator error message.

When a stream operator is applied to an unsupported type.
pull/764/head
Steven Kirk 10 years ago
parent
commit
715149b1f5
  1. 2
      src/Avalonia.Base/Avalonia.Base.csproj
  2. 57
      src/Avalonia.Base/Data/BindingChainException.cs
  3. 2
      src/Avalonia.Controls/TextBox.cs
  4. 2
      src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
  5. 4
      src/Markup/Avalonia.Markup/Data/ExpressionNode.cs
  6. 2
      src/Markup/Avalonia.Markup/Data/ExpressionObserver.cs
  7. 42
      src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs
  8. 33
      src/Markup/Avalonia.Markup/Data/MarkupBindingChainNullException.cs
  9. 2
      src/Markup/Avalonia.Markup/Data/StreamNode.cs
  10. 2
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_DataValidation.cs
  11. 25
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Observable.cs
  12. 4
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Property.cs

2
src/Avalonia.Base/Avalonia.Base.csproj

@ -44,7 +44,7 @@
<Compile Include="..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Data\BindingChainNullException.cs" />
<Compile Include="Data\BindingChainException.cs" />
<Compile Include="Data\BindingNotification.cs" />
<Compile Include="Data\IndexerBinding.cs" />
<Compile Include="Diagnostics\INotifyCollectionChangedDebug.cs" />

57
src/Avalonia.Base/Data/BindingChainNullException.cs → src/Avalonia.Base/Data/BindingChainException.cs

@ -10,36 +10,39 @@ namespace Avalonia.Data
/// requested binding expression could not be evaluated because of a null in one of the links
/// of the binding chain.
/// </summary>
public class BindingChainNullException : Exception
public class BindingChainException : Exception
{
private string _message;
/// <summary>
/// Initalizes a new instance of the <see cref="BindingChainNullException"/> class.
/// Initalizes a new instance of the <see cref="BindingChainException"/> class.
/// </summary>
public BindingChainNullException()
public BindingChainException()
{
}
/// <summary>
/// Initalizes a new instance of the <see cref="BindingChainNullException"/> class.
/// Initalizes a new instance of the <see cref="BindingChainException"/> class.
/// </summary>
public BindingChainNullException(string message)
/// <param name="message">The error message.</param>
public BindingChainException(string message)
{
_message = message;
}
/// <summary>
/// Initalizes a new instance of the <see cref="BindingChainNullException"/> class.
/// Initalizes a new instance of the <see cref="BindingChainException"/> class.
/// </summary>
/// <param name="message">The error message.</param>
/// <param name="expression">The expression.</param>
/// <param name="expressionNullPoint">
/// The point in the expression at which the null was encountered.
/// <param name="errorPoint">
/// The point in the expression at which the error was encountered.
/// </param>
public BindingChainNullException(string expression, string expressionNullPoint)
public BindingChainException(string message, string expression, string errorPoint)
{
_message = message;
Expression = expression;
ExpressionNullPoint = expressionNullPoint;
ExpressionErrorPoint = errorPoint;
}
/// <summary>
@ -48,37 +51,27 @@ namespace Avalonia.Data
public string Expression { get; protected set; }
/// <summary>
/// Gets the point in the expression at which the null was encountered.
/// Gets the point in the expression at which the error occured.
/// </summary>
public string ExpressionNullPoint { get; protected set; }
public string ExpressionErrorPoint { get; protected set; }
/// <inheritdoc/>
public override string Message
{
get
{
if (_message == null)
if (Expression != null && ExpressionErrorPoint != null)
{
_message = BuildMessage();
return $"{_message} in expression '{Expression}' at '{ExpressionErrorPoint}'.";
}
else if (ExpressionErrorPoint != null)
{
return $"{_message} in expression '{ExpressionErrorPoint}'.";
}
else
{
return $"{_message} in expression.";
}
return _message;
}
}
private string BuildMessage()
{
if (Expression != null && ExpressionNullPoint != null)
{
return $"'{ExpressionNullPoint}' is null in expression '{Expression}'.";
}
else if (ExpressionNullPoint != null)
{
return $"'{ExpressionNullPoint}' is null in expression.";
}
else
{
return "Null encountered in binding expression.";
}
}
}

2
src/Avalonia.Controls/TextBox.cs

@ -547,7 +547,7 @@ namespace Avalonia.Controls
var exceptions = aggregate == null ?
(IEnumerable<Exception>)new[] { exception } :
aggregate.InnerExceptions;
var filtered = exceptions.Where(x => !(x is BindingChainNullException)).ToList();
var filtered = exceptions.Where(x => !(x is BindingChainException)).ToList();
if (filtered.Count > 0)
{

2
src/Markup/Avalonia.Markup/Avalonia.Markup.csproj

@ -43,7 +43,7 @@
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Data\StreamNode.cs" />
<Compile Include="Data\MarkupBindingChainNullException.cs" />
<Compile Include="Data\MarkupBindingChainException.cs" />
<Compile Include="Data\CommonPropertyNames.cs" />
<Compile Include="Data\EmptyExpressionNode.cs" />
<Compile Include="Data\ExpressionNodeBuilder.cs" />

4
src/Markup/Avalonia.Markup/Data/ExpressionNode.cs

@ -88,7 +88,7 @@ namespace Avalonia.Markup.Data
protected virtual void NextValueChanged(object value)
{
var bindingBroken = BindingNotification.ExtractError(value) as MarkupBindingChainNullException;
var bindingBroken = BindingNotification.ExtractError(value) as MarkupBindingChainException;
bindingBroken?.AddNode(Description);
_observer.OnNext(value);
}
@ -152,7 +152,7 @@ namespace Avalonia.Markup.Data
private BindingNotification TargetNullNotification()
{
return new BindingNotification(
new MarkupBindingChainNullException(),
new MarkupBindingChainException("Null value"),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue);
}

2
src/Markup/Avalonia.Markup/Data/ExpressionObserver.cs

@ -235,7 +235,7 @@ namespace Avalonia.Markup.Data
}
else
{
var broken = BindingNotification.ExtractError(o) as MarkupBindingChainNullException;
var broken = BindingNotification.ExtractError(o) as MarkupBindingChainException;
if (broken != null)
{

42
src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Data;
namespace Avalonia.Markup.Data
{
internal class MarkupBindingChainException : BindingChainException
{
private IList<string> _nodes = new List<string>();
public MarkupBindingChainException(string message)
: base(message)
{
}
public MarkupBindingChainException(string message, string node)
: base(message)
{
AddNode(node);
}
public MarkupBindingChainException(string message, string expression, string expressionNullPoint)
: base(message, expression, expressionNullPoint)
{
_nodes = null;
}
public bool HasNodes => _nodes.Count > 0;
public void AddNode(string node) => _nodes.Add(node);
public void Commit(string expression)
{
Expression = expression;
ExpressionErrorPoint = string.Join(".", _nodes.Reverse())
.Replace(".!", "!")
.Replace(".[", "[")
.Replace(".^", "^");
_nodes = null;
}
}
}

33
src/Markup/Avalonia.Markup/Data/MarkupBindingChainNullException.cs

@ -1,33 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Avalonia.Data;
namespace Avalonia.Markup.Data
{
internal class MarkupBindingChainNullException : BindingChainNullException
{
private IList<string> _nodes = new List<string>();
public MarkupBindingChainNullException()
{
}
public MarkupBindingChainNullException(string expression, string expressionNullPoint)
: base(expression, expressionNullPoint)
{
_nodes = null;
}
public bool HasNodes => _nodes.Count > 0;
public void AddNode(string node) => _nodes.Add(node);
public void Commit(string expression)
{
Expression = expression;
ExpressionNullPoint = string.Join(".", _nodes.Reverse())
.Replace(".!", "!")
.Replace(".[", "[");
_nodes = null;
}
}
}

2
src/Markup/Avalonia.Markup/Data/StreamNode.cs

@ -24,7 +24,7 @@ namespace Avalonia.Markup.Data
// TODO: Improve error.
return Observable.Return(new BindingNotification(
new InvalidCastException("Value could not be streamed."),
new MarkupBindingChainException("Stream operator applied to unsupported type", Description),
BindingErrorType.Error));
}
}

2
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_DataValidation.cs

@ -143,7 +143,7 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(new[]
{
new BindingNotification(
new MarkupBindingChainNullException("Inner.MustBePositive", "Inner"),
new MarkupBindingChainException("Null value", "Inner.MustBePositive", "Inner"),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
}, result);

25
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Observable.cs

@ -110,6 +110,31 @@ namespace Avalonia.Markup.UnitTests.Data
}
}
[Fact]
public void Should_Return_BindingNotification_If_Stream_Operator_Applied_To_Not_Supported_Type()
{
using (var sync = UnitTestSynchronizationContext.Begin())
{
var data = new Class2("foo");
var target = new ExpressionObserver(data, "Foo^", true);
var result = new List<object>();
var sub = target.Subscribe(x => result.Add(x));
sync.ExecutePostedCallbacks();
Assert.Equal(
new[]
{
new BindingNotification(
new MarkupBindingChainException("Stream operator applied to unsupported type", "Foo^", "Foo^"),
BindingErrorType.Error)
},
result);
sub.Dispose();
}
}
private class Class1 : NotifyingBase
{
public Subject<Class2> Next { get; } = new Subject<Class2>();

4
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Property.cs

@ -146,7 +146,7 @@ namespace Avalonia.Markup.UnitTests.Data
new[]
{
new BindingNotification(
new MarkupBindingChainNullException("Foo.Bar.Baz", "Foo"),
new MarkupBindingChainException("Null value", "Foo.Bar.Baz", "Foo"),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
},
@ -274,7 +274,7 @@ namespace Avalonia.Markup.UnitTests.Data
{
"bar",
new BindingNotification(
new MarkupBindingChainNullException("Next.Next.Bar", "Next.Next"),
new MarkupBindingChainException("Null value", "Next.Next.Bar", "Next.Next"),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
"bar"

Loading…
Cancel
Save