Bugfix Issue #1705 (MAUI: 184): Allow null handling for BaseConverterOneWay<TFrom, TTo> (#1733)

* Fix typo ArgumenException for unit tests

* Add AllowsNullOrDefault handling for BaseConverterOneWay
Fix summary xml doc for ConvertBack of BaseConverterOneWay

* Add BaseConverterOneWay_Tests for new AllowsNullOrDefault

* Implement recommended changes
- Rename test converter to MockConverterOneWay
- Change ArgumentException to ArgumentNullException in BaseConverterOneWay<TFrom, TTo> when disallowing nulls
- Simplify unit test data generation and execution

* Update null check in BaseConverterOneWay<TFrom, TTo>

* Implement recommended changes

- Change check order back to original in BaseConverterOneWay<TFrom, TTo>
- Fix missing first paramName argument for ArgumentNullException in BaseConverterOneWay<TFrom, TTo>
- Refactor BaseConverterOneWay_Tests

* Rename property AllowsNullOrDefault to AllowsNull of BaseConverterOneWay<TFrom, TTo>

* Update IsNullOrEmptyConverter to inherit from BaseConverterOneWay<TFrom, TTo>

* Migrated more converters to base

* Revert compare converter changes

* Fixed unit tests and code review tidy up

* Switch to separate nullable converter base class

* Add null support to BaseConverter

NullableBaseConverter now completes symmetry with NullableBaseConverterOneWay.

Migrate converters to use NullableBaseConverter where possible.

Migrate tests to new approach.

* Remove Null-Forgiving Operator

* Rename converters to start with Base

* Code review improvement

* Add global.json

Ensures .NET 6.0 is the minimum version in use

* `dotnet format`

* Move global.json to root folder

* `dotnet format`

Co-authored-by: Pascal Ried <p.ried@comventure.de>
Co-authored-by: Shaun Lawrence <shaunrlawrence@gmail.com>
Co-authored-by: Pedro Jesus <pedrojesus.cefet@gmail.com>
Co-authored-by: Brandon Minnick <13558917+brminnick@users.noreply.github.com>
This commit is contained in:
Countryen 2021-12-03 15:37:41 +01:00 коммит произвёл GitHub
Родитель cbf4a19dc7
Коммит 35d7d5f762
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
116 изменённых файлов: 595 добавлений и 618 удалений

Просмотреть файл

@ -2,7 +2,7 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(MSBuildProjectExtension)' == '.csproj'">
<LangVersion>9.0</LangVersion>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<WarningsAsErrors>nullable</WarningsAsErrors>
</PropertyGroup>

6
global.json Normal file
Просмотреть файл

@ -0,0 +1,6 @@
{
"sdk": {
"version": "6.0.*",
"rollForward": "latestMajor"
}
}

Просмотреть файл

@ -90,7 +90,7 @@ namespace Xamarin.CommunityToolkit.Sample.WPF
Element.Lines.Clear();
}
var lines = Element.MultiLineMode ? e.Added : new StrokeCollection() {e.Added.First()};
var lines = Element.MultiLineMode ? e.Added : new StrokeCollection() { e.Added.First() };
foreach (var line in lines)
{
@ -100,7 +100,7 @@ namespace Xamarin.CommunityToolkit.Sample.WPF
Points = new ObservableCollection<Point>(points),
LineColor = Color.FromRgba(line.DrawingAttributes.Color.R, line.DrawingAttributes.Color.G,
line.DrawingAttributes.Color.B, line.DrawingAttributes.Color.A),
LineWidth = (float) line.DrawingAttributes.Width
LineWidth = (float)line.DrawingAttributes.Width
});
}
@ -127,12 +127,12 @@ namespace Xamarin.CommunityToolkit.Sample.WPF
var lines = Element.MultiLineMode
? Element.Lines
: Element.Lines.Any()
? new ObservableCollection<Line> {Element.Lines.LastOrDefault()}
? new ObservableCollection<Line> { Element.Lines.LastOrDefault() }
: new ObservableCollection<Line>();
foreach (var line in lines)
{
var stylusPoints = line.Points.Select(point => new StylusPoint(point.X, point.Y)).ToList();
if (stylusPoints is {Count: > 0})
if (stylusPoints is { Count: > 0 })
{
var stroke = new Stroke(new StylusPointCollection(stylusPoints))
{

Просмотреть файл

@ -21,6 +21,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
..\.editorconfig = ..\.editorconfig
..\Directory.Build.props = ..\Directory.Build.props
..\Xamarin.CommunityToolkit.ruleset = ..\Xamarin.CommunityToolkit.ruleset
..\global.json = ..\global.json
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.CommunityToolkit.Sample.WPF", "XCT.Sample.WPF\Xamarin.CommunityToolkit.Sample.WPF.csproj", "{C4D6CD2D-8DF4-4D46-936C-1AB31C87B5EA}"

Просмотреть файл

@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using Xamarin.Forms;
using Xamarin.CommunityToolkit.UI.Views;
using System.Collections.ObjectModel;
using System.Linq;
using Xamarin.CommunityToolkit.UI.Views;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Sample.Pages.Views
{

Просмотреть файл

@ -1,6 +1,6 @@
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.CommunityToolkit.PlatformConfiguration.iOSSpecific;
using Xamarin.CommunityToolkit.PlatformConfiguration.iOSSpecific;
using Xamarin.CommunityToolkit.PlatformConfiguration.WindowsSpecific;
using Xamarin.Forms.PlatformConfiguration;
namespace Xamarin.CommunityToolkit.Sample.Pages.Views.Popups
{

Просмотреть файл

@ -89,4 +89,3 @@ namespace Xamarin.CommunityToolkit.Sample.ViewModels.Animations
}
}
}

Просмотреть файл

@ -1,7 +1,7 @@
using System.Windows.Input;
using Xamarin.Forms;
using Xamarin.CommunityToolkit.Sample.Pages.TestCases.Popups;
using Xamarin.CommunityToolkit.Extensions;
using Xamarin.CommunityToolkit.Sample.Pages.TestCases.Popups;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Sample.ViewModels.TestCases.Popups
{

Просмотреть файл

@ -6,7 +6,7 @@ namespace Xamarin.CommunityToolkit.Sample.ViewModels.TestCases
{
public class TabViewItemBindingViewModel : BaseViewModel
{
public static TabViewItemBindingViewModel Current { get; } = new ();
public static TabViewItemBindingViewModel Current { get; } = new();
int countClick;

Просмотреть файл

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using NUnit.Framework;
using Xamarin.CommunityToolkit.Converters;
using Xamarin.CommunityToolkit.UnitTests.Mocks;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.UnitTests.Converters
{
[TestOf(typeof(BaseConverterOneWay<string, Color>))]
public class BaseConverterOneWay_Tests
{
[TestCaseSource(nameof(GetValidTestData))]
public void MockConverterOneWayConvert((bool ShouldAllowNull, string? Value, Color ExpectedResult) testCase)
{
var mockConverterOneWay = CreateConverter(testCase.ShouldAllowNull);
var result = mockConverterOneWay.Convert(testCase.Value, typeof(Color), null, CultureInfo.CurrentCulture);
Assert.AreEqual(result, testCase.ExpectedResult);
}
[TestCaseSource(nameof(GetInvalidTestData))]
public void MockConverterOneWayConvertInvalidValuesThrowException((bool ShouldAllowNull, string? Value, Type ExpectedExceptionType) testCase)
{
var mockConverterOneWay = CreateConverter(testCase.ShouldAllowNull);
Assert.Throws(testCase.ExpectedExceptionType, () => mockConverterOneWay.Convert(testCase.Value, typeof(Color), null, CultureInfo.CurrentCulture));
}
static IValueConverter CreateConverter(bool shouldAllowNull) =>
shouldAllowNull ? new MockNullableConverterOneWay() : new MockConverterOneWay();
static IEnumerable<(bool ShouldAllowNull, string? Value, Color ExpectedResult)> GetValidTestData()
{
yield return (true, "Red", Color.Red);
yield return (true, "Blue", Color.Blue);
yield return (true, null, Color.Black);
yield return (false, "Red", Color.Red);
yield return (false, "Blue", Color.Blue);
}
static IEnumerable<(bool ShouldAllowNull, string? Value, Type ExpectedExceptionType)> GetInvalidTestData()
{
yield return (true, "red", typeof(ArgumentException));
yield return (true, "Green", typeof(ArgumentException));
yield return (true, "red", typeof(ArgumentException));
yield return (true, "Green", typeof(ArgumentException));
yield return (false, null, typeof(ArgumentNullException));
}
}
}

Просмотреть файл

@ -37,7 +37,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
}
[TestCase("")]
public void BoolToObjectInValidValuesThrowArgumenException(object value)
public void BoolToObjectInValidValuesThrowArgumentException(object value)
{
var boolObjectConverter = new BoolToObjectConverter();
Assert.Throws<ArgumentException>(() => boolObjectConverter.Convert(value, typeof(BoolToObjectConverter_Tests), null, CultureInfo.CurrentCulture));

Просмотреть файл

@ -18,18 +18,17 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
var expectedValue = ImageSource.FromStream(() => memoryStream);
var byteArrayToImageSourceConverter = new ByteArrayToImageSourceConverter();
var byteArrayToImageSourceConverter = CreateConverter();
var result = byteArrayToImageSourceConverter.Convert(byteArray, typeof(ByteArrayToImageSourceConverter), null, CultureInfo.CurrentCulture);
Assert.IsTrue(StreamEquals(GetStreamFromImageSource((ImageSource?)result), memoryStream));
}
[TestCase("Random String Value")]
public void InvalidConverterValuesReturnsNull(object value)
{
var byteArrayToImageSourceConverter = new ByteArrayToImageSourceConverter();
var byteArrayToImageSourceConverter = CreateConverter();
Assert.Throws<ArgumentException>(() => byteArrayToImageSourceConverter.Convert(value, typeof(ByteArrayToImageSourceConverter), null, CultureInfo.CurrentCulture));
}
@ -63,5 +62,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
return true;
}
static IValueConverter CreateConverter() => new ByteArrayToImageSourceConverter();
}
}

Просмотреть файл

@ -11,6 +11,72 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
public const string TrueTestObject = nameof(TrueTestObject);
public const string FalseTestObject = nameof(FalseTestObject);
[TestCaseSource(nameof(GetTestData))]
public void CompareConverterConvert(IComparable value, CompareConverter.OperatorType comparisonOperator, IComparable comparingValue, object trueObject, object falseObject, object expectedResult)
{
var compareConverter = new CompareConverter
{
TrueObject = trueObject,
FalseObject = falseObject,
ComparisonOperator = comparisonOperator,
ComparingValue = comparingValue
};
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
var result = compareConverter.Convert(value, typeof(BoolToObjectConverter_Tests), null, CultureInfo.CurrentCulture);
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
Assert.AreEqual(result, expectedResult);
}
static IEnumerable<object?[]> GetThrowArgumentExceptionTestData()
{
yield return new object?[] { new { Name = "Not IComparable" } };
yield return new object?[] { null };
}
[TestCaseSource(nameof(GetThrowArgumentExceptionTestData))]
public void CompareConverterInValidValuesThrowArgumentException(object value)
{
var compareConverter = new CompareConverter()
{
ComparingValue = 20d
};
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
Assert.Throws<ArgumentException>(() => compareConverter.Convert(value, typeof(BoolToObjectConverter_Tests), null, CultureInfo.CurrentCulture));
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
}
[TestCase(20d, null, TrueTestObject, FalseTestObject)]
[TestCase(20d, 20d, TrueTestObject, null)]
[TestCase(20d, 20d, null, FalseTestObject)]
public void CompareConverterInValidValuesThrowArgumentNullException(object value, IComparable comparingValue, object trueObject, object falseObject)
{
var compareConverter = new CompareConverter()
{
ComparingValue = comparingValue,
FalseObject = falseObject,
TrueObject = trueObject
};
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
Assert.Throws<ArgumentNullException>(() => compareConverter.Convert(value, typeof(BoolToObjectConverter_Tests), null, CultureInfo.CurrentCulture));
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
}
[TestCase(20d, (CompareConverter.OperatorType)10, 20d)]
public void CompareConverterInValidValuesThrowArgumentOutOfRangeException(object value, CompareConverter.OperatorType comparisonOperator, IComparable comparingValue)
{
var compareConverter = new CompareConverter
{
ComparisonOperator = comparisonOperator,
ComparingValue = comparingValue
};
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
Assert.Throws<ArgumentOutOfRangeException>(() => compareConverter.Convert(value, typeof(BoolToObjectConverter_Tests), null, CultureInfo.CurrentCulture));
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
}
static IEnumerable<object?[]> GetTestData()
{
@ -35,7 +101,6 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
yield return new object?[] { 20d, CompareConverter.OperatorType.Smaller, 10d, TrueTestObject, FalseTestObject, FalseTestObject };
yield return new object?[] { 20d, CompareConverter.OperatorType.SmallerOrEqual, 10d, TrueTestObject, FalseTestObject, FalseTestObject };
yield return new object?[] { 20d, CompareConverter.OperatorType.Greater, 20d, null, null, false };
yield return new object?[] { 20d, CompareConverter.OperatorType.GreaterOrEqual, 20d, null, null, true };
yield return new object?[] { 20d, CompareConverter.OperatorType.Equal, 20d, null, null, true };
@ -57,64 +122,5 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
yield return new object?[] { 10d, CompareConverter.OperatorType.Smaller, 20d, null, null, true };
yield return new object?[] { 10d, CompareConverter.OperatorType.SmallerOrEqual, 20d, null, null, true };
}
[TestCaseSource(nameof(GetTestData))]
public void CompareConverterConvert(IComparable value, CompareConverter.OperatorType comparisonOperator, IComparable comparingValue, object trueObject, object falseObject, object expectedResult)
{
var compareConverter = new CompareConverter
{
TrueObject = trueObject,
FalseObject = falseObject,
ComparisonOperator = comparisonOperator,
ComparingValue = comparingValue
};
var result = compareConverter.Convert(value, typeof(BoolToObjectConverter_Tests), null!, CultureInfo.CurrentCulture);
Assert.AreEqual(result, expectedResult);
}
static IEnumerable<object?[]> GetThrowArgumenExceptionTestData()
{
yield return new object?[] { new { Name = "Not IComparable" } };
yield return new object?[] { null };
}
[TestCaseSource(nameof(GetThrowArgumenExceptionTestData))]
public void CompareConverterInValidValuesThrowArgumenException(object value)
{
var compareConverter = new CompareConverter()
{
ComparingValue = 20d
};
Assert.Throws<ArgumentException>(() => compareConverter.Convert(value, typeof(BoolToObjectConverter_Tests), null!, CultureInfo.CurrentCulture));
}
[TestCase(20d, null, TrueTestObject, FalseTestObject)]
[TestCase(20d, 20d, TrueTestObject, null)]
[TestCase(20d, 20d, null, FalseTestObject)]
public void CompareConverterInValidValuesThrowArgumentNullException(object value, IComparable comparingValue, object trueObject, object falseObject)
{
var compareConverter = new CompareConverter()
{
ComparingValue = comparingValue,
FalseObject = falseObject,
TrueObject = trueObject
};
Assert.Throws<ArgumentNullException>(() => compareConverter.Convert(value, typeof(BoolToObjectConverter_Tests), null!, CultureInfo.CurrentCulture));
}
[TestCase(20d, (CompareConverter.OperatorType)10, 20d)]
public void CompareConverterInValidValuesThrowArgumentOutOfRangeException(object value, CompareConverter.OperatorType comparisonOperator, IComparable comparingValue)
{
var compareConverter = new CompareConverter
{
ComparisonOperator = comparisonOperator,
ComparingValue = comparingValue
};
Assert.Throws<ArgumentOutOfRangeException>(() => compareConverter.Convert(value, typeof(BoolToObjectConverter_Tests), null!, CultureInfo.CurrentCulture));
}
}
}

Просмотреть файл

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using NUnit.Framework;
using Xamarin.CommunityToolkit.Converters;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.UnitTests.Converters
{
@ -17,8 +18,45 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
static readonly DateTimeOffset testDateTimeOffsetLocal = new DateTimeOffset(2020, 08, 25, 13, 37, 00, DateTimeOffset.Now.Offset);
static readonly DateTimeOffset testDateTimeOffsetUtc = new DateTimeOffset(2020, 08, 25, 13, 37, 00, DateTimeOffset.UtcNow.Offset);
public static IEnumerable<object[]> GetData() =>
new List<object[]>
[TestCaseSource(nameof(GetData))]
public void DateTimeOffsetConverter(DateTimeOffset value, DateTime expectedResult)
{
var dateTimeOffsetConverter = CreateConverter();
var result = dateTimeOffsetConverter.Convert(value, typeof(DateTimeOffsetConverter_Tests), null, CultureInfo.CurrentCulture);
Assert.AreEqual(expectedResult, result);
}
[TestCaseSource(nameof(GetDataReverse))]
public void DateTimeOffsetConverterBack(DateTime value, DateTimeOffset expectedResult)
{
var dateTimeOffsetConverter = CreateConverter();
var result = dateTimeOffsetConverter.ConvertBack(value, typeof(DateTimeOffsetConverter_Tests), null, CultureInfo.CurrentCulture);
Assert.AreEqual(expectedResult, result);
}
[Test]
public void DateTimeOffsetConverter_GivenInvalidParameters_ThrowsException()
{
var dateTimeOffsetConverter = CreateConverter();
Assert.Throws<ArgumentException>(() => dateTimeOffsetConverter.Convert("Not a DateTimeOffset", typeof(DateTimeOffsetConverter_Tests), null, CultureInfo.CurrentCulture));
}
[Test]
public void DateTimeOffsetConverterBack_GivenInvalidParameters_ThrowsException()
{
var dateTimeOffsetConverter = CreateConverter();
Assert.Throws<ArgumentException>(() => dateTimeOffsetConverter.ConvertBack("Not a DateTime", typeof(DateTimeOffsetConverter_Tests), null, CultureInfo.CurrentCulture));
}
static IValueConverter CreateConverter() => new DateTimeOffsetConverter();
static IEnumerable<object[]> GetData() => new List<object[]>
{
new object[] { testDateTimeOffsetNow, testDateTimeNow },
new object[] { DateTimeOffset.MinValue, DateTime.MinValue },
@ -28,8 +66,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
new object[] { testDateTimeOffsetUtc, testDateTimeUnspecified },
};
public static IEnumerable<object[]> GetDataReverse() =>
new List<object[]>
static IEnumerable<object[]> GetDataReverse() => new List<object[]>
{
new object[] { testDateTimeNow, testDateTimeOffsetNow },
new object[] { DateTime.MinValue, DateTimeOffset.MinValue },
@ -38,47 +75,5 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
new object[] { testDateTimeUtc, testDateTimeOffsetUtc },
new object[] { testDateTimeUnspecified, testDateTimeOffsetUtc },
};
[TestCaseSource(nameof(GetData))]
public void DateTimeOffsetConverter(DateTimeOffset value, DateTime expectedResult)
{
var dateTimeOffsetConverter = new DateTimeOffsetConverter();
var result = dateTimeOffsetConverter.Convert(value, typeof(DateTimeOffsetConverter_Tests), null,
CultureInfo.CurrentCulture);
Assert.AreEqual(expectedResult, result);
}
[TestCaseSource(nameof(GetDataReverse))]
public void DateTimeOffsetConverterBack(DateTime value, DateTimeOffset expectedResult)
{
var dateTimeOffsetConverter = new DateTimeOffsetConverter();
var result = dateTimeOffsetConverter.ConvertBack(value, typeof(DateTimeOffsetConverter_Tests), null,
CultureInfo.CurrentCulture);
Assert.AreEqual(expectedResult, result);
}
[Test]
public void DateTimeOffsetConverter_GivenInvalidParameters_ThrowsException()
{
var dateTimeOffsetConverter = new DateTimeOffsetConverter();
Assert.Throws<ArgumentException>(() => dateTimeOffsetConverter.Convert("Not a DateTimeOffset",
typeof(DateTimeOffsetConverter_Tests), null,
CultureInfo.CurrentCulture));
}
[Test]
public void DateTimeOffsetConverterBack_GivenInvalidParameters_ThrowsException()
{
var dateTimeOffsetConverter = new DateTimeOffsetConverter();
Assert.Throws<ArgumentException>(() => dateTimeOffsetConverter.ConvertBack("Not a DateTime",
typeof(DateTimeOffsetConverter_Tests), null,
CultureInfo.CurrentCulture));
}
}
}

Просмотреть файл

@ -31,7 +31,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
}
[TestCase("")]
public void DoubleToIntInValidValuesThrowArgumenException(object value)
public void DoubleToIntInValidValuesThrowArgumentException(object value)
{
var doubleToIntConverter = new DoubleToIntConverter();
Assert.Throws<ArgumentException>(() => doubleToIntConverter.Convert(value, typeof(BoolToObjectConverter_Tests), null, CultureInfo.CurrentCulture));

Просмотреть файл

@ -21,7 +21,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
[TestCase(null, null)]
[TestCase(null, 100)]
public void IndexToArrayInValidValuesThrowArgumenException(object value, object position)
public void IndexToArrayInValidValuesThrowArgumentException(object value, object position)
{
var indexToArrayConverter = new IndexToArrayItemConverter();
Assert.Throws<ArgumentException>(() => indexToArrayConverter.Convert(position, typeof(IndexToArrayItemConverter), value, CultureInfo.CurrentCulture));
@ -29,7 +29,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
[TestCase(new int[] { 1, 2, 3, 4, 5 }, 100)]
[TestCase(new int[] { 1, 2, 3, 4, 5 }, -1)]
public void IndexToArrayInValidValuesThrowArgumenOutOfRangeException(object value, object position)
public void IndexToArrayInValidValuesThrowArgumentOutOfRangeException(object value, object position)
{
var indexToArrayConverter = new IndexToArrayItemConverter();
Assert.Throws<ArgumentOutOfRangeException>(() => indexToArrayConverter.Convert(position, typeof(IndexToArrayItemConverter), value, CultureInfo.CurrentCulture));

Просмотреть файл

@ -2,6 +2,7 @@
using System.Globalization;
using NUnit.Framework;
using Xamarin.CommunityToolkit.Converters;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.UnitTests.Converters
{
@ -11,7 +12,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
[TestCase(0, false)]
public void IndexToArrayConverter(int value, bool expectedResult)
{
var intToBoolConverter = new IntToBoolConverter();
var intToBoolConverter = CreateConverter();
var result = intToBoolConverter.Convert(value, typeof(IntToBoolConverter_Tests), null, CultureInfo.CurrentCulture);
@ -22,7 +23,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
[TestCase(false, 0)]
public void IndexToArrayConverterBack(bool value, int expectedResult)
{
var intToBoolConverter = new IntToBoolConverter();
var intToBoolConverter = CreateConverter();
var result = intToBoolConverter.ConvertBack(value, typeof(IntToBoolConverter_Tests), null, CultureInfo.CurrentCulture);
@ -31,20 +32,34 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
[TestCase(2.5)]
[TestCase("")]
[TestCase(null)]
public void InValidConverterValuesThrowArgumenException(object value)
public void InvalidConverterValuesThrowArgumentException(object value)
{
var intToBoolConverter = new IntToBoolConverter();
Assert.Throws<ArgumentException>(() => intToBoolConverter.Convert(value, typeof(IndexToArrayItemConverter), null, CultureInfo.CurrentCulture));
var intToBoolConverter = CreateConverter();
Assert.Throws<ArgumentException>(() => intToBoolConverter.Convert(value, typeof(IntToBoolConverter_Tests), null, CultureInfo.CurrentCulture));
}
[TestCase(2.5)]
[TestCase("")]
[TestCase(null)]
public void InValidConverterBackValuesThrowArgumenException(object value)
public void InvalidConverterBackValuesThrowArgumentException(object value)
{
var intToBoolConverter = new IntToBoolConverter();
Assert.Throws<ArgumentException>(() => intToBoolConverter.ConvertBack(value, typeof(IndexToArrayItemConverter), null, CultureInfo.CurrentCulture));
var intToBoolConverter = CreateConverter();
Assert.Throws<ArgumentException>(() => intToBoolConverter.ConvertBack(value, typeof(IntToBoolConverter_Tests), null, CultureInfo.CurrentCulture));
}
[Test]
public void NullConverterValuesThrowArgumentException()
{
var intToBoolConverter = CreateConverter();
Assert.Throws<ArgumentNullException>(() => intToBoolConverter.Convert(null, typeof(IntToBoolConverter_Tests), null, CultureInfo.CurrentCulture));
}
[Test]
public void NullConverterBackValuesThrowArgumentException()
{
var intToBoolConverter = CreateConverter();
Assert.Throws<ArgumentNullException>(() => intToBoolConverter.ConvertBack(null, typeof(IntToBoolConverter_Tests), null, CultureInfo.CurrentCulture));
}
static IValueConverter CreateConverter() => new IntToBoolConverter();
}
}

Просмотреть файл

@ -2,6 +2,7 @@
using System.Globalization;
using NUnit.Framework;
using Xamarin.CommunityToolkit.Converters;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.UnitTests.Converters
{
@ -11,7 +12,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
[TestCase(false, true)]
public void InverterBoolConverter(bool value, bool expectedResult)
{
var inverterBoolConverter = new InvertedBoolConverter();
var inverterBoolConverter = CreateConverter();
var result = inverterBoolConverter.Convert(value, typeof(InvertedBoolConverter_Tests), null, CultureInfo.CurrentCulture);
@ -20,11 +21,19 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
[TestCase(2)]
[TestCase("")]
[TestCase(null)]
public void InValidConverterValuesThrowArgumenException(object value)
public void InvalidConverterValuesThrowArgumentException(object value)
{
var inverterBoolConverter = new InvertedBoolConverter();
var inverterBoolConverter = CreateConverter();
Assert.Throws<ArgumentException>(() => inverterBoolConverter.Convert(value, typeof(IndexToArrayItemConverter), null, CultureInfo.CurrentCulture));
}
[Test]
public void NullThrowsArgumentNullException()
{
var inverterBoolConverter = CreateConverter();
Assert.Throws<ArgumentNullException>(() => inverterBoolConverter.Convert(null, typeof(IndexToArrayItemConverter), null, CultureInfo.CurrentCulture));
}
static IValueConverter CreateConverter() => new InvertedBoolConverter();
}
}

Просмотреть файл

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using NUnit.Framework;
using Xamarin.CommunityToolkit.Converters;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.UnitTests.Converters
{
@ -20,19 +21,15 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
public static IEnumerable<object?[]> GetDataForException() => new List<object?[]>
{
new object?[] { null, 2, 3 },
new object?[] { 1, null, 3 },
new object?[] { 1, 2, null },
new object?[] { null, 2, 3, typeof(ArgumentNullException) },
new object?[] { 1, null, 3, typeof(ArgumentException) },
new object?[] { 1, 2, null, typeof(ArgumentException) },
};
[TestCaseSource(nameof(GetData))]
public void IsInRangeConverter(object value, object minValue, object maxValue, bool expectedResult)
{
var isInRangeConverter = new IsInRangeConverter
{
MinValue = minValue,
MaxValue = maxValue
};
var isInRangeConverter = CreateConverter(maxValue, minValue);
var result = isInRangeConverter.Convert(value, typeof(IsInRangeConverter_Tests), null, CultureInfo.CurrentCulture);
@ -40,14 +37,17 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
}
[TestCaseSource(nameof(GetDataForException))]
public void IsInRangeConverterInvalidValuesThrowArgumenException(object value, object minValue, object maxValue)
public void IsInRangeConverterInvalidValuesThrowArgumentException(object value, object minValue, object maxValue, Type expectedExceptionType)
{
var isInRangeConverter = new IsInRangeConverter
var isInRangeConverter = CreateConverter(maxValue, minValue);
Assert.Throws(expectedExceptionType, () => isInRangeConverter.Convert(value, typeof(IsInRangeConverter_Tests), null, CultureInfo.CurrentCulture));
}
static IValueConverter CreateConverter(object maxValue, object minValue) =>
new IsInRangeConverter
{
MinValue = minValue,
MaxValue = maxValue
};
Assert.Throws<ArgumentException>(() => isInRangeConverter.Convert(value, typeof(IsInRangeConverter_Tests), null, CultureInfo.CurrentCulture));
}
}
}

Просмотреть файл

@ -1,6 +1,7 @@
using System.Globalization;
using NUnit.Framework;
using Xamarin.CommunityToolkit.Converters;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.UnitTests.Converters
{
@ -12,7 +13,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
[TestCase("", false)]
public void IsNotNullOrEmptyConverter(object value, bool expectedResult)
{
var isNotNullOrEmptyConverter = new IsNotNullOrEmptyConverter();
IValueConverter isNotNullOrEmptyConverter = new IsNotNullOrEmptyConverter();
var result = isNotNullOrEmptyConverter.Convert(value, typeof(IsNotNullOrEmptyConverter_Tests), null, CultureInfo.CurrentCulture);

Просмотреть файл

@ -1,6 +1,7 @@
using System.Globalization;
using NUnit.Framework;
using Xamarin.CommunityToolkit.Converters;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.UnitTests.Converters
{
@ -12,7 +13,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
[TestCase(typeof(IsNullOrEmptyConverter), false)]
public void IsNullOrEmptyConverter(object value, bool expectedResult)
{
var isNullOrEmptyConverter = new IsNullOrEmptyConverter();
IValueConverter isNullOrEmptyConverter = new IsNullOrEmptyConverter();
var result = isNullOrEmptyConverter.Convert(value, typeof(IsNotNullOrEmptyConverter_Tests), null, CultureInfo.CurrentCulture);

Просмотреть файл

@ -23,17 +23,19 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
[TestCaseSource(nameof(GetData))]
public void ItemSelectedEventArgsConverter(SelectedItemChangedEventArgs value, object expectedResult)
{
var itemSelectedEventArgsConverter = new ItemSelectedEventArgsConverter();
var itemSelectedEventArgsConverter = CreateConverter();
var result = itemSelectedEventArgsConverter.Convert(value, typeof(ItemSelectedEventArgsConverter), null, CultureInfo.CurrentCulture);
Assert.AreEqual(result, expectedResult);
}
[TestCase("Random String")]
public void InvalidConverterValuesThrowsArgumenException(object value)
public void InvalidConverterValuesThrowsArgumentException(object value)
{
var itemSelectedEventArgsConverter = new ItemSelectedEventArgsConverter();
var itemSelectedEventArgsConverter = CreateConverter();
Assert.Throws<ArgumentException>(() => itemSelectedEventArgsConverter.Convert(value, typeof(ItemSelectedEventArgsConverter), null, CultureInfo.CurrentCulture));
}
static IValueConverter CreateConverter() => new ItemSelectedEventArgsConverter();
}
}

Просмотреть файл

@ -23,7 +23,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
[TestCaseSource(nameof(GetData))]
public void ItemTappedEventArgsConverter(ItemTappedEventArgs value, object expectedResult)
{
var itemTappedEventArgsConverter = new ItemTappedEventArgsConverter();
var itemTappedEventArgsConverter = CreateConverter();
var result = itemTappedEventArgsConverter.Convert(value, typeof(ItemTappedEventArgsConverter), null, CultureInfo.CurrentCulture);
@ -31,10 +31,12 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
}
[TestCase("Random String")]
public void InValidConverterValuesThrowArgumenException(object value)
public void InValidConverterValuesThrowArgumentException(object value)
{
var itemTappedEventArgsConverter = new ItemTappedEventArgsConverter();
var itemTappedEventArgsConverter = CreateConverter();
Assert.Throws<ArgumentException>(() => itemTappedEventArgsConverter.Convert(value, typeof(ItemTappedEventArgsConverter), null, CultureInfo.CurrentCulture));
}
static IValueConverter CreateConverter() => new ItemTappedEventArgsConverter();
}
}

Просмотреть файл

@ -4,6 +4,7 @@ using System.Globalization;
using System.Linq;
using NUnit.Framework;
using Xamarin.CommunityToolkit.Converters;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.UnitTests.Converters
{
@ -20,7 +21,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
[TestCaseSource(nameof(GetData))]
public void ListIsNotNullOrEmptyConverter(object value, bool expectedResult)
{
var listIsNotNullOrEmptyConverter = new ListIsNotNullOrEmptyConverter();
var listIsNotNullOrEmptyConverter = CreateConverter();
var result = listIsNotNullOrEmptyConverter.Convert(value, typeof(ListIsNotNullOrEmptyConverter), null, CultureInfo.CurrentCulture);
@ -28,11 +29,13 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
}
[TestCase(0)]
public void InValidConverterValuesThrowArgumenException(object value)
public void InValidConverterValuesThrowArgumentException(object value)
{
var listIsNotNullOrEmptyConverter = new ListIsNotNullOrEmptyConverter();
var listIsNotNullOrEmptyConverter = CreateConverter();
Assert.Throws<ArgumentException>(() => listIsNotNullOrEmptyConverter.Convert(value, typeof(ListIsNotNullOrEmptyConverter), null, CultureInfo.CurrentCulture));
}
static IValueConverter CreateConverter() => new ListIsNotNullOrEmptyConverter();
}
}

Просмотреть файл

@ -4,6 +4,7 @@ using System.Globalization;
using System.Linq;
using NUnit.Framework;
using Xamarin.CommunityToolkit.Converters;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.UnitTests.Converters
{
@ -20,19 +21,21 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
[TestCaseSource(nameof(GetData))]
public void ListIsNullOrEmptyConverter(object value, bool expectedResult)
{
var listIstNullOrEmptyConverter = new ListIsNullOrEmptyConverter();
var listIsNullOrEmptyConverter = CreateConverter();
var result = listIstNullOrEmptyConverter.Convert(value, typeof(ListIsNullOrEmptyConverter), null, CultureInfo.CurrentCulture);
var result = listIsNullOrEmptyConverter.Convert(value, typeof(ListIsNullOrEmptyConverter), null, CultureInfo.CurrentCulture);
Assert.AreEqual(result, expectedResult);
}
[TestCase(0)]
public void InValidConverterValuesThrowArgumenException(object value)
public void InValidConverterValuesThrowArgumentException(object value)
{
var listIstNullOrEmptyConverter = new ListIsNullOrEmptyConverter();
var listIsNullOrEmptyConverter = CreateConverter();
Assert.Throws<ArgumentException>(() => listIstNullOrEmptyConverter.Convert(value, typeof(ListIsNullOrEmptyConverter), null, CultureInfo.CurrentCulture));
Assert.Throws<ArgumentException>(() => listIsNullOrEmptyConverter.Convert(value, typeof(ListIsNullOrEmptyConverter), null, CultureInfo.CurrentCulture));
}
static IValueConverter CreateConverter() => new ListIsNullOrEmptyConverter();
}
}

Просмотреть файл

@ -29,7 +29,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
}
[TestCase(0)]
public void InValidConverterValuesThrowArgumenException(object value)
public void InValidConverterValuesThrowArgumentException(object value)
{
var listToStringConverter = new ListToStringConverter();
@ -37,7 +37,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
}
[TestCase(0)]
public void InValidConverterParametersThrowArgumenException(object parameter)
public void InValidConverterParametersThrowArgumentException(object parameter)
{
var listToStringConverter = new ListToStringConverter();

Просмотреть файл

@ -30,7 +30,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
}
[TestCase(0)]
public void InValidConverterValuesThrowArgumenException(object value)
public void InValidConverterValuesThrowArgumentException(object value)
{
var listToStringConverter = new ListToStringConverter();
@ -38,7 +38,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.Converters
}
[TestCase(0)]
public void InValidConverterParametersThrowArgumenException(object parameter)
public void InValidConverterParametersThrowArgumentException(object parameter)
{
var listToStringConverter = new ListToStringConverter();

Просмотреть файл

@ -0,0 +1,33 @@
using System;
using Xamarin.CommunityToolkit.Converters;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.UnitTests.Mocks
{
/// <summary>
/// Mock to test the <see cref="BaseNullableConverterOneWay{TFrom, TTo}"/> internals.
/// </summary>
public class MockNullableConverterOneWay : BaseNullableConverterOneWay<string, Color>
{
public override Color ConvertFrom(string? value) => value switch
{
null => Color.Black,
"Red" => Color.Red,
"Blue" => Color.Blue,
_ => throw new ArgumentException($"{value} unknown.")
};
}
/// <summary>
/// Mock to test the <see cref="BaseConverterOneWay{TFrom, TTo}"/> internals.
/// </summary>
public class MockConverterOneWay : BaseConverterOneWay<string, Color>
{
public override Color ConvertFrom(string value) => value switch
{
"Red" => Color.Red,
"Blue" => Color.Blue,
_ => throw new ArgumentException($"{value} unknown.")
};
}
}

Просмотреть файл

@ -32,6 +32,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.ObjectModel.ICommandTests.AsyncComm
// Assert
Assert.Throws<InvalidCommandParameterException>(() => command.Execute(true));
}
[Test]
public void IAsyncCommand_Execute_InvalidNullableTypeParameter()
{

Просмотреть файл

@ -18,6 +18,7 @@ namespace Xamarin.CommunityToolkit.UnitTests.ObjectModel.ICommandTests
protected Task NoParameterTask() => Task.Delay(Delay);
protected Task IntParameterTask(int delay) => Task.Delay(delay);
protected Task NullableParameterTask(int? delay) => Task.Delay(delay ?? 0);
protected Task StringParameterTask(string? text) => Task.Delay(Delay);

Просмотреть файл

@ -8,7 +8,7 @@ namespace Xamarin.CommunityToolkit.Converters
/// </summary>
/// <typeparam name="TFrom">Type of the input value.</typeparam>
/// <typeparam name="TTo">Type of the output value.</typeparam>
public abstract class BaseConverter<TFrom, TTo> : BaseConverterOneWay<TFrom, TTo>
public abstract class BaseConverter<TFrom, TTo> : BaseConverterOneWay<TFrom, TTo> where TFrom : notnull where TTo : notnull
{
/// <summary>
/// Converts the incoming value from <see cref="TTo"/>[] and returns the object of a type <see cref="TFrom"/>.
@ -18,8 +18,11 @@ namespace Xamarin.CommunityToolkit.Converters
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>An object of the type <see cref="TFrom"/></returns>
public sealed override object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public sealed override object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is null)
throw new ArgumentNullException(nameof(value), $"value needs to be of type {typeof(TFrom)}, but is null. If type {typeof(TFrom)} should be nullable, inherit from {nameof(BaseNullableConverter<TFrom, TTo>)} instead");
if (value is not TTo valueFrom)
throw new ArgumentException($"value needs to be of type {typeof(TTo)}", nameof(value));
@ -36,4 +39,38 @@ namespace Xamarin.CommunityToolkit.Converters
/// <returns>An object of type <see cref="TFrom"/>.</returns>
public abstract TFrom ConvertBackTo(TTo value);
}
/// <summary>
/// Abstract class used to implement converters that supports null and implements the ConvertBack logic.
/// </summary>
/// <typeparam name="TFrom">Type of the input value.</typeparam>
/// <typeparam name="TTo">Type of the output value.</typeparam>
public abstract class BaseNullableConverter<TFrom, TTo> : BaseNullableConverterOneWay<TFrom, TTo>
{
/// <summary>
/// Converts the incoming value from <see cref="TTo"/>[] and returns the object of a type <see cref="TFrom"/>.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>An object of the type <see cref="TFrom"/></returns>
public sealed override object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is not TTo valueFrom)
throw new ArgumentException($"value needs to be of type {typeof(TTo)}", nameof(value));
if (targetType != typeof(TFrom) && !(typeof(TFrom) != typeof(string)))
throw new ArgumentException($"targetType needs to be typeof {typeof(TFrom)}", nameof(targetType));
return ConvertBackTo(valueFrom);
}
/// <summary>
/// Method that will be called by <see cref="ConvertBack(object, Type, object, CultureInfo)"/>.
/// </summary>
/// <param name="value">Value to be converted from <see cref="TTo"/> to <see cref="TFrom"/>.</param>
/// <returns>An object of type <see cref="TFrom"/>.</returns>
public abstract TFrom? ConvertBackTo(TTo? value);
}
}

Просмотреть файл

@ -6,31 +6,14 @@ using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Converters
{
/// <summary>
/// Abstract class used to implement converters that implements the ConvertBack logic.
/// Abstract class used to implement converters that do not implement the ConvertBack logic.
/// </summary>
/// <typeparam name="TFrom">Type of the input value</typeparam>
/// <typeparam name="TTo">Type of the output value</typeparam>
public abstract class BaseConverterOneWay<TFrom, TTo> : ValueConverterExtension, IValueConverter
where TFrom : notnull
where TTo : notnull
{
/// <summary>
/// Converts the incoming value from <see cref="TFrom"/>[] and returns the object of a type <see cref="TTo"/>.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>An object of type <see cref="TTo"/>.</returns>
public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is not TFrom valueFrom)
throw new ArgumentException($"value needs to be of type {typeof(TFrom)}");
if (targetType != typeof(TTo) && !(typeof(TFrom) != typeof(string)))
throw new ArgumentException($"targetType needs to be typeof {typeof(TTo)}");
return ConvertFrom(valueFrom);
}
/// <summary>
/// Method that will be called by <see cref="Convert(object, Type, object, CultureInfo)"/>.
/// </summary>
@ -40,7 +23,59 @@ namespace Xamarin.CommunityToolkit.Converters
/// <summary>
/// Not implemented, use <see cref="BaseConverter{TFrom, TTo}"/>
/// </summary>
public virtual object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotImplementedException("Impossible to revert to original value. Consider setting BindingMode to OneWay.");
=> throw new NotSupportedException("Impossible to revert to original value. Consider setting BindingMode to OneWay.");
/// <inheritdoc/>
object? IValueConverter.Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is null)
throw new ArgumentNullException(nameof(value), $"value needs to be of type {typeof(TFrom)}, but is null. If type {typeof(TFrom)} should be nullable, inherit from {nameof(BaseNullableConverterOneWay<TFrom, TTo>)} instead");
if (value is not TFrom valueFrom)
throw new ArgumentException($"value needs to be of type {typeof(TFrom)}");
if (targetType != typeof(TTo) && !(typeof(TFrom) != typeof(string)))
throw new ArgumentException($"targetType needs to be typeof {typeof(TTo)}");
return ConvertFrom(valueFrom);
}
}
/// <summary>
/// Abstract class used to implement converters that support null and do not implement the ConvertBack logic.
/// </summary>
/// <typeparam name="TFrom">Type of the input value</typeparam>
/// <typeparam name="TTo">Type of the output value</typeparam>
public abstract class BaseNullableConverterOneWay<TFrom, TTo> : ValueConverterExtension, IValueConverter
{
/// <summary>
/// Method that will be called by <see cref="Convert(object, Type, object, CultureInfo)"/>.
/// </summary>
/// <param name="value">Value to be converted from <see cref="TFrom"/> to <see cref="TTo"/>.</param>
/// <returns>An object of type <see cref="TTo"/>.</returns>
public abstract TTo? ConvertFrom(TFrom? value);
/// <summary>
/// Not implemented, use <see cref="BaseConverter{TFrom, TTo}"/>
/// </summary>
public virtual object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException("Impossible to revert to original value. Consider setting BindingMode to OneWay.");
/// <inheritdoc/>
object? IValueConverter.Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is null)
return ConvertFrom(default);
if (value is not TFrom valueFrom)
throw new ArgumentException($"value needs to be of type {typeof(TFrom)}");
if (targetType != typeof(TTo) && !(typeof(TFrom) != typeof(string)))
throw new ArgumentException($"targetType needs to be typeof {typeof(TTo)}");
return ConvertFrom(valueFrom);
}
}
}

Просмотреть файл

@ -1,8 +1,6 @@
using System;
using System.Globalization;
using System.IO;
using System.Threading;
using Xamarin.CommunityToolkit.Extensions.Internals;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Converters
@ -10,36 +8,14 @@ namespace Xamarin.CommunityToolkit.Converters
/// <summary>
/// Converts the incoming value from <see cref="byte"/>[] and returns the object of a type <see cref="ImageSource"/> or vice versa.
/// </summary>
public class ByteArrayToImageSourceConverter : ValueConverterExtension, IValueConverter
public class ByteArrayToImageSourceConverter : BaseNullableConverter<byte[], ImageSource?>
{
/// <summary>
/// Converts the incoming value from <see cref="byte"/>[] and returns the object of a type <see cref="ImageSource"/>.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>An object of type <see cref="ImageSource"/>.</returns>
public object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
{
if (value == null)
return null;
if (value is byte[] imageBytes)
return ImageSource.FromStream(() => new MemoryStream(imageBytes));
throw new ArgumentException("Expected value to be of type byte[].", nameof(value));
}
/// <summary>
/// Converts the incoming value from <see cref="StreamImageSource"/> and returns a <see cref="byte"/>[].
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>An object of type <see cref="ImageSource"/>.</returns>
public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
public override byte[]? ConvertBackTo(ImageSource? value)
{
if (value == null)
return null;
@ -59,5 +35,18 @@ namespace Xamarin.CommunityToolkit.Converters
throw new ArgumentException("Expected value to be of type StreamImageSource.", nameof(value));
}
/// <summary>
/// Converts the incoming value from <see cref="byte"/>[] and returns the object of a type <see cref="ImageSource"/>.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <returns>An object of type <see cref="ImageSource"/>.</returns>
public override ImageSource? ConvertFrom(byte[]? value)
{
if (value == null)
return null;
return ImageSource.FromStream(() => new MemoryStream(value));
}
}
}

Просмотреть файл

@ -1,6 +1,3 @@
using System;
using System.Globalization;
using Xamarin.CommunityToolkit.Extensions;
using Xamarin.Forms;

Просмотреть файл

@ -1,43 +1,29 @@
using System;
using Xamarin.CommunityToolkit.Extensions.Internals;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Converters
{
/// <summary>
/// Converts <see cref="DateTimeOffset"/> to <see cref="DateTime"/> and back.
/// </summary>
public class DateTimeOffsetConverter : ValueConverterExtension, IValueConverter
public class DateTimeOffsetConverter : BaseConverter<DateTimeOffset, DateTime>
{
/// <summary>
/// Converts <see cref="DateTimeOffset"/> to <see cref="DateTime"/>
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>The <see cref="DateTime"/> value.</returns>
public object Convert(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo culture)
=> value is DateTimeOffset dateTimeOffset
? dateTimeOffset.DateTime
: throw new ArgumentException("Value is not a valid DateTimeOffset", nameof(value));
/// <summary>
/// Converts <see cref="DateTime"/> back to <see cref="DateTimeOffset"/>.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented..</param>
/// <returns>The <see cref="DateTimeOffset"/> value.</returns>
public object ConvertBack(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo culture)
=> value is DateTime dateTime
? dateTime.Kind switch
public override DateTimeOffset ConvertBackTo(DateTime value) => value.Kind switch
{
DateTimeKind.Local => new DateTimeOffset(dateTime, DateTimeOffset.Now.Offset),
DateTimeKind.Utc => new DateTimeOffset(dateTime, DateTimeOffset.UtcNow.Offset),
_ => new DateTimeOffset(dateTime, TimeSpan.Zero),
}
: throw new ArgumentException("Value is not a valid DateTime", nameof(value));
DateTimeKind.Local => new DateTimeOffset(DateTime.SpecifyKind(value, DateTimeKind.Unspecified), DateTimeOffset.Now.Offset),
DateTimeKind.Utc => new DateTimeOffset(value, DateTimeOffset.UtcNow.Offset),
_ => new DateTimeOffset(value, TimeSpan.Zero)
};
/// <summary>
/// Converts <see cref="DateTimeOffset"/> to <see cref="DateTime"/>
/// </summary>
/// <param name="value">The value to convert.</param>
/// <returns>The <see cref="DateTime"/> value.</returns>
public override DateTime ConvertFrom(DateTimeOffset value) => value.DateTime;
}
}

Просмотреть файл

@ -1,6 +1,4 @@
using System;
using System.Globalization;
using Xamarin.Forms;
using Xamarin.Forms;
#if NETSTANDARD1_0 || UAP10_0
using System.Reflection;
#endif
@ -10,31 +8,23 @@ namespace Xamarin.CommunityToolkit.Converters
/// <summary>
/// Converts embedded image resource ID to it ImageSource.
/// </summary>
public class ImageResourceConverter : IValueConverter
public class ImageResourceConverter : BaseNullableConverterOneWay<string, ImageSource>
{
/// <summary>
/// Converts embedded image resource ID to it ImageSource.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>The ImageSource related to the provided resource ID of the embedded image. If it's null it will returns null.</returns>
public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
public override ImageSource? ConvertFrom(string? value)
{
if (value == null)
return null;
if (value is not string imageId)
throw new ArgumentException("Value is not a string", nameof(value));
return ImageSource.FromResource(imageId, Application.Current.GetType()
return ImageSource.FromResource(value, Application.Current.GetType()
#if NETSTANDARD1_0 || UAP10_0
.GetTypeInfo()
#endif
.Assembly);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
}
}

Просмотреть файл

@ -1,42 +1,22 @@
using System;
using System.Globalization;
using Xamarin.CommunityToolkit.Extensions.Internals;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Converters
namespace Xamarin.CommunityToolkit.Converters
{
/// <summary>
/// Converts an <see cref="int"/> to corresponding <see cref="bool"/> and vice versa.
/// </summary>
public class IntToBoolConverter : ValueConverterExtension, IValueConverter
public class IntToBoolConverter : BaseConverter<int, bool>
{
/// <summary>
/// Converts an <see cref="int"/> to corresponding <see cref="bool"/>.
/// </summary>
/// <param name="value"><see cref="int"/> value.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>False if the value is 0, otherwise if the value is anything but 0 it returns True.</returns>
public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
=> value is int result
? result != 0
: throw new ArgumentException("Value is not a valid integer", nameof(value));
/// <summary>
/// Converts back <see cref="bool"/> to corresponding <see cref="int"/>.
/// </summary>
/// <param name="value"><see cref="bool"/> value.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>0 if the value is False, otherwise 1 if the value is True.</returns>
public object ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
{
if (value is bool result)
return result ? 1 : 0;
public override int ConvertBackTo(bool value) => value ? 1 : 0;
throw new ArgumentException("Value is not a valid boolean", nameof(value));
}
/// <summary>
/// Converts an <see cref="int"/> to corresponding <see cref="bool"/>.
/// </summary>
/// <param name="value"><see cref="int"/> value.</param>
/// <returns>False if the value is 0, otherwise if the value is anything but 0 it returns True.</returns>
public override bool ConvertFrom(int value) => value != 0;
}
}

Просмотреть файл

@ -1,48 +1,22 @@
using System;
using System.Globalization;
using Xamarin.CommunityToolkit.Extensions.Internals;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Converters
namespace Xamarin.CommunityToolkit.Converters
{
/// <summary>
/// Converts true to false and false to true. Simple as that!
/// </summary>
public class InvertedBoolConverter : ValueConverterExtension, IValueConverter
public class InvertedBoolConverter : BaseConverter<bool, bool>
{
/// <summary>
/// Converts a <see cref="bool"/> to its inverse value.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>An inverted <see cref="bool"/> from the one coming in.</returns>
public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
=> InverseBool(value);
public override bool ConvertBackTo(bool value) => ConvertFrom(value);
/// <summary>
/// Converts a <see cref="bool"/> to its inverse value.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>An inverted <see cref="bool"/> from the one coming in.</returns>
public object ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
=> InverseBool(value);
/// <summary>
/// Inverses an incoming <see cref="bool"/>.
/// </summary>
/// <param name="value">The value to inverse.</param>
/// <returns>The inverted value of the incoming <see cref="bool"/>.</returns>
bool InverseBool(object? value)
{
if (value is bool result)
return !result;
throw new ArgumentException("Value is not a valid boolean", nameof(value));
}
public override bool ConvertFrom(bool value) => !value;
}
}

Просмотреть файл

@ -1,6 +1,4 @@
using System;
using System.Globalization;
using Xamarin.CommunityToolkit.Extensions.Internals;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Converters
@ -8,13 +6,18 @@ namespace Xamarin.CommunityToolkit.Converters
/// <summary>
/// Checks if the value is between minValue and maxValue, returning true if the value is within the range and false if the value is out of the range.
/// </summary>
public class IsInRangeConverter : ValueConverterExtension, IValueConverter
public class IsInRangeConverter : BaseConverterOneWay<IComparable, bool>
{
/// <summary>
/// Backing BindableProperty for the <see cref="MinValue"/> property.
/// </summary>
public static readonly BindableProperty MinValueProperty = BindableProperty.Create(nameof(MinValue), typeof(object), typeof(IsInRangeConverter));
/// <summary>
/// Backing BindableProperty for the <see cref="MaxValue"/> property.
/// </summary>
public static readonly BindableProperty MaxValueProperty = BindableProperty.Create(nameof(MaxValue), typeof(object), typeof(IsInRangeConverter));
/// <summary>
/// Gets or sets the minimum value of the range for the <see cref="IsInRangeConverter"/>. This is a bindable property.
/// </summary>
@ -24,11 +27,6 @@ namespace Xamarin.CommunityToolkit.Converters
set => SetValue(MinValueProperty, value);
}
/// <summary>
/// Backing BindableProperty for the <see cref="MaxValue"/> property.
/// </summary>
public static readonly BindableProperty MaxValueProperty = BindableProperty.Create(nameof(MaxValue), typeof(object), typeof(IsInRangeConverter));
/// <summary>
/// Gets or sets the maximum value of the range for the <see cref="IsInRangeConverter"/>. This is a bindable property.
/// </summary>
@ -42,33 +40,16 @@ namespace Xamarin.CommunityToolkit.Converters
/// Checks if the value is between minValue and maxValue, returning true if the value is within the range and false if the value is out of the range.
/// </summary>
/// <param name="value">The object to compare.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>True if <paramref name="value"/> and <paramref name="parameter"/> are equal, False if they are not equal.</returns>
public object Convert(object value, Type targetType, object? parameter, CultureInfo culture)
public override bool ConvertFrom(IComparable value)
{
if (value is not IComparable comparable)
throw new ArgumentException("is expected to implement IComparable interface.", nameof(value));
if (MinValue is not IComparable)
throw new ArgumentException("is expected to implement IComparable interface.", nameof(MinValue));
if (MaxValue is not IComparable)
throw new ArgumentException("is expected to implement IComparable interface.", nameof(MaxValue));
return comparable.CompareTo(MinValue) >= 0 && comparable.CompareTo(MaxValue) <= 0;
return value.CompareTo(MinValue) >= 0 && value.CompareTo(MaxValue) <= 0;
}
/// <summary>
/// This method is not implemented and will throw a <see cref="NotImplementedException"/>.
/// </summary>
/// <param name="value">N/A</param>
/// <param name="targetType">N/A</param>
/// <param name="parameter">N/A</param>
/// <param name="culture">N/A</param>
/// <returns>N/A</returns>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
throw new NotImplementedException();
}
}

Просмотреть файл

@ -1,35 +1,15 @@
using System;
using System.Globalization;
using Xamarin.CommunityToolkit.Extensions.Internals;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Converters
namespace Xamarin.CommunityToolkit.Converters
{
/// <summary>
/// Converts the incoming value to a <see cref="bool"/> indicating whether or not the value is not null and not empty.
/// </summary>
public class IsNotNullOrEmptyConverter : ValueConverterExtension, IValueConverter
public class IsNotNullOrEmptyConverter : BaseNullableConverterOneWay<object, bool>
{
/// <summary>
/// Converts the incoming value to a <see cref="bool"/> indicating whether or not the value is not null and not empty.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>A <see cref="bool"/> indicating if the incoming value is not null and not empty.</returns>
public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture) =>
!IsNullOrEmptyConverter.ConvertInternal(value);
/// <summary>
/// This method is not implemented and will throw a <see cref="NotImplementedException"/>.
/// </summary>
/// <param name="value">N/A</param>
/// <param name="targetType">N/A</param>
/// <param name="parameter">N/A</param>
/// <param name="culture">N/A</param>
/// <returns>N/A</returns>
public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
=> throw new NotImplementedException();
public override bool ConvertFrom(object? value) => !IsNullOrEmptyConverter.ConvertInternal(value);
}
}

Просмотреть файл

@ -1,37 +1,18 @@
using System;
using System.Globalization;
using Xamarin.CommunityToolkit.Extensions.Internals;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Converters
namespace Xamarin.CommunityToolkit.Converters
{
/// <summary>
/// Converts the incoming value to a <see cref="bool"/> indicating whether or not the value is null or empty.
/// </summary>
public class IsNullOrEmptyConverter : ValueConverterExtension, IValueConverter
public class IsNullOrEmptyConverter : BaseNullableConverterOneWay<object, bool>
{
/// <summary>
/// Converts the incoming value to a <see cref="bool"/> indicating whether or not the value is null or empty.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>A <see cref="bool"/> indicating if the incoming value is null or empty.</returns>
public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture) => ConvertInternal(value);
public override bool ConvertFrom(object? value) => ConvertInternal(value);
internal static bool ConvertInternal(object? value) =>
value == null || (value is string str && string.IsNullOrWhiteSpace(str));
/// <summary>
/// This method is not implemented and will throw a <see cref="NotImplementedException"/>.
/// </summary>
/// <param name="value">N/A</param>
/// <param name="targetType">N/A</param>
/// <param name="parameter">N/A</param>
/// <param name="culture">N/A</param>
/// <returns>N/A</returns>
public object ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
=> throw new NotImplementedException();
}
}

Просмотреть файл

@ -1,42 +1,17 @@
using System;
using System.Globalization;
using Xamarin.CommunityToolkit.Extensions.Internals;
using Xamarin.Forms;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Converters
{
/// <summary>
/// Converts/Extracts the incoming value from <see cref="SelectedItemChangedEventArgs"/> object and returns the value of <see cref="SelectedItemChangedEventArgs.SelectedItem"/> property from it.
/// </summary>
public class ItemSelectedEventArgsConverter : ValueConverterExtension, IValueConverter
public class ItemSelectedEventArgsConverter : BaseNullableConverterOneWay<SelectedItemChangedEventArgs, object>
{
/// <summary>
/// Converts/Extracts the incoming value from <see cref="SelectedItemChangedEventArgs"/> object and returns the value of <see cref="SelectedItemChangedEventArgs.SelectedItem"/> property from it.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>A <see cref="SelectedItemChangedEventArgs.SelectedItem"/> object from object of type <see cref="SelectedItemChangedEventArgs"/>.</returns>
public object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
{
if (value == null)
return null;
return value is SelectedItemChangedEventArgs selectedItemChangedEventArgs
? selectedItemChangedEventArgs.SelectedItem
: throw new ArgumentException("Expected value to be of type SelectedItemChangedEventArgs", nameof(value));
}
/// <summary>
/// This method is not implemented and will throw a <see cref="NotImplementedException"/>.
/// </summary>
/// <param name="value">N/A</param>
/// <param name="targetType">N/A</param>
/// <param name="parameter">N/A</param>
/// <param name="culture">N/A</param>
/// <returns>N/A</returns>
public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
=> throw new NotImplementedException();
public override object? ConvertFrom(SelectedItemChangedEventArgs? value) => value?.SelectedItem;
}
}

Просмотреть файл

@ -1,42 +1,17 @@
using System;
using System.Globalization;
using Xamarin.CommunityToolkit.Extensions.Internals;
using Xamarin.Forms;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Converters
{
/// <summary>
/// Converts/Extracts the incoming value from <see cref="ItemTappedEventArgs"/> object and returns the value of <see cref="ItemTappedEventArgs.Item"/> property from it.
/// </summary>
public class ItemTappedEventArgsConverter : ValueConverterExtension, IValueConverter
public class ItemTappedEventArgsConverter : BaseNullableConverterOneWay<ItemTappedEventArgs, object>
{
/// <summary>
/// Converts/Extracts the incoming value from <see cref="ItemTappedEventArgs"/> object and returns the value of <see cref="ItemTappedEventArgs.Item"/> property from it.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>A <see cref="ItemTappedEventArgs.Item"/> object from object of type <see cref="ItemTappedEventArgs"/>.</returns>
public object? Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture)
{
if (value == null)
return null;
return value is ItemTappedEventArgs itemTappedEventArgs
? itemTappedEventArgs.Item
: throw new ArgumentException("Expected value to be of type ItemTappedEventArgs", nameof(value));
}
/// <summary>
/// This method is not implemented and will throw a <see cref="NotImplementedException"/>.
/// </summary>
/// <param name="value">N/A</param>
/// <param name="targetType">N/A</param>
/// <param name="parameter">N/A</param>
/// <param name="culture">N/A</param>
/// <returns>N/A</returns>
public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
=> throw new NotImplementedException();
public override object? ConvertFrom(ItemTappedEventArgs? value) => value?.Item;
}
}

Просмотреть файл

@ -1,35 +1,17 @@
using System;
using System.Globalization;
using Xamarin.CommunityToolkit.Extensions.Internals;
using Xamarin.Forms;
using System.Collections;
namespace Xamarin.CommunityToolkit.Converters
{
/// <summary>
/// Converts the incoming value to a <see cref="bool"/> indicating whether or not the value is not null and not empty.
/// </summary>
public class ListIsNotNullOrEmptyConverter : ValueConverterExtension, IValueConverter
public class ListIsNotNullOrEmptyConverter : BaseNullableConverterOneWay<IEnumerable, bool>
{
/// <summary>
/// Converts the incoming value to a <see cref="bool"/> indicating whether or not the value is not null and not empty.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>A <see cref="bool"/> indicating if the incoming value is not null and not empty.</returns>
public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture) =>
!ListIsNullOrEmptyConverter.ConvertInternal(value);
/// <summary>
/// This method is not implemented and will throw a <see cref="NotImplementedException"/>.
/// </summary>
/// <param name="value">N/A</param>
/// <param name="targetType">N/A</param>
/// <param name="parameter">N/A</param>
/// <param name="culture">N/A</param>
/// <returns>N/A</returns>
public object ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
=> throw new NotImplementedException();
public override bool ConvertFrom(IEnumerable? value) => !ListIsNullOrEmptyConverter.ConvertInternal(value);
}
}

Просмотреть файл

@ -1,46 +1,25 @@
using System;
using System.Collections;
using System.Globalization;
using Xamarin.CommunityToolkit.Extensions.Internals;
using Xamarin.Forms;
using System.Collections;
namespace Xamarin.CommunityToolkit.Converters
{
/// <summary>
/// Converts the incoming value to a <see cref="bool"/> indicating whether or not the value is null or empty.
/// </summary>
public class ListIsNullOrEmptyConverter : ValueConverterExtension, IValueConverter
public class ListIsNullOrEmptyConverter : BaseNullableConverterOneWay<IEnumerable, bool>
{
/// <summary>
/// Converts the incoming value to a <see cref="bool"/> indicating whether or not the value is null or empty.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <param name="targetType">The type of the binding target property. This is not implemented.</param>
/// <param name="parameter">Additional parameter for the converter to handle. This is not implemented.</param>
/// <param name="culture">The culture to use in the converter. This is not implemented.</param>
/// <returns>A <see cref="bool"/> indicating if the incoming value is null or empty.</returns>
public object Convert(object? value, Type? targetType, object? parameter, CultureInfo? culture) => ConvertInternal(value);
public override bool ConvertFrom(IEnumerable? value) => ConvertInternal(value);
internal static bool ConvertInternal(object? value)
internal static bool ConvertInternal(IEnumerable? value)
{
if (value == null)
return true;
if (value is IEnumerable list)
return !list.GetEnumerator().MoveNext();
throw new ArgumentException("Value is not a valid IEnumerable or null", nameof(value));
return !value.GetEnumerator().MoveNext();
}
/// <summary>
/// This method is not implemented and will throw a <see cref="NotImplementedException"/>.
/// </summary>
/// <param name="value">N/A</param>
/// <param name="targetType">N/A</param>
/// <param name="parameter">N/A</param>
/// <param name="culture">N/A</param>
/// <returns>N/A</returns>
public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture)
=> throw new NotImplementedException();
}
}

Просмотреть файл

@ -120,6 +120,7 @@ namespace Xamarin.CommunityToolkit.UI.Views
get => (double)GetValue(VolumeProperty);
set => SetValue(VolumeProperty, value);
}
public double Speed
{
get => (double)GetValue(SpeedProperty);

Просмотреть файл

@ -5,7 +5,7 @@ using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.UI.Views
{
partial class SnackBar
internal partial class SnackBar
{
internal partial ValueTask Show(VisualElement sender, SnackBarOptions arguments) => throw new PlatformNotSupportedException();
}

Просмотреть файл

@ -5,7 +5,7 @@ using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.UI.Views
{
partial class SnackBar
internal partial class SnackBar
{
internal partial ValueTask Show(VisualElement sender, SnackBarOptions arguments);

Просмотреть файл

@ -6,7 +6,7 @@ using EButton = ElmSharp.Button;
namespace Xamarin.CommunityToolkit.UI.Views
{
partial class SnackBar
internal partial class SnackBar
{
internal partial ValueTask Show(Forms.VisualElement sender, SnackBarOptions arguments)
{

Просмотреть файл

@ -1,9 +1,9 @@
using System;
using NUnit.Framework;
using Xamarin.Forms;
using System.Linq;
using System.Windows.Input;
using NUnit.Framework;
using Xamarin.CommunityToolkit.Markup.UnitTests.BindableObjectViews;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Markup.UnitTests
{
@ -88,7 +88,7 @@ namespace Xamarin.CommunityToolkit.Markup.UnitTests
Label.TextColorProperty,
nameof(viewModel.IsRed),
assertConverterInstanceIsAnyNotNull: true,
assertConvert: c => c.AssertConvert<bool?, Color>(true, Color.Red).AssertConvert<bool?,Color>(false, Color.Transparent)
assertConvert: c => c.AssertConvert<bool?, Color>(true, Color.Red).AssertConvert<bool?, Color>(false, Color.Transparent)
);
}

Просмотреть файл

@ -9,9 +9,10 @@ using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Markup.UnitTests
{
#pragma warning disable SA1200 // Using directives should be placed correctly
using Xamarin.CommunityToolkit.Markup.UnitTests.DefaultBindablePropertiesViews;
// These usings are placed here to avoid ambiguities
using Xamarin.Forms.Shapes;
using Xamarin.CommunityToolkit.Markup.UnitTests.DefaultBindablePropertiesViews;
#pragma warning restore SA1200 // Using directives should be placed correctly
[TestFixture]
@ -220,11 +221,11 @@ namespace Xamarin.CommunityToolkit.Markup.UnitTests
namespace Xamarin.CommunityToolkit.Markup.UnitTests.DefaultBindablePropertiesViews // This namespace simulates derived controls defined in a separate app, for use in the tests in this file only
#pragma warning restore SA1403 // File may only contain a single namespace
{
#pragma warning disable SA1200 // Using directives should be placed correctly
#pragma warning disable SA1200 // Using directives should be placed correctly
// These usings are placed here to avoid ambiguities
using System.Windows.Input;
using Xamarin.Forms;
#pragma warning restore SA1200 // Using directives should be placed correctly
#pragma warning restore SA1200 // Using directives should be placed correctly
class DerivedFromBoxView : BoxView { }

Просмотреть файл

@ -19,7 +19,7 @@ namespace Xamarin.CommunityToolkit.Markup.UnitTests
{
var grid = new Forms.Grid
{
RowDefinitions = Rows.Define(Auto, Star, Stars (starsValue), 20)
RowDefinitions = Rows.Define(Auto, Star, Stars(starsValue), 20)
};
Assert.That(grid.RowDefinitions.Count, Is.EqualTo(4));
@ -35,9 +35,9 @@ namespace Xamarin.CommunityToolkit.Markup.UnitTests
var grid = new Forms.Grid
{
RowDefinitions = Rows.Define(
(Row.First , Auto),
(Row.First, Auto),
(Row.Second, Star),
(Row.Third , Stars (starsValue)),
(Row.Third, Stars(starsValue)),
(Row.Fourth, 20)
)
};
@ -63,7 +63,7 @@ namespace Xamarin.CommunityToolkit.Markup.UnitTests
{
var grid = new Forms.Grid
{
ColumnDefinitions = Columns.Define(Auto, Star, Stars (starsValue), 20, 40)
ColumnDefinitions = Columns.Define(Auto, Star, Stars(starsValue), 20, 40)
};
Assert.That(grid.ColumnDefinitions.Count, Is.EqualTo(5));
@ -80,11 +80,11 @@ namespace Xamarin.CommunityToolkit.Markup.UnitTests
var grid = new Forms.Grid
{
ColumnDefinitions = Columns.Define(
(Col.First , Auto),
(Col.First, Auto),
(Col.Second, Star),
(Col.Third , Stars(starsValue)),
(Col.Third, Stars(starsValue)),
(Col.Fourth, 20),
(Col.Fifth , 40)
(Col.Fifth, 40)
)
};

Просмотреть файл

@ -1,6 +1,6 @@
using NUnit.Framework;
using Xamarin.Forms;
using Xamarin.CommunityToolkit.Markup.LeftToRight;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Markup.UnitTests
{

Просмотреть файл

@ -1,6 +1,6 @@
using NUnit.Framework;
using Xamarin.Forms;
using Xamarin.CommunityToolkit.Markup.RightToLeft;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Markup.UnitTests
{

Просмотреть файл

@ -67,10 +67,10 @@ namespace Xamarin.CommunityToolkit.Markup.UnitTests
Layout.Children(
new Label { IsPlatformEnabled = true }
.Assign(out Label child)
.Constrain() .X (() => 30)
.Y (() => 20)
.Width (() => Layout.Height / 2)
.Height (() => Layout.Height / 4)
.Constrain().X(() => 30)
.Y(() => 20)
.Width(() => Layout.Height / 2)
.Height(() => Layout.Height / 4)
);
Layout.Layout(new Rectangle(0, 0, 100, 100));
@ -85,25 +85,25 @@ namespace Xamarin.CommunityToolkit.Markup.UnitTests
Layout.IsPlatformEnabled = true;
Layout.Children(
new Label { IsPlatformEnabled = true }
.Assign (out Label child0)
.Constraints().X (30)
.Y (20)
.Width (parent => parent.Height / 5)
.Height (parent => parent.Height / 10),
.Assign(out Label child0)
.Constraints().X(30)
.Y(20)
.Width(parent => parent.Height / 5)
.Height(parent => parent.Height / 10),
new Label { IsPlatformEnabled = true }
.Assign(out Label child1)
.Constraints().X (child0, (layout, view) => view.Bounds.Right + 10)
.Y (child0, (layout, view) => view.Y)
.Width (child0, (layout, view) => view.Width)
.Height (child0, (layout, view) => view.Height),
.Constraints().X(child0, (layout, view) => view.Bounds.Right + 10)
.Y(child0, (layout, view) => view.Y)
.Width(child0, (layout, view) => view.Width)
.Height(child0, (layout, view) => view.Height),
new Label { IsPlatformEnabled = true }
.Assign(out Label child2)
.Constraints().X (parent => parent.Height / 5)
.Y (parent => parent.Height / 10)
.Width (30)
.Height (20)
.Constraints().X(parent => parent.Height / 5)
.Y(parent => parent.Height / 10)
.Width(30)
.Height(20)
);
Layout.Layout(new Rectangle(0, 0, 100, 100));

Просмотреть файл

@ -1,6 +1,6 @@
using NUnit.Framework;
using Xamarin.Forms;
using Xamarin.CommunityToolkit.Markup.LeftToRight;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Markup.UnitTests
{

Просмотреть файл

@ -1,6 +1,6 @@
using NUnit.Framework;
using Xamarin.Forms;
using Xamarin.CommunityToolkit.Markup.RightToLeft;
using Xamarin.Forms;
namespace Xamarin.CommunityToolkit.Markup.UnitTests
{

Просмотреть файл

@ -114,7 +114,7 @@ namespace Xamarin.CommunityToolkit.Markup
do
{
string bindableObjectTypeName = bindableObjectType.FullName;
var bindableObjectTypeName = bindableObjectType.FullName;
if (bindableObjectTypeDefaultProperty.TryGetValue(bindableObjectTypeName, out defaultProperty))
break;
if (bindableObjectTypeName.StartsWith("Xamarin.Forms.", StringComparison.Ordinal))
@ -150,7 +150,7 @@ namespace Xamarin.CommunityToolkit.Markup
do
{
string bindableObjectTypeName = bindableObjectType.FullName;
var bindableObjectTypeName = bindableObjectType.FullName;
if (bindableObjectTypeDefaultCommandAndParameterProperties.TryGetValue(bindableObjectTypeName, out commandAndParameterProperties))
break;
if (bindableObjectTypeName.StartsWith("Xamarin.Forms.", StringComparison.Ordinal))

Просмотреть файл

@ -1,7 +1,7 @@
using Xamarin.Forms;
using Xamarin.Forms.Internals;
using PaddingElement = Xamarin.Forms.Label; // TODO: Get rid of this after we have default interface implementation in Forms for IPaddingElement
using FontElement = Xamarin.Forms.Label; // TODO: Get rid of this after we have default interface implementation in Forms for IFontElement
using PaddingElement = Xamarin.Forms.Label; // TODO: Get rid of this after we have default interface implementation in Forms for IPaddingElement
namespace Xamarin.CommunityToolkit.Markup
{

Просмотреть файл

@ -18,7 +18,7 @@ namespace Xamarin.CommunityToolkit.Markup
{
var columnDefinitions = new ColumnDefinitionCollection();
for (int i = 0; i < widths.Length; i++)
for (var i = 0; i < widths.Length; i++)
columnDefinitions.Add(new ColumnDefinition { Width = widths[i] });
return columnDefinitions;
@ -27,7 +27,7 @@ namespace Xamarin.CommunityToolkit.Markup
public static ColumnDefinitionCollection Define<TEnum>(params (TEnum name, GridLength width)[] columns) where TEnum : Enum
{
var columnDefinitions = new ColumnDefinitionCollection();
for (int i = 0; i < columns.Length; i++)
for (var i = 0; i < columns.Length; i++)
{
if (i != columns[i].name.ToInt())
{
@ -48,7 +48,7 @@ namespace Xamarin.CommunityToolkit.Markup
{
var rowDefinitions = new RowDefinitionCollection();
for (int i = 0; i < heights.Length; i++)
for (var i = 0; i < heights.Length; i++)
rowDefinitions.Add(new RowDefinition { Height = heights[i] });
return rowDefinitions;
@ -57,7 +57,7 @@ namespace Xamarin.CommunityToolkit.Markup
public static RowDefinitionCollection Define<TEnum>(params (TEnum name, GridLength height)[] rows) where TEnum : Enum
{
var rowDefinitions = new RowDefinitionCollection();
for (int i = 0; i < rows.Length; i++)
for (var i = 0; i < rows.Length; i++)
{
if (i != rows[i].name.ToInt())
{
@ -75,14 +75,14 @@ namespace Xamarin.CommunityToolkit.Markup
public static int All<TEnum>() where TEnum : Enum
{
var values = Enum.GetValues(typeof(TEnum));
int span = (int)values.GetValue(values.Length - 1) + 1;
var span = (int)values.GetValue(values.Length - 1) + 1;
return span;
}
public static int Last<TEnum>() where TEnum : Enum
{
var values = Enum.GetValues(typeof(TEnum));
int last = (int)values.GetValue(values.Length - 1);
var last = (int)values.GetValue(values.Length - 1);
return last;
}

Просмотреть файл

@ -34,7 +34,7 @@ namespace Xamarin.CommunityToolkit.Markup
}
}
#pragma warning disable SA1403 // File may only contain a single namespace
#pragma warning disable SA1403 // File may only contain a single namespace
// The extensions in these sub-namespaces are designed to be used together with the extensions in the parent namespace.
// Keep them in a single file for better maintainability
@ -61,5 +61,5 @@ namespace Xamarin.CommunityToolkit.Markup
{ label.HorizontalTextAlignment = TextAlignment.Start; return label; }
}
}
#pragma warning restore SA1403 // File may only contain a single namespace
#pragma warning restore SA1403 // File may only contain a single namespace
}

Просмотреть файл

@ -74,7 +74,7 @@ namespace Xamarin.CommunityToolkit.Markup
{ view.Margin = new Thickness(left, top, right, bottom); return view; }
}
#pragma warning disable SA1403 // File may only contain a single namespace
#pragma warning disable SA1403 // File may only contain a single namespace
// The extensions in these sub-namespaces are designed to be used together with the extensions in the parent namespace.
// Keep them in a single file for better maintainability
@ -113,5 +113,5 @@ namespace Xamarin.CommunityToolkit.Markup
{ view.HorizontalOptions = LayoutOptions.StartAndExpand; return view; }
}
}
#pragma warning restore SA1403 // File may only contain a single namespace
#pragma warning restore SA1403 // File may only contain a single namespace
}

Просмотреть файл

@ -46,15 +46,15 @@ namespace Xamarin.CommunityToolkit.Markup
public static TView Row<TView, TRow>(this TView view, TRow row) where TView : View where TRow : Enum
{
int rowIndex = row.ToInt();
var rowIndex = row.ToInt();
view.SetValue(Grid.RowProperty, rowIndex);
return view;
}
public static TView Row<TView, TRow>(this TView view, TRow first, TRow last) where TView : View where TRow : Enum
{
int rowIndex = first.ToInt();
int span = last.ToInt() - rowIndex + 1;
var rowIndex = first.ToInt();
var span = last.ToInt() - rowIndex + 1;
view.SetValue(Grid.RowProperty, rowIndex);
view.SetValue(Grid.RowSpanProperty, span);
return view;
@ -62,17 +62,17 @@ namespace Xamarin.CommunityToolkit.Markup
public static TView Column<TView, TColumn>(this TView view, TColumn column) where TView : View where TColumn : Enum
{
int columnIndex = column.ToInt();
var columnIndex = column.ToInt();
view.SetValue(Grid.ColumnProperty, columnIndex);
return view;
}
public static TView Column<TView, TColumn>(this TView view, TColumn first, TColumn last) where TView : View where TColumn : Enum
{
int columnIndex = first.ToInt();
var columnIndex = first.ToInt();
view.SetValue(Grid.ColumnProperty, columnIndex);
int span = last.ToInt() + 1 - columnIndex;
var span = last.ToInt() + 1 - columnIndex;
view.SetValue(Grid.ColumnSpanProperty, span);
return view;