Browse Source

Reworked TypeConverters.

I started fixing #1559 and decided to rework our `TypeConverter` infrastructure a bit.

This PR renames `AvaloniaDefaultTypeConverters` to `AvaloniaTypeConverters` and allow registration of 3rd party converters here.

`AvaloniaTypeConverters` improves upon the type converter functionality in the BCL by providing the followinng extra functionality:

- Allow registering non-constructed generic types (such as `AvaloniaList<>`) - `AvaloniaTypeConverters` will do the sensible thing and create an `AvaloniaListConverter<T>` with the correct type
- If no type converter is provided, look for a a static `Parse(string)` method which can be used to create an implicit type converter

This allows us to remove a bunch of `TypeConverter`s which just called the relevant `Parse` method.

Fixes #1559
Steven Kirk 7 years ago
parent
commit
7588b16044
34 changed files with 228 additions and 508 deletions
  1. 9 9
      src/Avalonia.Base/Collections/AvaloniaListConverter.cs
  2. 7 0
      src/Avalonia.Controls/Classes.cs
  3. 7 1
      src/Avalonia.Controls/ColumnDefinitions.cs
  4. 7 1
      src/Avalonia.Controls/RowDefinitions.cs
  5. 7 0
      src/Avalonia.Input/Cursors.cs
  6. 2 2
      src/Avalonia.Visuals/Media/Brush.cs
  7. 6 7
      src/Avalonia.Visuals/Media/BrushConverter.cs
  8. 7 0
      src/Avalonia.Visuals/Media/Geometry.cs
  9. 3 0
      src/Avalonia.Visuals/Media/IBrush.cs
  10. 15 0
      src/Avalonia.Visuals/Media/SolidColorBrush.cs
  11. 4 21
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  12. 80 0
      src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs
  13. 0 22
      src/Markup/Avalonia.Markup.Xaml/Converters/ClassesTypeConverter.cs
  14. 0 26
      src/Markup/Avalonia.Markup.Xaml/Converters/ColorTypeConverter.cs
  15. 0 24
      src/Markup/Avalonia.Markup.Xaml/Converters/ColumnDefinitionsTypeConverter.cs
  16. 0 19
      src/Markup/Avalonia.Markup.Xaml/Converters/CornerRadiusTypeConverter.cs
  17. 0 25
      src/Markup/Avalonia.Markup.Xaml/Converters/CursorTypeConverter.cs
  18. 0 24
      src/Markup/Avalonia.Markup.Xaml/Converters/GeometryTypeConverter.cs
  19. 0 24
      src/Markup/Avalonia.Markup.Xaml/Converters/GridLengthTypeConverter.cs
  20. 0 24
      src/Markup/Avalonia.Markup.Xaml/Converters/KeyGestureConverter.cs
  21. 0 23
      src/Markup/Avalonia.Markup.Xaml/Converters/MatrixTypeConverter.cs
  22. 1 4
      src/Markup/Avalonia.Markup.Xaml/Converters/MemberSelectorTypeConverter.cs
  23. 66 0
      src/Markup/Avalonia.Markup.Xaml/Converters/ParseTypeConverter.cs
  24. 0 23
      src/Markup/Avalonia.Markup.Xaml/Converters/PointTypeConverter.cs
  25. 0 23
      src/Markup/Avalonia.Markup.Xaml/Converters/RectTypeConverter.cs
  26. 0 23
      src/Markup/Avalonia.Markup.Xaml/Converters/RelativePointTypeConverter.cs
  27. 0 23
      src/Markup/Avalonia.Markup.Xaml/Converters/RelativeRectTypeConverter.cs
  28. 0 24
      src/Markup/Avalonia.Markup.Xaml/Converters/RowDefinitionsTypeConverter.cs
  29. 0 23
      src/Markup/Avalonia.Markup.Xaml/Converters/SizeTypeConverter.cs
  30. 0 23
      src/Markup/Avalonia.Markup.Xaml/Converters/ThicknessTypeConverter.cs
  31. 0 89
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs
  32. 1 1
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaTypeAttributeProvider.cs
  33. 5 0
      src/Markup/Avalonia.Markup.Xaml/Templates/MemberSelector.cs
  34. 1 0
      tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs

+ 9 - 9
src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaListTypeConverter.cs → src/Avalonia.Base/Collections/AvaloniaListConverter.cs

@@ -2,16 +2,16 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using System.ComponentModel;
 using System.Globalization;
-using Avalonia.Collections;
 using Avalonia.Utilities;
 
-namespace Avalonia.Markup.Xaml.Converters
+namespace Avalonia.Collections
 {
-    using Portable.Xaml.ComponentModel;
-	using System.ComponentModel;
-
-    public class AvaloniaListTypeConverter<T> : TypeConverter
+    /// <summary>
+    /// Creates an <see cref="AvaloniaList{T}"/> from a string representation.
+    /// </summary>
+    public class AvaloniaListConverter<T> : TypeConverter
     {
         public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
         {
@@ -21,13 +21,13 @@ namespace Avalonia.Markup.Xaml.Converters
         public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
         {
             var result = new AvaloniaList<T>();
+
+            // TODO: Use StringTokenizer here.
             var values = ((string)value).Split(',');
 
             foreach (var s in values)
             {
-                object v;
-
-                if (TypeUtilities.TryConvert(typeof(T), s, culture, out v))
+                if (TypeUtilities.TryConvert(typeof(T), s, culture, out var v))
                 {
                     result.Add((T)v);
                 }

+ 7 - 0
src/Avalonia.Controls/Classes.cs

@@ -41,6 +41,13 @@ namespace Avalonia.Controls
         {            
         }
 
+        /// <summary>
+        /// Parses a classes string.
+        /// </summary>
+        /// <param name="s">The string.</param>
+        /// <returns>The <see cref="Classes"/>.</returns>
+        public static Classes Parse(string s) => new Classes(s.Split(' '));
+
         /// <summary>
         /// Adds a style class to the collection.
         /// </summary>

+ 7 - 1
src/Avalonia.Controls/ColumnDefinitions.cs

@@ -1,7 +1,6 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
-using System.Globalization;
 using System.Linq;
 using Avalonia.Collections;
 
@@ -29,5 +28,12 @@ namespace Avalonia.Controls
         {
             AddRange(GridLength.ParseLengths(s).Select(x => new ColumnDefinition(x)));
         }
+
+        /// <summary>
+        /// Parses a string representation of column definitions collection.
+        /// </summary>
+        /// <param name="s">The column definitions string.</param>
+        /// <returns>The <see cref="ColumnDefinitions"/>.</returns>
+        public static ColumnDefinitions Parse(string s) => new ColumnDefinitions(s);
     }
 }

+ 7 - 1
src/Avalonia.Controls/RowDefinitions.cs

@@ -1,7 +1,6 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
-using System.Globalization;
 using System.Linq;
 using Avalonia.Collections;
 
@@ -29,5 +28,12 @@ namespace Avalonia.Controls
         {
             AddRange(GridLength.ParseLengths(s).Select(x => new RowDefinition(x)));
         }
+
+        /// <summary>
+        /// Parses a string representation of row definitions collection.
+        /// </summary>
+        /// <param name="s">The row definitions string.</param>
+        /// <returns>The <see cref="RowDefinitions"/>.</returns>
+        public static RowDefinitions Parse(string s) => new RowDefinitions(s);
     }
 }

+ 7 - 0
src/Avalonia.Input/Cursors.cs

@@ -65,6 +65,13 @@ namespace Avalonia.Input
 
         public IPlatformHandle PlatformCursor { get; }
 
+        public static Cursor Parse(string s)
+        {
+            return Enum.TryParse<StandardCursorType>(s, true, out var t) ?
+                new Cursor(t) :
+                throw new ArgumentException($"Unrecognised cursor type '{s}'.");
+        }
+
         private static IPlatformHandle GetCursor(StandardCursorType type)
         {
             var platform = AvaloniaLocator.Current.GetService<IStandardCursorFactory>();

+ 2 - 2
src/Avalonia.Visuals/Media/Brush.cs

@@ -2,14 +2,14 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
-using System.Linq;
-using System.Reflection;
+using System.ComponentModel;
 
 namespace Avalonia.Media
 {
     /// <summary>
     /// Describes how an area is painted.
     /// </summary>
+    [TypeConverter(typeof(BrushConverter))]
     public abstract class Brush : AvaloniaObject, IBrush
     {
         /// <summary>

+ 6 - 7
src/Markup/Avalonia.Markup.Xaml/Converters/BrushTypeConverter.cs → src/Avalonia.Visuals/Media/BrushConverter.cs

@@ -2,16 +2,15 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using System.ComponentModel;
 using System.Globalization;
 
-using Avalonia.Media;
-
-namespace Avalonia.Markup.Xaml.Converters
+namespace Avalonia.Media
 {
-    using Portable.Xaml.ComponentModel;
-	using System.ComponentModel;
-
-    public class BrushTypeConverter : TypeConverter
+    /// <summary>
+    /// Creates an <see cref="IBrush"/> from a string representation.
+    /// </summary>
+    public class BrushConverter : TypeConverter
     {
         public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
         {

+ 7 - 0
src/Avalonia.Visuals/Media/Geometry.cs

@@ -69,6 +69,13 @@ namespace Avalonia.Media
             set { SetValue(TransformProperty, value); }
         }
 
+        /// <summary>
+        /// Creates a <see cref="Geometry"/> from a string.
+        /// </summary>
+        /// <param name="s">The string.</param>
+        /// <returns>A <see cref="StreamGeometry"/>.</returns>
+        public static Geometry Parse(string s) => StreamGeometry.Parse(s);
+
         /// <summary>
         /// Clones the geometry.
         /// </summary>

+ 3 - 0
src/Avalonia.Visuals/Media/IBrush.cs

@@ -1,11 +1,14 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
+using System.ComponentModel;
+
 namespace Avalonia.Media
 {
     /// <summary>
     /// Describes how an area is painted.
     /// </summary>
+    [TypeConverter(typeof(BrushConverter))]
     public interface IBrush
     {
         /// <summary>

+ 15 - 0
src/Avalonia.Visuals/Media/SolidColorBrush.cs

@@ -52,6 +52,21 @@ namespace Avalonia.Media
             set { SetValue(ColorProperty, value); }
         }
 
+        /// <summary>
+        /// Parses a brush string.
+        /// </summary>
+        /// <param name="s">The brush string.</param>
+        /// <returns>The <see cref="Color"/>.</returns>
+        /// <remarks>
+        /// Whereas <see cref="Brush.Parse(string)"/> may return an immutable solid color brush,
+        /// this method always returns a mutable <see cref="SolidColorBrush"/>.
+        /// </remarks>
+        public static new SolidColorBrush Parse(string s)
+        {
+            var brush = (ISolidColorBrush)Brush.Parse(s);
+            return brush is SolidColorBrush solid ? solid : new SolidColorBrush(brush.Color);
+        }
+
         /// <summary>
         /// Returns a string representation of the brush.
         /// </summary>

+ 4 - 21
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@@ -30,10 +30,10 @@
             <Link>Properties\SharedAssemblyInfo.cs</Link>
         </Compile>
         <Compile Include="AvaloniaXamlLoader.cs" />
-        <Compile Include="Converters\CornerRadiusTypeConverter.cs" />
-        <Compile Include="Converters\MatrixTypeConverter.cs" />
-        <Compile Include="Converters\RectTypeConverter.cs" />
+        <Compile Include="Converters\MemberSelectorTypeConverter.cs" />
+        <Compile Include="Converters\ParseTypeConverter.cs" />
         <Compile Include="Converters\SetterValueTypeConverter.cs" />
+        <Compile Include="Converters\TimeSpanTypeConverter.cs" />
         <Compile Include="Data\ResourceInclude.cs" />
         <Compile Include="MarkupExtensions\DynamicResourceExtension.cs" />
         <Compile Include="MarkupExtensions\StaticResourceExtension.cs" />
@@ -42,32 +42,15 @@
         <Compile Include="PortableXaml\AttributeExtensions.cs" />
         <Compile Include="PortableXaml\AvaloniaMemberAttributeProvider.cs" />
         <Compile Include="PortableXaml\AvaloniaNameScope.cs" />
-        <Compile Include="PortableXaml\AvaloniaDefaultTypeConverters.cs" />
+        <Compile Include="AvaloniaTypeConverters.cs" />
         <Compile Include="PortableXaml\AvaloniaXamlObjectWriter.cs" />
         <Compile Include="PortableXaml\AvaloniaRuntimeTypeProvider.cs" />
         <Compile Include="PortableXaml\AvaloniaXamlSchemaContext.cs" />
         <Compile Include="Converters\BitmapTypeConverter.cs" />
-        <Compile Include="Converters\BrushTypeConverter.cs" />
-        <Compile Include="Converters\ClassesTypeConverter.cs" />
-        <Compile Include="Converters\ColorTypeConverter.cs" />
-        <Compile Include="Converters\ColumnDefinitionsTypeConverter.cs" />
-        <Compile Include="Converters\CursorTypeConverter.cs" />
-        <Compile Include="Converters\GeometryTypeConverter.cs" />
-        <Compile Include="Converters\GridLengthTypeConverter.cs" />
         <Compile Include="Converters\IconTypeConverter.cs" />
-        <Compile Include="Converters\KeyGestureConverter.cs" />
-        <Compile Include="Converters\MemberSelectorTypeConverter.cs" />
-        <Compile Include="Converters\AvaloniaListTypeConverter.cs" />
         <Compile Include="Converters\AvaloniaPropertyTypeConverter.cs" />
         <Compile Include="Converters\PointsListTypeConverter.cs" />
-        <Compile Include="Converters\SizeTypeConverter.cs" />
-        <Compile Include="Converters\PointTypeConverter.cs" />
-        <Compile Include="Converters\RelativePointTypeConverter.cs" />
-        <Compile Include="Converters\RelativeRectTypeConverter.cs" />
-        <Compile Include="Converters\RowDefinitionsTypeConverter.cs" />
         <Compile Include="Converters\SelectorTypeConverter.cs" />
-        <Compile Include="Converters\ThicknessTypeConverter.cs" />
-        <Compile Include="Converters\TimeSpanTypeConverter.cs" />
         <Compile Include="Data\Binding.cs" />
         <Compile Include="Data\DelayedBinding.cs" />
         <Compile Include="Data\MultiBinding.cs" />

+ 80 - 0
src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs

@@ -0,0 +1,80 @@
+using System;
+using System.ComponentModel;
+using System.Collections.Generic;
+using Avalonia.Collections;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml.Converters;
+using Avalonia.Media.Imaging;
+using Avalonia.Styling;
+using Avalonia.Controls.Templates;
+
+namespace Avalonia.Markup.Xaml
+{
+    /// <summary>
+    /// Maintains a repository of <see cref="TypeConverter"/>s for XAML parsing on top of those
+    /// maintained by <see cref="TypeDescriptor"/>.
+    /// </summary>
+    /// <remarks>
+    /// The default method of defining type converters using <see cref="TypeConverterAttribute"/>
+    /// isn't powerful enough for our purposes:
+    /// 
+    /// - It doesn't handle non-constructed generic types (such as <see cref="AvaloniaList{T}"/>)
+    /// - Type converters which require XAML features cannot be defined in non-XAML assemblies and
+    ///   so can't be referenced using <see cref="TypeConverterAttribute"/>
+    /// - Many types have a static `Parse(string)` method which can be used implicitly; this class
+    ///   detects such methods and auto-creates a type converter
+    /// </remarks>
+    public static class AvaloniaTypeConverters
+    {
+        private static Dictionary<Type, Type> _converters = new Dictionary<Type, Type>()
+        {
+            { typeof(AvaloniaList<>), typeof(AvaloniaListConverter<>) },
+            { typeof(AvaloniaProperty), typeof(AvaloniaPropertyTypeConverter) },
+            { typeof(IBitmap), typeof(BitmapTypeConverter) },
+            { typeof(IList<Point>), typeof(PointsListTypeConverter) },
+            { typeof(IMemberSelector), typeof(MemberSelectorTypeConverter) },
+            { typeof(Selector), typeof(SelectorTypeConverter) },
+            { typeof(TimeSpan), typeof(TimeSpanTypeConverter) },
+            { typeof(WindowIcon), typeof(IconTypeConverter) },
+        };
+
+        /// <summary>
+        /// Tries to lookup a <see cref="TypeConverter"/> for a type.
+        /// </summary>
+        /// <param name="type">The type.</param>
+        /// <returns>The type converter.</returns>
+        public static Type GetTypeConverter(Type type)
+        {
+            if (_converters.TryGetValue(type, out var result))
+            {
+                return result;
+            }
+
+            // Converters for non-constructed generic types can't be specified using
+            // TypeConverterAttribute. Allow them to be registered here and handle them sanely.
+            if (type.IsConstructedGenericType &&
+                _converters.TryGetValue(type.GetGenericTypeDefinition(), out result))
+            {
+                return result?.MakeGenericType(type.GetGenericArguments());
+            }
+
+            // If the type has a static Parse method, use that.
+            if (ParseTypeConverter.HasParseMethod(type))
+            {
+                result = typeof(ParseTypeConverter<>).MakeGenericType(type);
+                _converters.Add(type, result);
+                return result;
+            }
+
+            _converters.Add(type, null);
+            return null;
+        }
+
+        /// <summary>
+        /// Registers a type converter for a type.
+        /// </summary>
+        /// <param name="type">The type. Maybe be a non-constructed generic type.</param>
+        /// <param name="converterType">The converter type. Maybe be a non-constructed generic type.</param>
+        public static void Register(Type type, Type converterType) => _converters[type] = converterType;
+    }
+}

+ 0 - 22
src/Markup/Avalonia.Markup.Xaml/Converters/ClassesTypeConverter.cs

@@ -1,22 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Globalization;
-using Avalonia.Controls;
-using System.ComponentModel;
-namespace Avalonia.Markup.Xaml.Converters
-{
-    public class ClassesTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return new Classes(((string)value).Split(' '));
-        }
-    }
-}

+ 0 - 26
src/Markup/Avalonia.Markup.Xaml/Converters/ColorTypeConverter.cs

@@ -1,26 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Globalization;
-using Avalonia.Media;
-using System.ComponentModel;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-	
-
-    public class ColorTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return Color.Parse((string)value);
-        }
-    }
-    
-}

+ 0 - 24
src/Markup/Avalonia.Markup.Xaml/Converters/ColumnDefinitionsTypeConverter.cs

@@ -1,24 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Globalization;
-using Avalonia.Controls;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-	using System.ComponentModel;
-
-    public class ColumnDefinitionsTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return new ColumnDefinitions((string)value);
-        }
-    }
-}

+ 0 - 19
src/Markup/Avalonia.Markup.Xaml/Converters/CornerRadiusTypeConverter.cs

@@ -1,19 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Globalization;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-    public class CornerRadiusTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return CornerRadius.Parse((string)value);
-        }
-    }
-}

+ 0 - 25
src/Markup/Avalonia.Markup.Xaml/Converters/CursorTypeConverter.cs

@@ -1,25 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Globalization;
-using Avalonia.Input;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-	using System.ComponentModel;
-
-    public class CursorTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            var cursor = (StandardCursorType)Enum.Parse(typeof(StandardCursorType), ((string)value).Trim(), true);
-            return new Cursor(cursor);
-        }
-    }
-}

+ 0 - 24
src/Markup/Avalonia.Markup.Xaml/Converters/GeometryTypeConverter.cs

@@ -1,24 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Globalization;
-using Avalonia.Media;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-	using System.ComponentModel;
-
-    public class GeometryTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return StreamGeometry.Parse((string)value);
-        }
-    }
-}

+ 0 - 24
src/Markup/Avalonia.Markup.Xaml/Converters/GridLengthTypeConverter.cs

@@ -1,24 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Globalization;
-using Avalonia.Controls;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-	using System.ComponentModel;
-
-    public class GridLengthTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return GridLength.Parse((string)value);
-        }
-    }
-}

+ 0 - 24
src/Markup/Avalonia.Markup.Xaml/Converters/KeyGestureConverter.cs

@@ -1,24 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Globalization;
-using Avalonia.Input;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-	using System.ComponentModel;
-
-    public class KeyGestureConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return KeyGesture.Parse((string)value);
-        }
-    }
-}

+ 0 - 23
src/Markup/Avalonia.Markup.Xaml/Converters/MatrixTypeConverter.cs

@@ -1,23 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Globalization;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-	using System.ComponentModel;
-
-    public class MatrixTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return Matrix.Parse((string)value);
-        }
-    }
-}

+ 1 - 4
src/Markup/Avalonia.Markup.Xaml/Converters/MemberSelectorTypeConverter.cs

@@ -18,10 +18,7 @@ namespace Avalonia.Markup.Xaml.Converters
 
         public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
         {
-            return new MemberSelector
-            {
-                MemberName = (string)value,
-            };
+            return MemberSelector.Parse((string)value);
         }
     }
 }

+ 66 - 0
src/Markup/Avalonia.Markup.Xaml/Converters/ParseTypeConverter.cs

@@ -0,0 +1,66 @@
+using System;
+using System.ComponentModel;
+using System.Globalization;
+using System.Reflection;
+
+namespace Avalonia.Markup.Xaml.Converters
+{
+    /// <summary>
+    /// Base class for type converters which call a static Parse method.
+    /// </summary>
+    public abstract class ParseTypeConverter : TypeConverter
+    {
+        protected const BindingFlags PublicStatic = BindingFlags.Public | BindingFlags.Static;
+        protected static readonly Type[] StringParameter = new[] { typeof(string) };
+        protected static readonly Type[] StringIFormatProviderParameters = new[] { typeof(string), typeof(IFormatProvider) };
+
+        /// <summary>
+        /// Checks whether a type has a suitable Parse method.
+        /// </summary>
+        /// <param name="type">The type.</param>
+        /// <returns>True if the type has a suitable parse method, otherwise false.</returns>
+        public static bool HasParseMethod(Type type)
+        {
+            return type.GetMethod("Parse", PublicStatic, null, StringIFormatProviderParameters, null) != null ||
+                   type.GetMethod("Parse", PublicStatic, null, StringParameter, null) != null;
+        }
+    }
+
+    /// <summary>
+    /// A type converter which calls a static Parse method.
+    /// </summary>
+    /// <typeparam name="T">The type with the Parse method.</typeparam>
+    public class ParseTypeConverter<T> : ParseTypeConverter
+    {
+        private static MethodInfo _parseMethod;
+        private static bool _acceptsCulture;
+
+        static ParseTypeConverter()
+        {
+            _parseMethod = typeof(T).GetMethod("Parse", PublicStatic, null, StringIFormatProviderParameters, null);
+            _acceptsCulture = _parseMethod != null;
+
+            if (_parseMethod == null)
+            {
+                _parseMethod = typeof(T).GetMethod("Parse", PublicStatic, null, StringParameter, null);
+            }
+        }
+
+        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+        {
+            return _parseMethod != null && sourceType == typeof(string);
+        }
+
+        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+        {
+            if (value != null && _parseMethod != null)
+            {
+                return _acceptsCulture ?
+                    _parseMethod.Invoke(null, new[] { value, culture }) :
+                    _parseMethod.Invoke(null, new[] { value });
+            }
+
+            return null;
+        }
+    }
+}

+ 0 - 23
src/Markup/Avalonia.Markup.Xaml/Converters/PointTypeConverter.cs

@@ -1,23 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Globalization;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-	using System.ComponentModel;
-
-    public class PointTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return Point.Parse((string)value);
-        }
-    }
-}

+ 0 - 23
src/Markup/Avalonia.Markup.Xaml/Converters/RectTypeConverter.cs

@@ -1,23 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Globalization;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-	using System.ComponentModel;
-
-    public class RectTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return Rect.Parse((string)value);
-        }
-    }
-}

+ 0 - 23
src/Markup/Avalonia.Markup.Xaml/Converters/RelativePointTypeConverter.cs

@@ -1,23 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Globalization;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-	using System.ComponentModel;
-
-    public class RelativePointTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return RelativePoint.Parse((string)value);
-        }
-    }
-}

+ 0 - 23
src/Markup/Avalonia.Markup.Xaml/Converters/RelativeRectTypeConverter.cs

@@ -1,23 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Globalization;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-	using System.ComponentModel;
-
-    public class RelativeRectTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return RelativeRect.Parse((string)value);
-        }
-    }
-}

+ 0 - 24
src/Markup/Avalonia.Markup.Xaml/Converters/RowDefinitionsTypeConverter.cs

@@ -1,24 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Globalization;
-using Avalonia.Controls;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-	using System.ComponentModel;
-
-    public class RowDefinitionsTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return new RowDefinitions((string)value);
-        }
-    }
-}

+ 0 - 23
src/Markup/Avalonia.Markup.Xaml/Converters/SizeTypeConverter.cs

@@ -1,23 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Globalization;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-	using System.ComponentModel;
-
-    public class SizeTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return Size.Parse((string)value);
-        }
-    }
-}

+ 0 - 23
src/Markup/Avalonia.Markup.Xaml/Converters/ThicknessTypeConverter.cs

@@ -1,23 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Globalization;
-
-namespace Avalonia.Markup.Xaml.Converters
-{
-	using System.ComponentModel;
-
-    public class ThicknessTypeConverter : TypeConverter
-    {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return sourceType == typeof(string);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            return Thickness.Parse((string)value);
-        }
-    }
-}

+ 0 - 89
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs

@@ -1,89 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Reflection;
-using Avalonia.Markup.Xaml.Converters;
-using Avalonia.Media;
-using Avalonia.Media.Imaging;
-using Avalonia.Styling;
-using Portable.Xaml.ComponentModel;
-using System.ComponentModel;
-using Avalonia.Controls;
-using Avalonia.Input;
-using Avalonia.Collections;
-using Avalonia.Controls.Templates;
-
-namespace Avalonia.Markup.Xaml.PortableXaml
-{
-    public class AvaloniaDefaultTypeConverters
-    {
-        private static Dictionary<Type, Type> _defaultConverters = new Dictionary<Type, Type>()
-        {
-            //avalonia default converters
-            { typeof(IBitmap), typeof(BitmapTypeConverter)},
-            { typeof(IBrush), typeof(BrushTypeConverter) },
-            { typeof(Color), typeof(ColorTypeConverter) },
-            { typeof(Classes), typeof(ClassesTypeConverter) },
-            { typeof(ColumnDefinitions), typeof(ColumnDefinitionsTypeConverter) },
-            //{ typeof(DateTime), typeof(DateTimeTypeConverter) },
-            { typeof(Geometry), typeof(GeometryTypeConverter) },
-            { typeof(GridLength), typeof(GridLengthTypeConverter) },
-            { typeof(KeyGesture), typeof(KeyGestureConverter) },
-            { typeof(AvaloniaList<double>), typeof(AvaloniaListTypeConverter<double>) },
-            { typeof(IMemberSelector), typeof(MemberSelectorTypeConverter) },
-            { typeof(Point), typeof(PointTypeConverter) },
-            { typeof(Matrix), typeof(MatrixTypeConverter) },
-            { typeof(IList<Point>), typeof(PointsListTypeConverter) },
-            { typeof(AvaloniaProperty), typeof(AvaloniaPropertyTypeConverter) },
-            { typeof(RelativePoint), typeof(RelativePointTypeConverter) },
-            { typeof(RelativeRect), typeof(RelativeRectTypeConverter) },
-            { typeof(RowDefinitions), typeof(RowDefinitionsTypeConverter) },
-            { typeof(Size), typeof(SizeTypeConverter) },
-            { typeof(Rect), typeof(RectTypeConverter) },
-            { typeof(Selector), typeof(SelectorTypeConverter)},
-            { typeof(SolidColorBrush), typeof(BrushTypeConverter) },
-            { typeof(Thickness), typeof(ThicknessTypeConverter) },
-            { typeof(CornerRadius), typeof(CornerRadiusTypeConverter) },
-            { typeof(TimeSpan), typeof(TimeSpanTypeConverter) },
-            //{ typeof(Uri), typeof(Converters.UriTypeConverter) },
-            { typeof(Cursor), typeof(CursorTypeConverter) },
-            { typeof(WindowIcon), typeof(IconTypeConverter) },
-            //{ typeof(FontWeight), typeof(FontWeightConverter) },
-        };
-
-        public static Type GetTypeConverter(Type type)
-        {
-            Type converterType;
-
-            if (_defaultConverters.TryGetValue(type, out converterType))
-            {
-                return converterType;
-            }
-
-            //TODO: probably a smarter way to handle types
-            //is it needed ??
-            //Type curType = type;
-
-            //while (curType != null)
-            //{
-            //    if (_defaultConverters.TryGetValue(curType, out converterType))
-            //    {
-            //        if (curType != type)
-            //        {
-            //            _defaultConverters[curType] = converterType;
-            //        }
-            //        return converterType;
-            //    }
-            //    else
-            //    {
-            //        _defaultConverters[curType] = null;
-            //    }
-            //    curType = curType.GetTypeInfo().BaseType;
-            //}
-
-
-
-            return null;
-        }
-    }
-}

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaTypeAttributeProvider.cs

@@ -46,7 +46,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml
 
                 if (result == null)
                 {
-                    var convType = AvaloniaDefaultTypeConverters.GetTypeConverter(_type);
+                    var convType = AvaloniaTypeConverters.GetTypeConverter(_type);
 
                     if (convType != null)
                     {

+ 5 - 0
src/Markup/Avalonia.Markup.Xaml/Templates/MemberSelector.cs

@@ -25,6 +25,11 @@ namespace Avalonia.Markup.Xaml.Templates
             }
         }
 
+        public static MemberSelector Parse(string s)
+        {
+            return new MemberSelector { MemberName = s };
+        }
+
         public object Select(object o)
         {
             if (string.IsNullOrEmpty(MemberName))

+ 1 - 0
tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.Specialized;
+using System.ComponentModel;
 using System.Linq;
 using Avalonia.Collections;
 using Xunit;