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"));
+ }
+ }
+}