Browse Source

Add FuncValueConverter with parameter support (#15323)

* Add FuncValueConverter with parameter support

* Fix summary comment

* Fix summary comment

* Add Equal and NotEqual converters to a ObjectConverters class

* Fix condition for Equal

* Added ObjectConverters unit tests
Wiesław Šoltés 1 year ago
parent
commit
dd27d61b9a

+ 41 - 1
src/Avalonia.Base/Data/Converters/FuncValueConverter.cs

@@ -5,7 +5,7 @@ using Avalonia.Utilities;
 namespace Avalonia.Data.Converters
 {
     /// <summary>
-    /// A general purpose <see cref="IValueConverter"/> that uses a <see cref="Func{T1, TResult}"/>
+    /// A general purpose <see cref="IValueConverter"/> that uses a <see cref="Func{TIn, TResult}"/>
     /// to provide the converter logic.
     /// </summary>
     /// <typeparam name="TIn">The input type.</typeparam>
@@ -42,4 +42,44 @@ namespace Avalonia.Data.Converters
             throw new NotImplementedException();
         }
     }
+
+    /// <summary>
+    /// A general purpose <see cref="IValueConverter"/> that uses a <see cref="Func{TIn, TParam, TOut}"/>
+    /// to provide the converter logic.
+    /// </summary>
+    /// <typeparam name="TIn">The input type.</typeparam>
+    /// <typeparam name="TParam">The param type.</typeparam>
+    /// <typeparam name="TOut">The output type.</typeparam>
+    public class FuncValueConverter<TIn, TParam, TOut> : IValueConverter
+    {
+        private readonly Func<TIn?, TParam?, TOut> _convert;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="FuncValueConverter{TIn, TParam, TOut}"/> class.
+        /// </summary>
+        /// <param name="convert">The convert function.</param>
+        public FuncValueConverter(Func<TIn?, TParam?, TOut> convert)
+        {
+            _convert = convert;
+        }
+
+        /// <inheritdoc/>
+        public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+        {
+            if (TypeUtilities.CanCast<TIn>(value) && TypeUtilities.CanCast<TParam>(parameter))
+            {
+                return _convert((TIn?)value, (TParam?)parameter);
+            }
+            else
+            {
+                return AvaloniaProperty.UnsetValue;
+            }
+        }
+
+        /// <inheritdoc/>
+        public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
 }

+ 12 - 0
src/Avalonia.Base/Data/Converters/ObjectConverters.cs

@@ -16,5 +16,17 @@ namespace Avalonia.Data.Converters
         /// </summary>
         public static readonly IValueConverter IsNotNull =
             new FuncValueConverter<object?, bool>(x => x is not null);
+
+        /// <summary>
+        /// A value converter that returns true if the input object is equal to a parameter object.
+        /// </summary>
+        public static readonly IValueConverter Equal =
+            new FuncValueConverter<object?, object?, bool>((a, b) => a?.Equals(b) ?? b is null);
+ 
+        /// <summary>
+        /// A value converter that returns true if the input object is not equal to a parameter object.
+        /// </summary>
+        public static readonly IValueConverter NotEqual =
+            new FuncValueConverter<object?, object?, bool>((a, b) => !a?.Equals(b) ?? b is not null);
     }
 }

+ 45 - 0
tests/Avalonia.Base.UnitTests/Data/ObjectConvertersTests_Eqaul.cs

@@ -0,0 +1,45 @@
+using System.Globalization;
+using Avalonia.Data.Converters;
+using Xunit;
+
+namespace Avalonia.Base.UnitTests.Data;
+
+public class ObjectConvertersTests_Equal
+{
+    [Fact]
+    public void Returns_True_If_Value_And_Parameter_Are_Null()
+    {
+        var result = ObjectConverters.Equal.Convert(null, typeof(object), null, CultureInfo.InvariantCulture);
+
+        Assert.IsType<bool>(result);
+        Assert.True(result is true);
+    }
+
+    [Fact]
+    public void Returns_False_If_Value_Is_Null_And_Parameter_Is_Not_Null()
+    {
+        var result = ObjectConverters.Equal.Convert(null, typeof(object), new object(), CultureInfo.InvariantCulture);
+
+        Assert.IsType<bool>(result);
+        Assert.True(result is false);
+    }
+
+    [Fact]
+    public void Returns_False_If_Value_And_Parameter_Are_Different_Objects()
+    {
+        var result = ObjectConverters.Equal.Convert(new object(), typeof(object), new object(), CultureInfo.InvariantCulture);
+
+        Assert.IsType<bool>(result);
+        Assert.True(result is false);
+    }
+
+    [Fact]
+    public void Returns_True_If_Value_And_Parameter_Are_Same_Object()
+    {
+        var target = new object();
+        var result = ObjectConverters.Equal.Convert(target, typeof(object), target, CultureInfo.InvariantCulture);
+
+        Assert.IsType<bool>(result);
+        Assert.True(result is true);
+    }
+}

+ 26 - 0
tests/Avalonia.Base.UnitTests/Data/ObjectConvertersTests_IsNotNull.cs

@@ -0,0 +1,26 @@
+using System.Globalization;
+using Avalonia.Data.Converters;
+using Xunit;
+
+namespace Avalonia.Base.UnitTests.Data;
+
+public class ObjectConvertersTests_IsNull
+{
+    [Fact]
+    public void Returns_True_If_Value_Is_Null()
+    {
+        var result = ObjectConverters.IsNull.Convert(null, typeof(object), null, CultureInfo.InvariantCulture);
+
+        Assert.IsType<bool>(result);
+        Assert.True(result is true);
+    }
+
+    [Fact]
+    public void Returns_False_If_Value_Is_Not_Null()
+    {
+        var result = ObjectConverters.IsNull.Convert(new object(), typeof(object), null, CultureInfo.InvariantCulture);
+
+        Assert.IsType<bool>(result);
+        Assert.True(result is false);
+    }
+}

+ 26 - 0
tests/Avalonia.Base.UnitTests/Data/ObjectConvertersTests_IsNull.cs

@@ -0,0 +1,26 @@
+using System.Globalization;
+using Avalonia.Data.Converters;
+using Xunit;
+
+namespace Avalonia.Base.UnitTests.Data;
+
+public class ObjectConvertersTests_IsNotNull
+{
+    [Fact]
+    public void Returns_True_If_Value_Is_Not_Null()
+    {
+        var result = ObjectConverters.IsNotNull.Convert(new object(), typeof(object), null, CultureInfo.InvariantCulture);
+
+        Assert.IsType<bool>(result);
+        Assert.True(result is true);
+    }
+
+    [Fact]
+    public void Returns_False_If_Value_Is_Null()
+    {
+        var result = ObjectConverters.IsNotNull.Convert(null, typeof(object), null, CultureInfo.InvariantCulture);
+
+        Assert.IsType<bool>(result);
+        Assert.True(result is false);
+    }
+}

+ 45 - 0
tests/Avalonia.Base.UnitTests/Data/ObjectConvertersTests_NotEqaul.cs

@@ -0,0 +1,45 @@
+using System.Globalization;
+using Avalonia.Data.Converters;
+using Xunit;
+
+namespace Avalonia.Base.UnitTests.Data;
+
+public class ObjectConvertersTests_NotEqual
+{
+    [Fact]
+    public void Returns_False_If_Value_And_Parameter_Are_Null()
+    {
+        var result = ObjectConverters.NotEqual.Convert(null, typeof(object), null, CultureInfo.InvariantCulture);
+
+        Assert.IsType<bool>(result);
+        Assert.True(result is false);
+    }
+
+    [Fact]
+    public void Returns_True_If_Value_Is_Null_And_Parameter_Is_Not_Null()
+    {
+        var result = ObjectConverters.NotEqual.Convert(null, typeof(object), new object(), CultureInfo.InvariantCulture);
+
+        Assert.IsType<bool>(result);
+        Assert.True(result is true);
+    }
+
+    [Fact]
+    public void Returns_True_If_Value_And_Parameter_Are_Different_Objects()
+    {
+        var result = ObjectConverters.NotEqual.Convert(new object(), typeof(object), new object(), CultureInfo.InvariantCulture);
+
+        Assert.IsType<bool>(result);
+        Assert.True(result is true);
+    }
+
+    [Fact]
+    public void Returns_False_If_Value_And_Parameter_Are_Same_Object()
+    {
+        var target = new object();
+        var result = ObjectConverters.NotEqual.Convert(target, typeof(object), target, CultureInfo.InvariantCulture);
+
+        Assert.IsType<bool>(result);
+        Assert.True(result is false);
+    }
+}