Browse Source

Improve numerical binding error messages.

pull/691/head
Steven Kirk 10 years ago
parent
commit
c9d3408c29
  1. 1
      samples/BindingTest/MainWindow.xaml
  2. 3
      samples/BindingTest/ViewModels/DataAnnotationsErrorViewModel.cs
  3. 2
      src/Avalonia.Base/AvaloniaObject.cs
  4. 2
      src/Avalonia.Base/PriorityValue.cs
  5. 41
      src/Avalonia.Base/Utilities/TypeUtilities.cs
  6. 3
      src/Markup/Avalonia.Markup/Data/ExpressionSubject.cs
  7. 12
      src/Markup/Avalonia.Markup/DefaultValueConverter.cs
  8. 2
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
  9. 2
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
  10. 6
      tests/Avalonia.Markup.UnitTests/Data/ExpressionSubjectTests.cs
  11. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs

1
samples/BindingTest/MainWindow.xaml

@ -82,6 +82,7 @@
<StackPanel Margin="18" Gap="4" MinWidth="200" DataContext="{Binding DataAnnotationsValidation}">
<TextBlock FontSize="16" Text="Data Annotations Validation"/>
<TextBox Watermark="Phone #" UseFloatingWatermark="True" Text="{Binding PhoneNumber}"/>
<TextBox Watermark="Less Than 10" UseFloatingWatermark="True" Text="{Binding Path=LessThan10}"/>
</StackPanel>
</StackPanel>
</TabItem>

3
samples/BindingTest/ViewModels/DataAnnotationsErrorViewModel.cs

@ -10,5 +10,8 @@ namespace BindingTest.ViewModels
[Phone]
[MaxLength(10)]
public string PhoneNumber { get; set; }
[Range(0, 9)]
public int LessThan10 { get; set; }
}
}

2
src/Avalonia.Base/AvaloniaObject.cs

@ -667,7 +667,7 @@ namespace Avalonia
Logger.Error(
LogArea.Binding,
this,
"Error binding to {Target}.{Property}: {Message}",
"Error in binding to {Target}.{Property}: {Message}",
this,
property,
ExceptionUtilities.GetMessage(notification.Error));

2
src/Avalonia.Base/PriorityValue.cs

@ -189,7 +189,7 @@ namespace Avalonia
LogEventLevel.Error,
LogArea.Binding,
_owner,
"Error binding to {Target}.{Property}: {Message}",
"Error in binding to {Target}.{Property}: {Message}",
_owner,
Property,
error.Error.Message);

41
src/Avalonia.Base/Utilities/TypeUtilities.cs

@ -27,6 +27,21 @@ namespace Avalonia.Utilities
{ typeof(short), new List<Type> { typeof(byte) } }
};
private static readonly Type[] NumericTypes = new[]
{
typeof(Byte),
typeof(Decimal),
typeof(Double),
typeof(Int16),
typeof(Int32),
typeof(Int64),
typeof(SByte),
typeof(Single),
typeof(UInt16),
typeof(UInt32),
typeof(UInt64),
};
/// <summary>
/// Returns a value indicating whether null can be assigned to the specified type.
/// </summary>
@ -208,5 +223,31 @@ namespace Avalonia.Utilities
return null;
}
}
/// <summary>
/// Determines if a type is numeric. Nullable numeric types are considered numeric.
/// </summary>
/// <returns>
/// True if the type is numberic; otherwise false.
/// </returns>
/// <remarks>
/// Boolean is not considered numeric.
/// </remarks>
public static bool IsNumeric(Type type)
{
if (type == null)
{
return false;
}
if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return IsNumeric(Nullable.GetUnderlyingType(type));
}
else
{
return NumericTypes.Contains(type);
}
}
}
}

3
src/Markup/Avalonia.Markup/Data/ExpressionSubject.cs

@ -139,9 +139,6 @@ namespace Avalonia.Markup.Data
"IValueConverter should not return non-errored BindingNotification.");
}
notification.Error = new InvalidCastException(
$"Error setting '{_inner.Expression}': {notification.Error.Message}");
notification.ErrorType = BindingErrorType.Error;
_errors.OnNext(notification);
if (_fallbackValue != AvaloniaProperty.UnsetValue)

12
src/Markup/Avalonia.Markup/DefaultValueConverter.cs

@ -43,7 +43,17 @@ namespace Avalonia.Markup
if (value != null)
{
var message = $"Could not convert '{value}' to '{targetType}'";
string message;
if (TypeUtilities.IsNumeric(targetType))
{
message = $"'{value}' is not a valid number.";
}
else
{
message = $"Could not convert '{value}' to '{targetType.Name}'.";
}
return new BindingNotification(new InvalidCastException(message), BindingErrorType.Error);
}

2
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

@ -332,7 +332,7 @@ namespace Avalonia.Base.UnitTests
var target = new Class1();
var source = new Subject<object>();
var called = false;
var expectedMessageTemplate = "Error binding to {Target}.{Property}: {Message}";
var expectedMessageTemplate = "Error in binding to {Target}.{Property}: {Message}";
LogCallback checkLogMessage = (level, area, src, mt, pv) =>
{

2
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs

@ -437,7 +437,7 @@ namespace Avalonia.Base.UnitTests
{
if (level == LogEventLevel.Error &&
area == LogArea.Binding &&
mt == "Error binding to {Target}.{Property}: {Message}" &&
mt == "Error in binding to {Target}.{Property}: {Message}" &&
pv.Length == 3 &&
pv[0] is Class1 &&
object.ReferenceEquals(pv[1], Class1.FooProperty) &&

6
tests/Avalonia.Markup.UnitTests/Data/ExpressionSubjectTests.cs

@ -123,7 +123,7 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(
new BindingNotification(
new InvalidCastException("Could not convert 'foo' to 'System.Int32'"),
new InvalidCastException("'foo' is not a valid number."),
BindingErrorType.Error,
42),
result);
@ -144,7 +144,7 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(
new BindingNotification(
new InvalidCastException("Could not convert 'foo' to 'System.Int32'"),
new InvalidCastException("'foo' is not a valid number."),
BindingErrorType.Error,
42),
result);
@ -294,7 +294,7 @@ namespace Avalonia.Markup.UnitTests.Data
new BindingNotification("1.2"),
new BindingNotification("3.4"),
new BindingNotification(
new InvalidCastException("Error setting 'DoubleValue': Could not convert 'bar' to 'System.Double'"),
new InvalidCastException("'bar' is not a valid number."),
BindingErrorType.Error)
},
result);

2
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlBindingTests.cs

@ -39,7 +39,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
{
if (level == LogEventLevel.Error &&
area == LogArea.Binding &&
mt == "Error binding to {Target}.{Property}: {Message}" &&
mt == "Error in binding to {Target}.{Property}: {Message}" &&
pv.Length == 3 &&
pv[0] is ProgressBar &&
object.ReferenceEquals(pv[1], ProgressBar.ValueProperty) &&

Loading…
Cancel
Save