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/Avalonia.Visuals/Media/StreamGeometry.cs b/src/Avalonia.Visuals/Media/StreamGeometry.cs index 9c29c62bad..9848a649aa 100644 --- a/src/Avalonia.Visuals/Media/StreamGeometry.cs +++ b/src/Avalonia.Visuals/Media/StreamGeometry.cs @@ -33,7 +33,7 @@ namespace Avalonia.Media /// /// The string. /// A . - public static StreamGeometry Parse(string s) + public static new StreamGeometry Parse(string s) { StreamGeometry result = new StreamGeometry(); diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 1bf5fb9608..8bfe8c25bd 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -26,11 +26,10 @@ - - - - + + + @@ -39,32 +38,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..36c03be65a --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs @@ -0,0 +1,84 @@ +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 isn't a primitive or a type that XAML already handles, but has a static + // Parse method, use that + if (!type.IsPrimitive && + type != typeof(DateTime) && + type != typeof(Uri) && + 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/FontFamilyTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs deleted file mode 100644 index 9d03db5fa8..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.ComponentModel; -using System.Globalization; -using Avalonia.Media; - -namespace Avalonia.Markup.Xaml.Converters -{ - public class FontFamilyTypeConverter : TypeConverter - { - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - return sourceType == typeof(string); - } - - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) - { - return FontFamily.Parse((string)value); - } - } -} \ 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..bfb446fa15 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/ParseTypeConverter.cs @@ -0,0 +1,79 @@ +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 Func _parse; + private static Func _parseWithFormat; + + static ParseTypeConverter() + { + var method = typeof(T).GetMethod("Parse", PublicStatic, null, StringIFormatProviderParameters, null); + + if (method != null) + { + _parseWithFormat = (Func)method + .CreateDelegate(typeof(Func)); + return; + } + + method = typeof(T).GetMethod("Parse", PublicStatic, null, StringParameter, null); + + if (method != null) + { + _parse = (Func)method.CreateDelegate(typeof(Func)); + } + } + + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value != null) + { + if (_parse != null) + { + return _parse(value.ToString()); + } + else if (_parseWithFormat != null) + { + return _parseWithFormat(value.ToString(), culture); + } + } + + 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/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; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs index 4213a31c39..0fcb9432ab 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs @@ -530,7 +530,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml style.TryGetResource("Brush", out var brush); Assert.NotNull(brush); - + Assert.IsType(brush); Assert.Equal(Colors.White, ((ISolidColorBrush)brush).Color); style.TryGetResource("Double", out var d); diff --git a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs index 19716431b9..e65c78e285 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs @@ -11,7 +11,7 @@ namespace Avalonia.Visuals.UnitTests.Media.Fonts { using System.Diagnostics; - public class FontFamilyLoaderTests + public class FontFamilyLoaderTests : IDisposable { private const string FontName = "#MyFont"; private const string Assembly = "?assembly=Avalonia.Visuals.UnitTests"; @@ -35,7 +35,7 @@ namespace Avalonia.Visuals.UnitTests.Media.Fonts _testApplication = StartWithResources(fontAssets); } - ~FontFamilyLoaderTests() + public void Dispose() { _testApplication.Dispose(); }