From 7588b160440d63c52b9abf4c9f1d63f09e396098 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 6 May 2018 20:07:18 +0200 Subject: [PATCH] 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` 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 --- .../Collections/AvaloniaListConverter.cs} | 18 ++-- src/Avalonia.Controls/Classes.cs | 7 ++ src/Avalonia.Controls/ColumnDefinitions.cs | 8 +- src/Avalonia.Controls/RowDefinitions.cs | 8 +- src/Avalonia.Input/Cursors.cs | 7 ++ src/Avalonia.Visuals/Media/Brush.cs | 4 +- .../Media/BrushConverter.cs} | 13 ++- src/Avalonia.Visuals/Media/Geometry.cs | 7 ++ src/Avalonia.Visuals/Media/IBrush.cs | 3 + src/Avalonia.Visuals/Media/SolidColorBrush.cs | 15 ++++ .../Avalonia.Markup.Xaml.csproj | 25 +----- .../AvaloniaTypeConverters.cs | 80 +++++++++++++++++ .../Converters/ClassesTypeConverter.cs | 22 ----- .../Converters/ColorTypeConverter.cs | 26 ------ .../ColumnDefinitionsTypeConverter.cs | 24 ----- .../Converters/CornerRadiusTypeConverter.cs | 19 ---- .../Converters/CursorTypeConverter.cs | 25 ------ .../Converters/GeometryTypeConverter.cs | 24 ----- .../Converters/GridLengthTypeConverter.cs | 24 ----- .../Converters/KeyGestureConverter.cs | 24 ----- .../Converters/MatrixTypeConverter.cs | 23 ----- .../Converters/MemberSelectorTypeConverter.cs | 5 +- .../Converters/ParseTypeConverter.cs | 66 ++++++++++++++ .../Converters/PointTypeConverter.cs | 23 ----- .../Converters/RectTypeConverter.cs | 23 ----- .../Converters/RelativePointTypeConverter.cs | 23 ----- .../Converters/RelativeRectTypeConverter.cs | 23 ----- .../Converters/RowDefinitionsTypeConverter.cs | 24 ----- .../Converters/SizeTypeConverter.cs | 23 ----- .../Converters/ThicknessTypeConverter.cs | 23 ----- .../AvaloniaDefaultTypeConverters.cs | 89 ------------------- .../AvaloniaTypeAttributeProvider.cs | 2 +- .../Templates/MemberSelector.cs | 5 ++ .../Collections/AvaloniaListTests.cs | 1 + 34 files changed, 228 insertions(+), 508 deletions(-) rename src/{Markup/Avalonia.Markup.Xaml/Converters/AvaloniaListTypeConverter.cs => Avalonia.Base/Collections/AvaloniaListConverter.cs} (77%) rename src/{Markup/Avalonia.Markup.Xaml/Converters/BrushTypeConverter.cs => Avalonia.Visuals/Media/BrushConverter.cs} (72%) create mode 100644 src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/ClassesTypeConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/ColorTypeConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/ColumnDefinitionsTypeConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/CornerRadiusTypeConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/CursorTypeConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/GeometryTypeConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/GridLengthTypeConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/KeyGestureConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/MatrixTypeConverter.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/ParseTypeConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/PointTypeConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/RectTypeConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/RelativePointTypeConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/RelativeRectTypeConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/RowDefinitionsTypeConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/SizeTypeConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/Converters/ThicknessTypeConverter.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaListTypeConverter.cs b/src/Avalonia.Base/Collections/AvaloniaListConverter.cs similarity index 77% rename from src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaListTypeConverter.cs rename to src/Avalonia.Base/Collections/AvaloniaListConverter.cs index 915cac163d..7f5442857c 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaListTypeConverter.cs +++ b/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 : TypeConverter + /// + /// Creates an from a string representation. + /// + public class AvaloniaListConverter : 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(); + + // 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); } diff --git a/src/Avalonia.Controls/Classes.cs b/src/Avalonia.Controls/Classes.cs index 613273d5c7..48ce69f0c9 100644 --- a/src/Avalonia.Controls/Classes.cs +++ b/src/Avalonia.Controls/Classes.cs @@ -41,6 +41,13 @@ namespace Avalonia.Controls { } + /// + /// Parses a classes string. + /// + /// The string. + /// The . + public static Classes Parse(string s) => new Classes(s.Split(' ')); + /// /// Adds a style class to the collection. /// diff --git a/src/Avalonia.Controls/ColumnDefinitions.cs b/src/Avalonia.Controls/ColumnDefinitions.cs index 972655262a..ecfe6027ac 100644 --- a/src/Avalonia.Controls/ColumnDefinitions.cs +++ b/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))); } + + /// + /// Parses a string representation of column definitions collection. + /// + /// The column definitions string. + /// The . + public static ColumnDefinitions Parse(string s) => new ColumnDefinitions(s); } } \ No newline at end of file diff --git a/src/Avalonia.Controls/RowDefinitions.cs b/src/Avalonia.Controls/RowDefinitions.cs index 2dfad7111a..1a14cc78f3 100644 --- a/src/Avalonia.Controls/RowDefinitions.cs +++ b/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))); } + + /// + /// Parses a string representation of row definitions collection. + /// + /// The row definitions string. + /// The . + public static RowDefinitions Parse(string s) => new RowDefinitions(s); } } \ No newline at end of file diff --git a/src/Avalonia.Input/Cursors.cs b/src/Avalonia.Input/Cursors.cs index 02a026c998..f38f653503 100644 --- a/src/Avalonia.Input/Cursors.cs +++ b/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(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(); diff --git a/src/Avalonia.Visuals/Media/Brush.cs b/src/Avalonia.Visuals/Media/Brush.cs index d6b0e43cdc..eef6e1a43c 100644 --- a/src/Avalonia.Visuals/Media/Brush.cs +++ b/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 { /// /// Describes how an area is painted. /// + [TypeConverter(typeof(BrushConverter))] public abstract class Brush : AvaloniaObject, IBrush { /// diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/BrushTypeConverter.cs b/src/Avalonia.Visuals/Media/BrushConverter.cs similarity index 72% rename from src/Markup/Avalonia.Markup.Xaml/Converters/BrushTypeConverter.cs rename to src/Avalonia.Visuals/Media/BrushConverter.cs index e82d93b629..c79360204a 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/BrushTypeConverter.cs +++ b/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 + /// + /// Creates an from a string representation. + /// + public class BrushConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { diff --git a/src/Avalonia.Visuals/Media/Geometry.cs b/src/Avalonia.Visuals/Media/Geometry.cs index 17c0d2ab74..748d2526af 100644 --- a/src/Avalonia.Visuals/Media/Geometry.cs +++ b/src/Avalonia.Visuals/Media/Geometry.cs @@ -69,6 +69,13 @@ namespace Avalonia.Media set { SetValue(TransformProperty, value); } } + /// + /// Creates a from a string. + /// + /// The string. + /// A . + public static Geometry Parse(string s) => StreamGeometry.Parse(s); + /// /// Clones the geometry. /// diff --git a/src/Avalonia.Visuals/Media/IBrush.cs b/src/Avalonia.Visuals/Media/IBrush.cs index 52b4d959cd..bf096076fd 100644 --- a/src/Avalonia.Visuals/Media/IBrush.cs +++ b/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 { /// /// Describes how an area is painted. /// + [TypeConverter(typeof(BrushConverter))] public interface IBrush { /// diff --git a/src/Avalonia.Visuals/Media/SolidColorBrush.cs b/src/Avalonia.Visuals/Media/SolidColorBrush.cs index 6dfee555c9..d090f63069 100644 --- a/src/Avalonia.Visuals/Media/SolidColorBrush.cs +++ b/src/Avalonia.Visuals/Media/SolidColorBrush.cs @@ -52,6 +52,21 @@ namespace Avalonia.Media set { SetValue(ColorProperty, value); } } + /// + /// Parses a brush string. + /// + /// The brush string. + /// The . + /// + /// Whereas may return an immutable solid color brush, + /// this method always returns a mutable . + /// + public static new SolidColorBrush Parse(string s) + { + var brush = (ISolidColorBrush)Brush.Parse(s); + return brush is SolidColorBrush solid ? solid : new SolidColorBrush(brush.Color); + } + /// /// Returns a string representation of the brush. /// diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index e388a49a6e..64fc097b27 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -30,10 +30,10 @@ Properties\SharedAssemblyInfo.cs - - - + + + @@ -42,32 +42,15 @@ - + - - - - - - - - - - - - - - - - - diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs new file mode 100644 index 0000000000..2a3dde07f7 --- /dev/null +++ b/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 +{ + /// + /// Maintains a repository of s for XAML parsing on top of those + /// maintained by . + /// + /// + /// The default method of defining type converters using + /// isn't powerful enough for our purposes: + /// + /// - It doesn't handle non-constructed generic types (such as ) + /// - Type converters which require XAML features cannot be defined in non-XAML assemblies and + /// so can't be referenced using + /// - Many types have a static `Parse(string)` method which can be used implicitly; this class + /// detects such methods and auto-creates a type converter + /// + public static class AvaloniaTypeConverters + { + private static Dictionary _converters = new Dictionary() + { + { typeof(AvaloniaList<>), typeof(AvaloniaListConverter<>) }, + { typeof(AvaloniaProperty), typeof(AvaloniaPropertyTypeConverter) }, + { typeof(IBitmap), typeof(BitmapTypeConverter) }, + { typeof(IList), typeof(PointsListTypeConverter) }, + { typeof(IMemberSelector), typeof(MemberSelectorTypeConverter) }, + { typeof(Selector), typeof(SelectorTypeConverter) }, + { typeof(TimeSpan), typeof(TimeSpanTypeConverter) }, + { typeof(WindowIcon), typeof(IconTypeConverter) }, + }; + + /// + /// Tries to lookup a for a type. + /// + /// The type. + /// The type converter. + 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; + } + + /// + /// Registers a type converter for a type. + /// + /// The type. Maybe be a non-constructed generic type. + /// The converter type. Maybe be a non-constructed generic type. + public static void Register(Type type, Type converterType) => _converters[type] = converterType; + } +} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/ClassesTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/ClassesTypeConverter.cs deleted file mode 100644 index 8968807e4a..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/ClassesTypeConverter.cs +++ /dev/null @@ -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(' ')); - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/ColorTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/ColorTypeConverter.cs deleted file mode 100644 index a6a19eb9ed..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/ColorTypeConverter.cs +++ /dev/null @@ -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); - } - } - -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/ColumnDefinitionsTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/ColumnDefinitionsTypeConverter.cs deleted file mode 100644 index b0db691a8d..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/ColumnDefinitionsTypeConverter.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/CornerRadiusTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/CornerRadiusTypeConverter.cs deleted file mode 100644 index d8c1ecd4eb..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/CornerRadiusTypeConverter.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/CursorTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/CursorTypeConverter.cs deleted file mode 100644 index 9639cfc1bd..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/CursorTypeConverter.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/GeometryTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/GeometryTypeConverter.cs deleted file mode 100644 index d7ebc084ae..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/GeometryTypeConverter.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/GridLengthTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/GridLengthTypeConverter.cs deleted file mode 100644 index 05f3bed04d..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/GridLengthTypeConverter.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/KeyGestureConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/KeyGestureConverter.cs deleted file mode 100644 index 5b11ab0389..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/KeyGestureConverter.cs +++ /dev/null @@ -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); - } - } -} diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/MatrixTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/MatrixTypeConverter.cs deleted file mode 100644 index fec6e31771..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/MatrixTypeConverter.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/MemberSelectorTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/MemberSelectorTypeConverter.cs index fb369610d1..8dc052fe63 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/MemberSelectorTypeConverter.cs +++ b/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); } } } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/ParseTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/ParseTypeConverter.cs new file mode 100644 index 0000000000..3fb121d2e0 --- /dev/null +++ b/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 +{ + /// + /// Base class for type converters which call a static Parse method. + /// + 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) }; + + /// + /// Checks whether a type has a suitable Parse method. + /// + /// The type. + /// True if the type has a suitable parse method, otherwise false. + public static bool HasParseMethod(Type type) + { + return type.GetMethod("Parse", PublicStatic, null, StringIFormatProviderParameters, null) != null || + type.GetMethod("Parse", PublicStatic, null, StringParameter, null) != null; + } + } + + /// + /// A type converter which calls a static Parse method. + /// + /// The type with the Parse method. + public class ParseTypeConverter : 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; + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/PointTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/PointTypeConverter.cs deleted file mode 100644 index 9ca7212c68..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/PointTypeConverter.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/RectTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/RectTypeConverter.cs deleted file mode 100644 index 0946f95938..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/RectTypeConverter.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/RelativePointTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/RelativePointTypeConverter.cs deleted file mode 100644 index b3178f1496..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/RelativePointTypeConverter.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/RelativeRectTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/RelativeRectTypeConverter.cs deleted file mode 100644 index 38c2833815..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/RelativeRectTypeConverter.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/RowDefinitionsTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/RowDefinitionsTypeConverter.cs deleted file mode 100644 index 64f3a7a04d..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/RowDefinitionsTypeConverter.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/SizeTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/SizeTypeConverter.cs deleted file mode 100644 index 535e7948d5..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/SizeTypeConverter.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/ThicknessTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/ThicknessTypeConverter.cs deleted file mode 100644 index 3a7652a153..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/ThicknessTypeConverter.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs deleted file mode 100644 index 1ae24c8a34..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs +++ /dev/null @@ -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 _defaultConverters = new Dictionary() - { - //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), typeof(AvaloniaListTypeConverter) }, - { typeof(IMemberSelector), typeof(MemberSelectorTypeConverter) }, - { typeof(Point), typeof(PointTypeConverter) }, - { typeof(Matrix), typeof(MatrixTypeConverter) }, - { typeof(IList), 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; - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaTypeAttributeProvider.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaTypeAttributeProvider.cs index 6e23459cdf..83f4919d4b 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaTypeAttributeProvider.cs +++ b/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) { diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/MemberSelector.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/MemberSelector.cs index 5e412633d2..1581765132 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/MemberSelector.cs +++ b/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)) diff --git a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs index fd731455d8..8a38a00493 100644 --- a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs +++ b/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;