From e71d9fc93f60992fa1c85485421b0084ad6a23ee Mon Sep 17 00:00:00 2001 From: donandren Date: Thu, 2 Mar 2017 03:37:38 +0200 Subject: [PATCH] styles working --- .../Avalonia.Markup.Xaml.csproj | 3 + .../Context/AvaloniaRuntimeTypeSource.cs | 2 + .../Context/AvaloniaTypeFeatureProvider.cs | 2 + .../AvaloniaPropertyTypeConverter.cs | 94 ++++++++++++++++++- .../Converters/BrushTypeConverter.cs | 24 ++++- .../Converters/SelectorTypeConverter.cs | 26 ++++- .../AvaloniaDefaultTypeConverters.cs | 71 ++++++++++++++ .../AvaloniaTypeAttributeProvider.cs | 19 ++++ .../PortableXaml/AvaloniaXamlSchemaContext.cs | 13 ++- .../PortableXaml/AvaloniaXamlType.cs | 94 +++++++++++++++++++ .../PortableXaml/TypeDescriptorExtensions.cs | 37 ++++++++ 11 files changed, 378 insertions(+), 7 deletions(-) create mode 100644 src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 2f33fb912d..6cc354bc00 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -44,6 +44,7 @@ + @@ -274,6 +275,8 @@ + + diff --git a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaRuntimeTypeSource.cs b/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaRuntimeTypeSource.cs index 762ffdb24b..dfc8fbceee 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaRuntimeTypeSource.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaRuntimeTypeSource.cs @@ -1,5 +1,6 @@ namespace Avalonia.Markup.Xaml.Context { +#if OMNIXAML using System; using System.Collections.Generic; using System.Linq; @@ -88,4 +89,5 @@ namespace Avalonia.Markup.Xaml.Context return inner.GetAttachableMember(name, getter, setter); } } +#endif } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaTypeFeatureProvider.cs b/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaTypeFeatureProvider.cs index 7828934a62..d03fd2e72c 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaTypeFeatureProvider.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaTypeFeatureProvider.cs @@ -23,6 +23,7 @@ using OmniMetadata = OmniXaml.Typing.Metadata; namespace Avalonia.Markup.Xaml.Context { +#if OMNIXAML public class AvaloniaTypeFeatureProvider : ITypeFeatureProvider { private Dictionary _metadata = @@ -177,4 +178,5 @@ namespace Avalonia.Markup.Xaml.Context RegisterTypeConverter(typeof(FontWeight), new FontWeightConverter()); } } +#endif } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs index 026217eacb..ae9339b299 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs @@ -4,12 +4,99 @@ using System; using System.Globalization; using System.Linq; -using OmniXaml; -using OmniXaml.TypeConversion; -using Avalonia.Styling; namespace Avalonia.Markup.Xaml.Converters { + using Avalonia.Styling; + using Portable.Xaml; +#if !OMNIXAML + + using Portable.Xaml.ComponentModel; + using Portable.Xaml.Markup; + + public class AvaloniaPropertyTypeConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + var s = (string)value; + + string typeName; + string propertyName; + Type type = null; + + ParseProperty(s, out typeName, out propertyName); + + if (typeName == null) + { + var amb = context.GetService(); + var sc = context.GetService().SchemaContext; + var xamlStyleType = sc.GetXamlType(typeof(Style)); + var ambValue = amb.GetFirstAmbientValue(xamlStyleType) as AmbientPropertyValue; + + var style = ambValue?.Value as Style; + type = style.Selector?.TargetType; + + if (type == null) + { + throw new Exception( + "Could not determine the target type. Please fully qualify the property name."); + } + } + else + { + //type = context.TypeRepository.GetByQualifiedName(typeName)?.UnderlyingType; + + if (type == null) + { + throw new Exception($"Could not find type '{typeName}'."); + } + } + + // First look for non-attached property on the type and then look for an attached property. + var property = AvaloniaPropertyRegistry.Instance.FindRegistered(type, s) ?? + AvaloniaPropertyRegistry.Instance.GetAttached(type) + .FirstOrDefault(x => x.Name == propertyName); + + if (property == null) + { + throw new Exception( + $"Could not find AvaloniaProperty '{type.Name}.{propertyName}'."); + } + + return property; + } + + private void ParseProperty(string s, out string typeName, out string propertyName) + { + var split = s.Split('.'); + + if (split.Length == 1) + { + typeName = null; + propertyName = split[0]; + } + else if (split.Length == 2) + { + typeName = split[0]; + propertyName = split[1]; + } + else + { + throw new Exception($"Invalid property name: '{s}'."); + } + } + } + +#else + + using OmniXaml; + using OmniXaml.TypeConversion; + public class AvaloniaPropertyTypeConverter : ITypeConverter { public bool CanConvertFrom(IValueContext context, Type sourceType) @@ -93,4 +180,5 @@ namespace Avalonia.Markup.Xaml.Converters } } } +#endif } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/BrushTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/BrushTypeConverter.cs index 45f835914f..0c256cf645 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/BrushTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/BrushTypeConverter.cs @@ -3,11 +3,32 @@ using System; using System.Globalization; -using OmniXaml.TypeConversion; + using Avalonia.Media; namespace Avalonia.Markup.Xaml.Converters { +#if !OMNIXAML + + using Portable.Xaml.ComponentModel; + + public class BrushTypeConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + return Brush.Parse((string)value); + } + } + +#else + + using OmniXaml.TypeConversion; + public class BrushTypeConverter : ITypeConverter { public bool CanConvertFrom(IValueContext context, Type sourceType) @@ -30,4 +51,5 @@ namespace Avalonia.Markup.Xaml.Converters throw new NotImplementedException(); } } +#endif } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/SelectorTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/SelectorTypeConverter.cs index 0970f42724..391663ed43 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/SelectorTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/SelectorTypeConverter.cs @@ -3,11 +3,33 @@ using System; using System.Globalization; -using OmniXaml.TypeConversion; using Avalonia.Markup.Xaml.Parsers; namespace Avalonia.Markup.Xaml.Converters { +#if !OMNIXAML + + using Portable.Xaml.ComponentModel; + + public class SelectorTypeConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + var parser = new SelectorParser((t, ns) => context.ResolveType(ns, t)); + + return parser.Parse((string)value); + } + } + +#else + + using OmniXaml.TypeConversion; + public class SelectorTypeConverter : ITypeConverter { public bool CanConvertFrom(IValueContext context, Type sourceType) @@ -31,4 +53,6 @@ namespace Avalonia.Markup.Xaml.Converters throw new NotImplementedException(); } } + +#endif } \ 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 new file mode 100644 index 0000000000..e92b84b041 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Avalonia.Markup.Xaml.Converters; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Styling; + +namespace Avalonia.Markup.Xaml.PortableXaml +{ + public class AvaloniaDefaultTypeConverters + { + private static Dictionary _defaultConverters = new Dictionary() + { + { 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(IList), typeof(PointsListTypeConverter) }, + { typeof(AvaloniaProperty), typeof(AvaloniaPropertyTypeConverter) }, + //{ typeof(RelativePoint), typeof(RelativePointTypeConverter) }, + //{ typeof(RelativeRect), typeof(RelativeRectTypeConverter) }, + //{ typeof(RowDefinitions), typeof(RowDefinitionsTypeConverter) }, + //{ typeof(Size), typeof(SizeTypeConverter) }, + { typeof(Selector), typeof(SelectorTypeConverter)}, + //{ typeof(SolidColorBrush), typeof(BrushTypeConverter) }, + //{ typeof(Thickness), typeof(ThicknessTypeConverter) }, + //{ 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; + + 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 ba470d521d..1604fadf4b 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaTypeAttributeProvider.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaTypeAttributeProvider.cs @@ -43,6 +43,25 @@ namespace Avalonia.Markup.Xaml.PortableXaml }; } } + else if (attributeType == typeof(TypeConverterAttribute)) + { + var attribs = _type.GetTypeInfo().GetCustomAttributes(attributeType, inherit); + + if (attribs?.Any() != true) + { + var convType = AvaloniaDefaultTypeConverters.GetTypeConverter(_type); + + if (convType != null) + { + return new object[] + { + new TypeConverterAttribute(convType) + }; + } + } + + return (attribs as object[]) ?? attribs.ToArray(); + } var attr = _type.GetTypeInfo().GetCustomAttributes(attributeType, inherit); return (attr as object[]) ?? attr.ToArray(); diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs index 75ca5dff37..818aa2e43b 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Reflection; using Avalonia.Markup.Xaml.Context; @@ -30,7 +29,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml type = base.GetXamlType(xamlNamespace, name, typeArguments); } } - catch(Exception e) + catch (Exception e) { //TODO: log or wrap exception throw e; @@ -66,5 +65,15 @@ namespace Avalonia.Markup.Xaml.PortableXaml protected override ICustomAttributeProvider GetCustomAttributeProvider(MemberInfo member) => new AvaloniaMemberAttributeProvider(member); + + public override XamlType GetXamlType(Type type) + { + if (type.FullName.StartsWith("Avalonia.")) + { + return new AvaloniaXamlType(type, this); + } + + return base.GetXamlType(type); + } } } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs new file mode 100644 index 0000000000..ee1acbefa7 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Portable.Xaml; +using Portable.Xaml.Schema; +using am = Avalonia.Metadata; + +namespace Avalonia.Markup.Xaml.PortableXaml +{ + public class AvaloniaXamlType : XamlType + { + public AvaloniaXamlType(Type underlyingType, XamlSchemaContext schemaContext) : + base(underlyingType, schemaContext) + { + } + + protected override XamlMember LookupMember(string name, bool skipReadOnlyCheck) + { + var pi = UnderlyingType.GetRuntimeProperty(name); + + var dependAttr = pi.GetCustomAttribute(); + + if (dependAttr != null) + { + return new DependOnXamlMember(dependAttr.Name, pi, SchemaContext); + } + + return base.LookupMember(name, skipReadOnlyCheck); + } + } + + public class DependOnXamlMember : XamlMember + { + private string _dependOn; + + public DependOnXamlMember(string dependOn, + PropertyInfo propertyInfo, + XamlSchemaContext schemaContext) : + base(propertyInfo, schemaContext) + { + _dependOn = dependOn; + } + + private XamlMember _dependOnMember; + + public XamlMember DependOnMember + { + get + { + return _dependOnMember ?? + (_dependOnMember = DeclaringType.GetMember(_dependOn)); + } + } + + protected override IList LookupDependsOn() + { + return new List() { DeclaringType.GetMember(_dependOn) }; + } + + protected override XamlMemberInvoker LookupInvoker() + { + return new DependOnInvoker(this); + } + + private class DependOnInvoker : XamlMemberInvoker + { + public DependOnInvoker(XamlMember member) : base(member) + { + } + + public override void SetValue(object instance, object value) + { + if (value is string && + (Member.UnderlyingMember as PropertyInfo).PropertyType != typeof(string)) + { + var dpm = (Member as DependOnXamlMember).DependOnMember.UnderlyingMember; + var pi = (dpm as PropertyInfo); + var avp = pi.GetValue(instance) as AvaloniaProperty; + + Type targetType = avp != null ? avp.PropertyType : pi.PropertyType; + + var xamTargetType = Member.DeclaringType.SchemaContext.GetXamlType(targetType); + var ttConv = xamTargetType?.TypeConverter?.ConverterInstance; + if (ttConv != null) + { + value = ttConv.ConvertFromString(value as string); + } + } + + base.SetValue(instance, value); + } + } + } +} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs new file mode 100644 index 0000000000..169befeb84 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs @@ -0,0 +1,37 @@ +using System; +using Portable.Xaml.Markup; + +namespace Portable.Xaml.ComponentModel +{ + internal static class TypeDescriptorExtensions + { + /// + /// Gets the service from ITypeDescriptorContext + /// usually in TypeConverter in xaml reader context + /// examples: + /// context.GetService() + /// context.GetService() + /// context.GetService() + /// context.GetService() + /// context.GetService() + /// context.GetService() + /// context.GetService() + /// + /// Service Type + /// The TypeDescriptor context. + /// + public static T GetService(this ITypeDescriptorContext ctx) where T : class + { + return ctx.GetService(typeof(T)) as T; + } + + public static Type ResolveType(this ITypeDescriptorContext ctx, string namespacePrefix, string type) + { + var tr = ctx.GetService(); + + string name = string.IsNullOrEmpty(namespacePrefix) ? type : $"{namespacePrefix}:{type}"; + + return tr?.Resolve(name); + } + } +} \ No newline at end of file