diff --git a/src/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs b/src/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs index b6528ab8bb41..c7c3ff16afc8 100644 --- a/src/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs +++ b/src/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs @@ -497,6 +497,15 @@ public TimeSpanConverter() { } public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } } + public partial class VersionConverter : System.ComponentModel.TypeConverter + { + public VersionConverter() { } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType) { throw null; } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type destinationType) { throw null; } + public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { throw null; } + public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { throw null; } + public override bool IsValid(System.ComponentModel.ITypeDescriptorContext context, object value) { throw null; } + } public partial class TypeConverter { public TypeConverter() { } diff --git a/src/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj b/src/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj index 8ab0533cdf38..bfcd7f2501e2 100644 --- a/src/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj +++ b/src/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj @@ -43,6 +43,7 @@ + diff --git a/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs b/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs index ee0f28fc894d..065aa3bce980 100644 --- a/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs +++ b/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs @@ -126,6 +126,7 @@ internal ReflectTypeDescriptionProvider() [typeof(TimeSpan)] = typeof(TimeSpanConverter), [typeof(Guid)] = typeof(GuidConverter), [typeof(Uri)] = typeof(UriTypeConverter), + [typeof(Version)] = typeof(VersionConverter), [typeof(Color)] = typeof(ColorConverter), [typeof(Point)] = typeof(PointConverter), [typeof(Rectangle)] = typeof(RectangleConverter), diff --git a/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/VersionTypeConverter.cs b/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/VersionTypeConverter.cs new file mode 100644 index 000000000000..538b0fe712fd --- /dev/null +++ b/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/VersionTypeConverter.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Globalization; + +namespace System.ComponentModel +{ + /// + /// Provides a type converter to convert Version objects to and + /// from various other representations. + /// + public class VersionConverter : TypeConverter + { + /// + /// Gets a value indicating whether this converter can convert an object in the + /// given source type to a Version. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == null) + throw new ArgumentNullException(nameof(sourceType)); + + return sourceType == typeof(string) || sourceType == typeof(Version); + } + + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + return destinationType == typeof(string) || destinationType == typeof(Version); + } + + /// + /// Converts the given object to a Version. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string versionString) + { + // Let the Version constructor throw any informative exceptions + return new Version(versionString); + } + + if (value is Version version) + { + return new Version(version.Major, version.Minor, version.Build, version.Revision); // return new instance + } + + throw GetConvertFromException(value); + } + + /// + /// Converts the given value object to + /// the specified destination type using the specified context and arguments. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + if (destinationType == null) + { + throw new ArgumentNullException(nameof(destinationType)); + } + + if (value is Version version) + { + if (destinationType == typeof(string)) + return version.ToString(); + + if (destinationType == typeof(Version)) + return new Version(version.Major, version.Minor, version.Build, version.Revision); + } + + throw GetConvertToException(value, destinationType); + } + + public override bool IsValid(ITypeDescriptorContext context, object value) + { + if (value is string version) + { + return Version.TryParse(version, out Version _); + } + return value is Version; + } + } +} diff --git a/src/System.ComponentModel.TypeConverter/tests/Performance/Perf.TypeDescriptorTests.cs b/src/System.ComponentModel.TypeConverter/tests/Performance/Perf.TypeDescriptorTests.cs index 63968f168703..e8f02cd70684 100644 --- a/src/System.ComponentModel.TypeConverter/tests/Performance/Perf.TypeDescriptorTests.cs +++ b/src/System.ComponentModel.TypeConverter/tests/Performance/Perf.TypeDescriptorTests.cs @@ -45,6 +45,7 @@ public class Perf_TypeDescriptorTests [InlineData(typeof(ClassIBase), typeof(IBaseConverter))] [InlineData(typeof(ClassIDerived), typeof(IBaseConverter))] [InlineData(typeof(Uri), typeof(UriTypeConverter))] + [InlineData(typeof(Version), typeof(VersionConverter))] public static void GetConverter(Type typeToConvert, Type expectedConverter) { const int innerIterations = 100; diff --git a/src/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj b/src/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj index 7883fd5176c6..fd520ccaf92e 100644 --- a/src/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj +++ b/src/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj @@ -75,6 +75,7 @@ + diff --git a/src/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs b/src/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs index ef7efcb5a8d7..6d7149671225 100644 --- a/src/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs +++ b/src/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs @@ -276,6 +276,7 @@ class FooBarDerived : FooBarBase new Tuple (typeof(ClassIBase), typeof(IBaseConverter)), new Tuple (typeof(ClassIDerived), typeof(IBaseConverter)), new Tuple (typeof(Uri), typeof(UriTypeConverter)), + new Tuple (typeof(Version), typeof(VersionConverter)), new Tuple (typeof(CultureInfo), typeof(CultureInfoConverter)) }; } diff --git a/src/System.ComponentModel.TypeConverter/tests/VersionTypeConverterTests.cs b/src/System.ComponentModel.TypeConverter/tests/VersionTypeConverterTests.cs new file mode 100644 index 000000000000..f5a2ea57d0af --- /dev/null +++ b/src/System.ComponentModel.TypeConverter/tests/VersionTypeConverterTests.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.ComponentModel.Tests +{ + public class VersionConverterTests : ConverterTestBase + { + private static VersionConverter s_converter = new VersionConverter(); + + [Fact] + public static void CanConvertFrom_WithContext() + { + CanConvertFrom_WithContext(new object[2, 2] + { + { typeof(string), true }, + { typeof(Version), true } + }, + VersionConverterTests.s_converter); + } + + [Fact] + public static void ConvertFrom_WithContext() + { + ConvertFrom_WithContext(new object[3, 3] + { + {"1.2", new Version(1, 2), null}, + {"1.2.3", new Version(1, 2, 3), null}, + {"1.2.3.4", new Version(1, 2, 3, 4), null} + }, + VersionConverterTests.s_converter); + } + + [Fact] + public static void ConvertFrom_WithContext_Negative() + { + Assert.Throws( + () => VersionConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, null)); + Assert.Throws( + () => VersionConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, "")); + Assert.Throws( + () => VersionConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, "1")); + } + } +}