@ -2,8 +2,10 @@
using System.Collections ;
using System.Collections.Generic ;
using System.ComponentModel.DataAnnotations ;
using System.Globalization ;
using System.Linq ;
using Avalonia.Data ;
using Avalonia.Data.Converters ;
using Avalonia.Data.Core.Plugins ;
using Avalonia.UnitTests ;
using Xunit ;
@ -576,6 +578,115 @@ public partial class BindingExpressionTests
}
}
private class InvalidIdConverter ( BindingErrorType errorType ) : IValueConverter
{
private readonly BindingErrorType _ errorType = errorType ;
public object? Convert ( object? value , Type targetType , object? parameter , CultureInfo culture ) = > value ;
public object? ConvertBack ( object? value , Type targetType , object? parameter , CultureInfo culture )
{
if ( value is int i )
return i ;
if ( value is string s & & int . TryParse ( s , out var parsed ) )
return parsed ;
return new BindingNotification (
new FormatException ( $"'{value}' is not a valid ID." ) ,
_ errorType ) ;
}
}
[Fact]
public void ConvertBack_DataValidationError_Updates_Data_Validation ( )
{
var data = new ViewModel { IntValue = 1 } ;
var target = CreateTargetWithSource (
data ,
o = > o . IntValue ,
converter : new InvalidIdConverter ( BindingErrorType . DataValidationError ) ,
enableDataValidation : true ,
mode : BindingMode . TwoWay ,
targetProperty : TargetClass . TagProperty ) ;
target . Tag = "42" ;
AssertNoError ( target , TargetClass . TagProperty ) ;
target . Tag = "0x555g" ;
Assert . Equal ( 4 2 , data . IntValue ) ;
AssertBindingError (
target ,
TargetClass . TagProperty ,
new FormatException ( "'0x555g' is not a valid ID." ) ,
BindingErrorType . DataValidationError ) ;
GC . KeepAlive ( data ) ;
}
[Fact]
public void ConvertBack_Error_Updates_Data_Validation ( )
{
var data = new ViewModel { IntValue = 1 } ;
var target = CreateTargetWithSource (
data ,
o = > o . IntValue ,
converter : new InvalidIdConverter ( BindingErrorType . Error ) ,
enableDataValidation : true ,
mode : BindingMode . TwoWay ,
targetProperty : TargetClass . TagProperty ) ;
target . Tag = "42" ;
AssertNoError ( target , TargetClass . TagProperty ) ;
target . Tag = "0x555g" ;
Assert . Equal ( 4 2 , data . IntValue ) ;
AssertBindingError (
target ,
TargetClass . TagProperty ,
new FormatException ( "'0x555g' is not a valid ID." ) ,
BindingErrorType . Error ) ;
GC . KeepAlive ( data ) ;
}
[Fact]
public void ConvertBack_Notification_With_Value_Writes_Value ( )
{
var data = new ViewModel { IntValue = 1 } ;
var target = CreateTargetWithSource (
data ,
o = > o . IntValue ,
converter : new FuncValueConverter ( v = > new BindingNotification ( 4 2 ) ) ,
enableDataValidation : true ,
mode : BindingMode . TwoWay ,
targetProperty : TargetClass . TagProperty ) ;
target . Tag = "foo" ;
Assert . Equal ( 4 2 , data . IntValue ) ;
AssertNoError ( target , TargetClass . TagProperty ) ;
GC . KeepAlive ( data ) ;
}
private class FuncValueConverter : IValueConverter
{
private readonly Func < object? , object? > _ convertBack ;
public FuncValueConverter ( Func < object? , object? > convertBack ) = > _ convertBack = convertBack ;
public object? Convert ( object? value , Type targetType , object? parameter , CultureInfo culture )
= > value ;
public object? ConvertBack ( object? value , Type targetType , object? parameter , CultureInfo culture )
= > _ convertBack ( value ) ;
}
private static void AssertNoError ( TargetClass target , AvaloniaProperty property )
{
Assert . False ( target . BindingNotifications . TryGetValue ( property , out var notification ) ) ;