Browse Source

Only send BindingNotifications on error.

Assume that a normal value when received by a property with data
validation enabled means no error.
pull/691/head
Steven Kirk 10 years ago
parent
commit
57e646583f
  1. 38
      src/Avalonia.Base/AvaloniaObject.cs
  2. 6
      src/Markup/Avalonia.Markup/Data/Plugins/DataValidatiorBase.cs
  3. 8
      src/Markup/Avalonia.Markup/Data/Plugins/IndeiValidationPlugin.cs
  4. 12
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_DataValidation.cs
  5. 2
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Observable.cs
  6. 8
      tests/Avalonia.Markup.UnitTests/Data/ExpressionSubjectTests.cs
  7. 10
      tests/Avalonia.Markup.UnitTests/Data/Plugins/ExceptionValidationPluginTests.cs
  8. 12
      tests/Avalonia.Markup.UnitTests/Data/Plugins/IndeiValidationPluginTests.cs
  9. 8
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Validation.cs

38
src/Avalonia.Base/AvaloniaObject.cs

@ -464,7 +464,7 @@ namespace Avalonia
/// <inheritdoc/>
void IPriorityValueOwner.BindingNotificationReceived(PriorityValue sender, BindingNotification notification)
{
BindingNotificationReceived(sender.Property, notification);
UpdateDataValidation(sender.Property, notification);
}
/// <inheritdoc/>
@ -499,14 +499,16 @@ namespace Avalonia
}
/// <summary>
/// Occurs when a <see cref="BindingNotification"/> is received for a property which has
/// data validation enabled.
/// Called to update the validation state for properties for which data validation is
/// enabled.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="notification">The binding notification.</param>
protected virtual void BindingNotificationReceived(
/// <param name="status">
/// The new validation status. A value of null indicates no validation error.
/// </param>
protected virtual void UpdateDataValidation(
AvaloniaProperty property,
BindingNotification notification)
BindingNotification status)
{
}
@ -647,20 +649,12 @@ namespace Avalonia
/// <returns></returns>
private void DirectBindingSet(AvaloniaProperty property, object value)
{
var validated = property.GetMetadata(GetType()).EnableDataValidation;
var notification = value as BindingNotification;
if (notification == null)
{
SetValue(property, value);
}
else
if (notification != null)
{
if (notification.HasValue)
{
SetValue(property, notification.Value);
}
BindingNotificationReceived(property, notification);
value = notification.Value;
if (notification.ErrorType == BindingErrorType.Error)
{
@ -673,6 +667,16 @@ namespace Avalonia
ExceptionUtilities.GetMessage(notification.Error));
}
}
if (notification?.HasValue != false)
{
SetValue(property, value);
}
if (validated)
{
UpdateDataValidation(property, notification);
}
}
/// <summary>

6
src/Markup/Avalonia.Markup/Data/Plugins/DataValidatiorBase.cs

@ -68,13 +68,11 @@ namespace Avalonia.Markup.Data.Plugins
/// </summary>
/// <param name="value">The value.</param>
/// <remarks>
/// Notifies the observer that the value has changed. The value will be wrapped in a
/// <see cref="BindingNotification"/> if it is not already a binding notification.
/// Notifies the observer that the value has changed.
/// </remarks>
protected virtual void InnerValueChanged(object value)
{
var notification = value as BindingNotification ?? new BindingNotification(value);
Observer.OnNext(notification);
Observer.OnNext(value);
}
}
}

8
src/Markup/Avalonia.Markup/Data/Plugins/IndeiValidationPlugin.cs

@ -40,7 +40,7 @@ namespace Avalonia.Markup.Data.Plugins
{
if (e.PropertyName == _name || string.IsNullOrEmpty(e.PropertyName))
{
Observer.OnNext(CreateBindingNotification(Value));
Observer.OnNext(BuildResult(Value));
}
}
@ -76,10 +76,10 @@ namespace Avalonia.Markup.Data.Plugins
protected override void InnerValueChanged(object value)
{
base.InnerValueChanged(CreateBindingNotification(value));
base.InnerValueChanged(BuildResult(value));
}
private BindingNotification CreateBindingNotification(object value)
private object BuildResult(object value)
{
var target = (INotifyDataErrorInfo)_reference.Target;
@ -98,7 +98,7 @@ namespace Avalonia.Markup.Data.Plugins
}
}
return new BindingNotification(value);
return value;
}
private Exception GenerateException(IList<string> errors)

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

@ -81,16 +81,16 @@ namespace Avalonia.Markup.UnitTests.Data
observer.SetValue("foo");
observer.SetValue(5);
Assert.Equal(new[]
Assert.Equal(new object[]
{
new BindingNotification(0),
0,
// Value is notified twice as ErrorsChanged is always called by IndeiTest.
new BindingNotification(5),
new BindingNotification(5),
5,
5,
// Value is first signalled without an error as validation hasn't been updated.
new BindingNotification(-5),
-5,
new BindingNotification(new Exception("Must be positive"), BindingErrorType.DataValidationError, -5),
// Exception is thrown by trying to set value to "foo".
@ -100,7 +100,7 @@ namespace Avalonia.Markup.UnitTests.Data
// Value is set then validation is updated.
new BindingNotification(new Exception("Must be positive"), BindingErrorType.DataValidationError, 5),
new BindingNotification(5),
5,
}, result);
}

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

@ -85,7 +85,7 @@ namespace Avalonia.Markup.UnitTests.Data
data.Next.OnNext(new Class2("foo"));
sync.ExecutePostedCallbacks();
Assert.Equal(new[] { new BindingNotification("foo") }, result);
Assert.Equal(new[] { "foo" }, result);
sub.Dispose();
Assert.Equal(0, data.PropertyChangedSubscriptionCount);

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

@ -288,11 +288,11 @@ namespace Avalonia.Markup.UnitTests.Data
target.OnNext("bar");
Assert.Equal(
new[]
new object[]
{
new BindingNotification("5.6"),
new BindingNotification("1.2"),
new BindingNotification("3.4"),
"5.6",
"1.2",
"3.4",
new BindingNotification(
new InvalidCastException("Error setting 'DoubleValue': Could not convert 'bar' to 'System.Double'"),
BindingErrorType.Error)

10
tests/Avalonia.Markup.UnitTests/Data/Plugins/ExceptionValidationPluginTests.cs

@ -14,7 +14,7 @@ namespace Avalonia.Markup.UnitTests.Data.Plugins
public class ExceptionValidationPluginTests
{
[Fact]
public void Produces_BindingNotifications()
public void Produces_Correct_Results()
{
var inpcAccessorPlugin = new InpcPropertyAccessorPlugin();
var validatorPlugin = new ExceptionValidationPlugin();
@ -28,12 +28,12 @@ namespace Avalonia.Markup.UnitTests.Data.Plugins
validator.SetValue(-2, BindingPriority.LocalValue);
validator.SetValue(6, BindingPriority.LocalValue);
Assert.Equal(new[]
Assert.Equal(new object[]
{
new BindingNotification(0),
new BindingNotification(5),
0,
5,
new BindingNotification(new ArgumentOutOfRangeException("value"), BindingErrorType.DataValidationError),
new BindingNotification(6),
6,
}, result);
}

12
tests/Avalonia.Markup.UnitTests/Data/Plugins/IndeiValidationPluginTests.cs

@ -14,7 +14,7 @@ namespace Avalonia.Markup.UnitTests.Data.Plugins
public class IndeiValidationPluginTests
{
[Fact]
public void Produces_BindingNotifications()
public void Produces_Correct_Results()
{
var inpcAccessorPlugin = new InpcPropertyAccessorPlugin();
var validatorPlugin = new IndeiValidationPlugin();
@ -29,19 +29,19 @@ namespace Avalonia.Markup.UnitTests.Data.Plugins
data.Maximum = 10;
data.Maximum = 5;
Assert.Equal(new[]
Assert.Equal(new object[]
{
new BindingNotification(0),
new BindingNotification(5),
0,
5,
// Value is first signalled without an error as validation hasn't been updated.
new BindingNotification(6),
6,
// Then the ErrorsChanged event is fired.
new BindingNotification(new Exception("Must be less than Maximum"), BindingErrorType.DataValidationError, 6),
// Maximum is changed to 10 so value is now valid.
new BindingNotification(6),
6,
// And Maximum is changed back to 5.
new BindingNotification(new Exception("Must be less than Maximum"), BindingErrorType.DataValidationError, 6),

8
tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Validation.cs

@ -45,10 +45,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
Assert.Equal(
new[]
{
new BindingNotification(5),
new BindingNotification(6),
null, // 5
null, // 6
new BindingNotification(new ArgumentOutOfRangeException("value"), BindingErrorType.DataValidationError),
new BindingNotification(7),
null, // 7
},
target.Notifications.AsEnumerable());
}
@ -94,7 +94,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
public IList<BindingNotification> Notifications { get; } = new List<BindingNotification>();
protected override void BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification)
protected override void UpdateDataValidation(AvaloniaProperty property, BindingNotification notification)
{
Notifications.Add(notification);
}

Loading…
Cancel
Save