committed by
GitHub
47 changed files with 118 additions and 2415 deletions
@ -1,107 +0,0 @@ |
|||||
using System; |
|
||||
using System.ComponentModel; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Globalization; |
|
||||
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 |
|
||||
{ |
|
||||
using System.Reflection; |
|
||||
using Avalonia.Media; |
|
||||
|
|
||||
/// <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 |
|
||||
{ |
|
||||
// When adding item to that list make sure to modify AvaloniaXamlIlLanguage
|
|
||||
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) }, |
|
||||
{ typeof(CultureInfo), typeof(CultureInfoConverter) }, |
|
||||
{ typeof(Uri), typeof(AvaloniaUriTypeConverter) }, |
|
||||
{ typeof(FontFamily), typeof(FontFamilyTypeConverter) }, |
|
||||
{ typeof(EventInfo), typeof(AvaloniaEventConverter) }, |
|
||||
}; |
|
||||
|
|
||||
internal static Type GetBuiltinTypeConverter(Type type) |
|
||||
{ |
|
||||
_converters.TryGetValue(type, out var result); |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
/// <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 (type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) |
|
||||
{ |
|
||||
var inner = GetTypeConverter(type.GetGenericArguments()[0]); |
|
||||
if (inner == null) |
|
||||
return null; |
|
||||
return typeof(NullableTypeConverter<>).MakeGenericType(inner); |
|
||||
} |
|
||||
|
|
||||
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; |
|
||||
} |
|
||||
|
|
||||
/// <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; |
|
||||
} |
|
||||
} |
|
||||
@ -1,99 +0,0 @@ |
|||||
using System; |
|
||||
using System.ComponentModel; |
|
||||
using System.Globalization; |
|
||||
using System.Linq; |
|
||||
using System.Linq.Expressions; |
|
||||
using System.Reflection; |
|
||||
using Avalonia.Controls; |
|
||||
using Avalonia.Markup.Xaml.PortableXaml; |
|
||||
using Portable.Xaml; |
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.Converters |
|
||||
{ |
|
||||
internal class AvaloniaEventConverter : TypeConverter |
|
||||
{ |
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) |
|
||||
{ |
|
||||
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); |
|
||||
} |
|
||||
|
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) |
|
||||
{ |
|
||||
var text = value as string; |
|
||||
if (text != null) |
|
||||
{ |
|
||||
var rootObjectProvider = context.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider; |
|
||||
var destinationTypeProvider = context.GetService(typeof(IDestinationTypeProvider)) as IDestinationTypeProvider; |
|
||||
if (rootObjectProvider != null && destinationTypeProvider != null) |
|
||||
{ |
|
||||
var target = rootObjectProvider.RootObject; |
|
||||
var eventType = destinationTypeProvider.GetDestinationType(); |
|
||||
var eventParameters = eventType.GetRuntimeMethods().First(r => r.Name == "Invoke").GetParameters(); |
|
||||
// go in reverse to match System.Xaml behaviour
|
|
||||
var methods = target.GetType().GetRuntimeMethods().Reverse(); |
|
||||
|
|
||||
// find based on exact match parameter types first
|
|
||||
foreach (var method in methods) |
|
||||
{ |
|
||||
if (method.Name != text) |
|
||||
continue; |
|
||||
var parameters = method.GetParameters(); |
|
||||
if (eventParameters.Length != parameters.Length) |
|
||||
continue; |
|
||||
if (parameters.Length == 0) |
|
||||
return method.CreateDelegate(eventType, target); |
|
||||
|
|
||||
for (int i = 0; i < parameters.Length; i++) |
|
||||
{ |
|
||||
var param = parameters[i]; |
|
||||
var eventParam = eventParameters[i]; |
|
||||
if (param.ParameterType != eventParam.ParameterType) |
|
||||
break; |
|
||||
if (i == parameters.Length - 1) |
|
||||
return method.CreateDelegate(eventType, target); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// EnhancedXaml: Find method with compatible base class parameters
|
|
||||
foreach (var method in methods) |
|
||||
{ |
|
||||
if (method.Name != text) |
|
||||
continue; |
|
||||
var parameters = method.GetParameters(); |
|
||||
if (parameters.Length == 0 || eventParameters.Length != parameters.Length) |
|
||||
continue; |
|
||||
|
|
||||
for (int i = 0; i < parameters.Length; i++) |
|
||||
{ |
|
||||
var param = parameters[i]; |
|
||||
var eventParam = eventParameters[i]; |
|
||||
if (!param.ParameterType.GetTypeInfo().IsAssignableFrom(eventParam.ParameterType.GetTypeInfo())) |
|
||||
break; |
|
||||
if (i == parameters.Length - 1) |
|
||||
return method.CreateDelegate(eventType, target); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
var contextProvider = (IXamlSchemaContextProvider)context.GetService(typeof(IXamlSchemaContextProvider)); |
|
||||
var avaloniaContext = (AvaloniaXamlSchemaContext)contextProvider.SchemaContext; |
|
||||
|
|
||||
if (avaloniaContext.IsDesignMode) |
|
||||
{ |
|
||||
// We want to ignore missing events in the designer, so if event handler
|
|
||||
// wasn't found create an empty delegate.
|
|
||||
var lambdaExpression = Expression.Lambda( |
|
||||
eventType, |
|
||||
Expression.Empty(), |
|
||||
eventParameters.Select(x => Expression.Parameter(x.ParameterType))); |
|
||||
return lambdaExpression.Compile(); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
throw new XamlObjectWriterException($"Referenced value method {text} in type {target.GetType()} indicated by event {eventType.FullName} was not found"); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
return base.ConvertFrom(context, culture, value); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,89 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections; |
|
||||
using System.ComponentModel; |
|
||||
using System.Globalization; |
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.Converters |
|
||||
{ |
|
||||
public class NullableTypeConverter<T> : TypeConverter where T : TypeConverter, new() |
|
||||
{ |
|
||||
private TypeConverter _inner; |
|
||||
|
|
||||
public NullableTypeConverter() |
|
||||
{ |
|
||||
_inner = new T(); |
|
||||
} |
|
||||
|
|
||||
public NullableTypeConverter(TypeConverter inner) |
|
||||
{ |
|
||||
_inner = inner; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) |
|
||||
{ |
|
||||
if (value == null) |
|
||||
return null; |
|
||||
return _inner.ConvertTo(context, culture, value, destinationType); |
|
||||
} |
|
||||
|
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) |
|
||||
{ |
|
||||
if (value == null) |
|
||||
return null; |
|
||||
if (value as string == "") |
|
||||
return null; |
|
||||
return _inner.ConvertFrom(context, culture, value); |
|
||||
} |
|
||||
|
|
||||
public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) |
|
||||
{ |
|
||||
return _inner.CreateInstance(context, propertyValues); |
|
||||
} |
|
||||
|
|
||||
public override bool GetStandardValuesSupported(ITypeDescriptorContext context) |
|
||||
{ |
|
||||
return _inner.GetStandardValuesSupported(context); |
|
||||
} |
|
||||
|
|
||||
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) |
|
||||
{ |
|
||||
return _inner.GetStandardValuesExclusive(context); |
|
||||
} |
|
||||
|
|
||||
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) |
|
||||
{ |
|
||||
return _inner.GetCreateInstanceSupported(context); |
|
||||
} |
|
||||
|
|
||||
public override bool GetPropertiesSupported(ITypeDescriptorContext context) |
|
||||
{ |
|
||||
return _inner.GetPropertiesSupported(context); |
|
||||
} |
|
||||
|
|
||||
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) |
|
||||
{ |
|
||||
return _inner.GetStandardValues(context); |
|
||||
} |
|
||||
|
|
||||
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) |
|
||||
{ |
|
||||
return _inner.GetProperties(context, value, attributes); |
|
||||
} |
|
||||
|
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) |
|
||||
{ |
|
||||
return _inner.CanConvertTo(context, destinationType); |
|
||||
} |
|
||||
|
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) |
|
||||
{ |
|
||||
return _inner.CanConvertFrom(context, sourceType); |
|
||||
} |
|
||||
|
|
||||
public override bool IsValid(ITypeDescriptorContext context, object value) |
|
||||
{ |
|
||||
return _inner.IsValid(context, value); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,79 +0,0 @@ |
|||||
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 Func<string, T> _parse; |
|
||||
private static Func<string, IFormatProvider, T> _parseWithFormat; |
|
||||
|
|
||||
static ParseTypeConverter() |
|
||||
{ |
|
||||
var method = typeof(T).GetMethod("Parse", PublicStatic, null, StringIFormatProviderParameters, null); |
|
||||
|
|
||||
if (method != null) |
|
||||
{ |
|
||||
_parseWithFormat = (Func<string, IFormatProvider, T>)method |
|
||||
.CreateDelegate(typeof(Func<string, IFormatProvider, T>)); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
method = typeof(T).GetMethod("Parse", PublicStatic, null, StringParameter, null); |
|
||||
|
|
||||
if (method != null) |
|
||||
{ |
|
||||
_parse = (Func<string, T>)method.CreateDelegate(typeof(Func<string, T>)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,27 +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.Markup.Parsers; |
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.Converters |
|
||||
{ |
|
||||
using Portable.Xaml.ComponentModel; |
|
||||
using System.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(context.ResolveType); |
|
||||
|
|
||||
return parser.Parse((string)value); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,49 +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 Avalonia.Styling; |
|
||||
using Portable.Xaml; |
|
||||
using Portable.Xaml.ComponentModel; |
|
||||
using System.ComponentModel; |
|
||||
using Portable.Xaml.Markup; |
|
||||
using System; |
|
||||
using System.Globalization; |
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.Converters |
|
||||
{ |
|
||||
public class SetterValueTypeConverter : TypeConverter |
|
||||
{ |
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) |
|
||||
{ |
|
||||
return sourceType == typeof(string); |
|
||||
} |
|
||||
|
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) |
|
||||
{ |
|
||||
object setter = context.GetService<IProvideValueTarget>().TargetObject; |
|
||||
var schemaContext = context.GetService<IXamlSchemaContextProvider>().SchemaContext; |
|
||||
|
|
||||
return ConvertSetterValue(context, schemaContext, culture, (setter as Setter), value); |
|
||||
} |
|
||||
|
|
||||
[Obsolete("TODO: try assosiate Setter.Value property with SetterValueTypeConverter, so far coouldn't make it :(")] |
|
||||
internal static object ConvertSetterValue(ITypeDescriptorContext dcontext, XamlSchemaContext context, CultureInfo info, Setter setter, object value) |
|
||||
{ |
|
||||
Type targetType = setter?.Property?.PropertyType; |
|
||||
|
|
||||
if (targetType == null) |
|
||||
{ |
|
||||
return value; |
|
||||
} |
|
||||
|
|
||||
var ttConv = context.GetXamlType(targetType)?.TypeConverter?.ConverterInstance; |
|
||||
|
|
||||
if (ttConv != null) |
|
||||
{ |
|
||||
value = ttConv.ConvertFromString(dcontext, info, value as string); |
|
||||
} |
|
||||
|
|
||||
return value; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,9 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Markup.Xaml |
||||
|
{ |
||||
|
public abstract class MarkupExtension |
||||
|
{ |
||||
|
public abstract object ProvideValue(IServiceProvider serviceProvider); |
||||
|
} |
||||
|
} |
||||
@ -1,39 +0,0 @@ |
|||||
using Avalonia.Markup.Xaml.Templates; |
|
||||
using avm = Avalonia.Metadata; |
|
||||
using pm = Portable.Xaml.Markup; |
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.PortableXaml |
|
||||
{ |
|
||||
internal static class AttributeExtensions |
|
||||
{ |
|
||||
public static pm.XamlDeferLoadAttribute ToPortableXaml(this avm.TemplateContentAttribute attrib) |
|
||||
{ |
|
||||
if (attrib == null) |
|
||||
{ |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
return new pm.XamlDeferLoadAttribute(typeof(TemplateLoader), typeof(TemplateContent)); |
|
||||
} |
|
||||
|
|
||||
public static pm.AmbientAttribute ToPortableXaml(this avm.AmbientAttribute attrib) |
|
||||
{ |
|
||||
if (attrib == null) |
|
||||
{ |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
return new pm.AmbientAttribute(); |
|
||||
} |
|
||||
|
|
||||
public static pm.DependsOnAttribute ToPortableXaml(this avm.DependsOnAttribute attrib) |
|
||||
{ |
|
||||
if (attrib == null) |
|
||||
{ |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
return new pm.DependsOnAttribute(attrib.Name); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,83 +0,0 @@ |
|||||
using Avalonia.Markup.Xaml.Converters; |
|
||||
using Avalonia.Styling; |
|
||||
using Portable.Xaml.ComponentModel; |
|
||||
using System.ComponentModel; |
|
||||
using System; |
|
||||
using System.Linq; |
|
||||
using System.Reflection; |
|
||||
using avm = Avalonia.Metadata; |
|
||||
using pm = Portable.Xaml.Markup; |
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.PortableXaml |
|
||||
{ |
|
||||
public class AvaloniaMemberAttributeProvider : ICustomAttributeProvider |
|
||||
{ |
|
||||
public AvaloniaMemberAttributeProvider(MemberInfo info) |
|
||||
{ |
|
||||
_info = info; |
|
||||
} |
|
||||
|
|
||||
public object[] GetCustomAttributes(bool inherit) |
|
||||
{ |
|
||||
throw new NotImplementedException(); |
|
||||
} |
|
||||
|
|
||||
public object[] GetCustomAttributes(Type attributeType, bool inherit) |
|
||||
{ |
|
||||
Attribute result = null; |
|
||||
|
|
||||
if (attributeType == typeof(pm.XamlDeferLoadAttribute)) |
|
||||
{ |
|
||||
result = _info.GetCustomAttribute<avm.TemplateContentAttribute>(inherit) |
|
||||
.ToPortableXaml(); |
|
||||
} |
|
||||
else if (attributeType == typeof(pm.AmbientAttribute)) |
|
||||
{ |
|
||||
result = _info.GetCustomAttribute<avm.AmbientAttribute>(inherit) |
|
||||
.ToPortableXaml(); |
|
||||
} |
|
||||
else if (attributeType == typeof(pm.DependsOnAttribute)) |
|
||||
{ |
|
||||
result = _info.GetCustomAttribute<avm.DependsOnAttribute>(inherit) |
|
||||
.ToPortableXaml(); |
|
||||
} |
|
||||
else if (attributeType == typeof(TypeConverterAttribute) && |
|
||||
_info.DeclaringType == typeof(Setter) && |
|
||||
_info.Name == nameof(Setter.Value)) |
|
||||
{ |
|
||||
//actually it never comes here looks like if property type is object
|
|
||||
//Portable.Xaml is not searching for Type Converter
|
|
||||
result = new TypeConverterAttribute(typeof(SetterValueTypeConverter)); |
|
||||
} |
|
||||
else if (attributeType == typeof(TypeConverterAttribute) && _info is EventInfo) |
|
||||
{ |
|
||||
// If a type converter for `EventInfo` is registered, then use that to convert
|
|
||||
// event handler values. This is used by the designer to override the lookup
|
|
||||
// for event handlers with a null handler.
|
|
||||
var eventConverter = AvaloniaTypeConverters.GetTypeConverter(typeof(EventInfo)); |
|
||||
|
|
||||
if (eventConverter != null) |
|
||||
{ |
|
||||
result = new TypeConverterAttribute(eventConverter); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (result == null) |
|
||||
{ |
|
||||
var attr = _info.GetCustomAttributes(attributeType, inherit); |
|
||||
return (attr as object[]) ?? attr.ToArray(); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return new object[] { result }; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public bool IsDefined(Type attributeType, bool inherit) |
|
||||
{ |
|
||||
throw new NotImplementedException(); |
|
||||
} |
|
||||
|
|
||||
private readonly MemberInfo _info; |
|
||||
} |
|
||||
} |
|
||||
@ -1,56 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
using Avalonia.Controls; |
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.PortableXaml |
|
||||
{ |
|
||||
internal class AvaloniaNameScope : Portable.Xaml.Markup.INameScope |
|
||||
{ |
|
||||
public object Instance { get; set; } |
|
||||
|
|
||||
private Dictionary<string, object> _names = new Dictionary<string, object>(); |
|
||||
|
|
||||
public object FindName(string name) |
|
||||
{ |
|
||||
object result; |
|
||||
if (_names.TryGetValue(name, out result)) |
|
||||
return result; |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
public void RegisterName(string name, object scopedElement) |
|
||||
{ |
|
||||
if (scopedElement != null) |
|
||||
_names.Add(name, scopedElement); |
|
||||
|
|
||||
//TODO: ???
|
|
||||
//var control = scopedElement as Control;
|
|
||||
|
|
||||
//if (control != null)
|
|
||||
//{
|
|
||||
// var nameScope = (Instance as INameScope) ?? control.FindNameScope();
|
|
||||
|
|
||||
// if (nameScope != null)
|
|
||||
// {
|
|
||||
// nameScope.Register(name, scopedElement);
|
|
||||
// }
|
|
||||
//}
|
|
||||
} |
|
||||
|
|
||||
public void UnregisterName(string name) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public void RegisterOnNameScope(object target) |
|
||||
{ |
|
||||
var nameScope = target as INameScope; |
|
||||
|
|
||||
if (nameScope != null) |
|
||||
{ |
|
||||
foreach (var v in _names) |
|
||||
{ |
|
||||
nameScope.Register(v.Key, v.Value); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,147 +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.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Reflection; |
|
||||
using Avalonia.Controls; |
|
||||
using Avalonia.Data; |
|
||||
using Avalonia.Markup.Xaml.Templates; |
|
||||
using Avalonia.Media; |
|
||||
using Avalonia.Metadata; |
|
||||
using Avalonia.Platform; |
|
||||
using Avalonia.Styling; |
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.Context |
|
||||
{ |
|
||||
using ClrNamespaceInfo = Tuple<string, Assembly>; |
|
||||
|
|
||||
public interface IRuntimeTypeProvider |
|
||||
{ |
|
||||
Type FindType(string xamlNamespace, string name, Type[] genArgs); |
|
||||
|
|
||||
IEnumerable<Assembly> ReferencedAssemblies { get; } |
|
||||
} |
|
||||
|
|
||||
public class AvaloniaRuntimeTypeProvider : IRuntimeTypeProvider |
|
||||
{ |
|
||||
private const string ClrNamespace = "clr-namespace:"; |
|
||||
// private const string AvaloniaNs = "https://github.com/avaloniaui";
|
|
||||
|
|
||||
private static readonly IEnumerable<Assembly> ForcedAssemblies = new[] |
|
||||
{ |
|
||||
typeof(AvaloniaObject).GetTypeInfo().Assembly, |
|
||||
typeof(Animation.Animation).GetTypeInfo().Assembly, |
|
||||
typeof(Control).GetTypeInfo().Assembly, |
|
||||
typeof(Style).GetTypeInfo().Assembly, |
|
||||
typeof(DataTemplate).GetTypeInfo().Assembly, |
|
||||
typeof(SolidColorBrush).GetTypeInfo().Assembly, |
|
||||
typeof(Binding).GetTypeInfo().Assembly, |
|
||||
}; |
|
||||
|
|
||||
private Dictionary<string, HashSet<ClrNamespaceInfo>> _namespaces = new Dictionary<string, HashSet<ClrNamespaceInfo>>(); |
|
||||
|
|
||||
private List<Assembly> _scanned = new List<Assembly>(); |
|
||||
|
|
||||
public IEnumerable<Assembly> ReferencedAssemblies => _scanned; |
|
||||
|
|
||||
public AvaloniaRuntimeTypeProvider() |
|
||||
{ |
|
||||
ScanAssemblies(ForcedAssemblies); |
|
||||
ScanNewAssemblies(); |
|
||||
} |
|
||||
|
|
||||
private static bool IsClrNamespace(string ns) |
|
||||
{ |
|
||||
return ns.StartsWith(ClrNamespace); |
|
||||
} |
|
||||
|
|
||||
private static Assembly GetAssembly(string assemblyName) |
|
||||
{ |
|
||||
return Assembly.Load(new AssemblyName(assemblyName)); |
|
||||
} |
|
||||
|
|
||||
private void ScanAssemblies(IEnumerable<Assembly> assemblies) |
|
||||
{ |
|
||||
foreach (var assembly in assemblies) |
|
||||
{ |
|
||||
var namespaces = assembly.GetCustomAttributes<XmlnsDefinitionAttribute>() |
|
||||
.Select(x => new { x.XmlNamespace, x.ClrNamespace }) |
|
||||
.GroupBy(x => x.XmlNamespace); |
|
||||
|
|
||||
foreach (var nsa in namespaces) |
|
||||
{ |
|
||||
HashSet<ClrNamespaceInfo> reg; |
|
||||
|
|
||||
if (!_namespaces.TryGetValue(nsa.Key, out reg)) |
|
||||
{ |
|
||||
_namespaces[nsa.Key] = reg = new HashSet<Tuple<string, Assembly>>(); |
|
||||
} |
|
||||
|
|
||||
foreach (var child in nsa) |
|
||||
{ |
|
||||
reg.Add(new ClrNamespaceInfo(child.ClrNamespace, assembly)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
_scanned.Add(assembly); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void ScanNewAssemblies() |
|
||||
{ |
|
||||
IEnumerable<Assembly> assemblies = AppDomain.CurrentDomain.GetAssemblies(); |
|
||||
|
|
||||
if (assemblies != null) |
|
||||
{ |
|
||||
assemblies = assemblies.Except(_scanned); |
|
||||
ScanAssemblies(assemblies); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private Dictionary<string, Type> _typeCache = new Dictionary<string, Type>(); |
|
||||
|
|
||||
public Type FindType(string xamlNamespace, string name, Type[] genArgs) |
|
||||
{ |
|
||||
if (IsClrNamespace(xamlNamespace)) |
|
||||
{ |
|
||||
//we need to handle only xaml url namespaces for avalonia,
|
|
||||
//the other namespaces are handled well in portable.xaml
|
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
string key = $"{xamlNamespace}:{name}"; |
|
||||
|
|
||||
Type type; |
|
||||
|
|
||||
if (_typeCache.TryGetValue(key, out type)) |
|
||||
{ |
|
||||
return type; |
|
||||
} |
|
||||
|
|
||||
HashSet<ClrNamespaceInfo> reg; |
|
||||
|
|
||||
if (!_namespaces.TryGetValue(xamlNamespace, out reg)) |
|
||||
{ |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
if (genArgs != null) |
|
||||
name += "`" + genArgs.Length; |
|
||||
|
|
||||
foreach (var ns in reg) |
|
||||
{ |
|
||||
var n = ns.Item1 + "." + name; |
|
||||
var t = ns.Item2.GetType(n); |
|
||||
if (t != null) |
|
||||
{ |
|
||||
_typeCache[key] = t; |
|
||||
return t; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,117 +0,0 @@ |
|||||
// Copyright (c) The Perspex Project. All rights reserved.
|
|
||||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
||||
|
|
||||
using Portable.Xaml.ComponentModel; |
|
||||
using System.ComponentModel; |
|
||||
using System; |
|
||||
using System.Linq; |
|
||||
using System.Reflection; |
|
||||
using avm = Avalonia.Metadata; |
|
||||
using pm = Portable.Xaml.Markup; |
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.PortableXaml |
|
||||
{ |
|
||||
internal class AvaloniaTypeAttributeProvider : ICustomAttributeProvider |
|
||||
{ |
|
||||
public AvaloniaTypeAttributeProvider(Type type) |
|
||||
{ |
|
||||
_type = type; |
|
||||
} |
|
||||
|
|
||||
public object[] GetCustomAttributes(bool inherit) |
|
||||
{ |
|
||||
throw new NotImplementedException(); |
|
||||
} |
|
||||
|
|
||||
public object[] GetCustomAttributes(Type attributeType, bool inherit) |
|
||||
{ |
|
||||
Attribute result = null; |
|
||||
|
|
||||
var ti = _type.GetTypeInfo(); |
|
||||
|
|
||||
if (attributeType == typeof(pm.ContentPropertyAttribute)) |
|
||||
{ |
|
||||
result = GetContentPropertyAttribute(inherit); |
|
||||
} |
|
||||
else if (attributeType == typeof(pm.RuntimeNamePropertyAttribute)) |
|
||||
{ |
|
||||
if (_namedType.IsAssignableFrom(ti)) |
|
||||
{ |
|
||||
result = new pm.RuntimeNamePropertyAttribute(nameof(INamed.Name)); |
|
||||
} |
|
||||
} |
|
||||
else if (attributeType == typeof(TypeConverterAttribute)) |
|
||||
{ |
|
||||
var builtin = AvaloniaTypeConverters.GetBuiltinTypeConverter(_type); |
|
||||
if (builtin != null) |
|
||||
result = new TypeConverterAttribute(builtin); |
|
||||
result = result ?? ti.GetCustomAttribute(attributeType, inherit); |
|
||||
|
|
||||
if (result == null) |
|
||||
{ |
|
||||
var convType = AvaloniaTypeConverters.GetTypeConverter(_type); |
|
||||
|
|
||||
if (convType != null) |
|
||||
{ |
|
||||
result = new TypeConverterAttribute(convType); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
else if (attributeType == typeof(pm.AmbientAttribute)) |
|
||||
{ |
|
||||
result = ti.GetCustomAttribute<avm.AmbientAttribute>(inherit) |
|
||||
.ToPortableXaml(); |
|
||||
} |
|
||||
|
|
||||
if (result == null) |
|
||||
{ |
|
||||
var attr = ti.GetCustomAttributes(attributeType, inherit); |
|
||||
return (attr as object[]) ?? attr.ToArray(); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return new object[] { result }; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public bool IsDefined(Type attributeType, bool inherit) |
|
||||
{ |
|
||||
throw new NotImplementedException(); |
|
||||
} |
|
||||
|
|
||||
private readonly TypeInfo _namedType = typeof(INamed).GetTypeInfo(); |
|
||||
|
|
||||
private readonly Type _type; |
|
||||
|
|
||||
private Attribute GetContentPropertyAttribute(bool inherit) |
|
||||
{ |
|
||||
var type = _type; |
|
||||
|
|
||||
while (type != null) |
|
||||
{ |
|
||||
var properties = type.GetTypeInfo().DeclaredProperties |
|
||||
.Where(x => x.GetCustomAttribute<avm.ContentAttribute>() != null); |
|
||||
string result = null; |
|
||||
|
|
||||
foreach (var property in properties) |
|
||||
{ |
|
||||
if (result != null) |
|
||||
{ |
|
||||
throw new Exception($"Content property defined more than once on {type}."); |
|
||||
} |
|
||||
|
|
||||
result = property.Name; |
|
||||
} |
|
||||
|
|
||||
if (result != null) |
|
||||
{ |
|
||||
return new pm.ContentPropertyAttribute(result); |
|
||||
} |
|
||||
|
|
||||
type = inherit ? type.GetTypeInfo().BaseType : null; |
|
||||
} |
|
||||
|
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,31 +0,0 @@ |
|||||
using Portable.Xaml; |
|
||||
using Portable.Xaml.Markup; |
|
||||
using System; |
|
||||
using System.Reflection; |
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.PortableXaml |
|
||||
{ |
|
||||
public class AvaloniaXamlContext : IUriContext |
|
||||
{ |
|
||||
private AvaloniaXamlContext() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public Assembly LocalAssembly { get; private set; } |
|
||||
|
|
||||
public Uri BaseUri { get; set; } |
|
||||
|
|
||||
public object RootInstance { get; private set; } |
|
||||
|
|
||||
internal static AvaloniaXamlContext For(XamlXmlReaderSettings sett, |
|
||||
object rootInstance) |
|
||||
{ |
|
||||
return new AvaloniaXamlContext() |
|
||||
{ |
|
||||
BaseUri = sett.BaseUri, |
|
||||
LocalAssembly = sett.LocalAssembly, |
|
||||
RootInstance = rootInstance |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,222 +0,0 @@ |
|||||
using Avalonia.Data; |
|
||||
using Portable.Xaml; |
|
||||
using Portable.Xaml.ComponentModel; |
|
||||
using System.ComponentModel; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Reflection; |
|
||||
using Avalonia.Controls; |
|
||||
using Portable.Xaml.Schema; |
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.PortableXaml |
|
||||
{ |
|
||||
class AvaloniaXamlObjectWriter : XamlObjectWriter |
|
||||
{ |
|
||||
private static Dictionary<XamlDirective, string> DesignDirectives = new Dictionary<string, string> |
|
||||
{ |
|
||||
["DataContext"] = "DataContext", |
|
||||
["DesignWidth"] = "Width", ["DesignHeight"] = "Height", ["PreviewWith"] = "PreviewWith" |
|
||||
} |
|
||||
.ToDictionary(p => new XamlDirective( |
|
||||
new[] {"http://schemas.microsoft.com/expression/blend/2008"}, p.Key, |
|
||||
XamlLanguage.Object, null, AllowedMemberLocations.Attribute), p => p.Value); |
|
||||
|
|
||||
private readonly AvaloniaXamlSchemaContext _schemaContext; |
|
||||
|
|
||||
public static AvaloniaXamlObjectWriter Create( |
|
||||
AvaloniaXamlSchemaContext schemaContext, |
|
||||
AvaloniaXamlContext context, |
|
||||
IAmbientProvider parentAmbientProvider = null) |
|
||||
{ |
|
||||
var nameScope = new AvaloniaNameScope { Instance = context?.RootInstance }; |
|
||||
|
|
||||
var writerSettings = new XamlObjectWriterSettings() |
|
||||
{ |
|
||||
ExternalNameScope = nameScope, |
|
||||
RegisterNamesOnExternalNamescope = true, |
|
||||
RootObjectInstance = context?.RootInstance |
|
||||
}; |
|
||||
|
|
||||
return new AvaloniaXamlObjectWriter(schemaContext, |
|
||||
writerSettings.WithContext(context), |
|
||||
nameScope, |
|
||||
parentAmbientProvider); |
|
||||
} |
|
||||
|
|
||||
private readonly DelayedValuesHelper _delayedValuesHelper = new DelayedValuesHelper(); |
|
||||
|
|
||||
private AvaloniaNameScope _nameScope; |
|
||||
|
|
||||
private AvaloniaXamlObjectWriter( |
|
||||
AvaloniaXamlSchemaContext schemaContext, |
|
||||
XamlObjectWriterSettings settings, |
|
||||
AvaloniaNameScope nameScope, |
|
||||
IAmbientProvider parentAmbientProvider) |
|
||||
: base(schemaContext, settings, parentAmbientProvider) |
|
||||
{ |
|
||||
_nameScope = nameScope; |
|
||||
_schemaContext = schemaContext; |
|
||||
} |
|
||||
|
|
||||
protected override void Dispose(bool disposing) |
|
||||
{ |
|
||||
if (disposing) |
|
||||
{ |
|
||||
if (_nameScope != null && Result != null) |
|
||||
{ |
|
||||
_nameScope.RegisterOnNameScope(Result); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
base.Dispose(disposing); |
|
||||
} |
|
||||
|
|
||||
public void ApplyAllDelayedProperties() |
|
||||
{ |
|
||||
//HACK: We need this because Begin/EndInit ordering is broken
|
|
||||
_delayedValuesHelper.ApplyAll(); |
|
||||
} |
|
||||
|
|
||||
protected internal override void OnAfterProperties(object value) |
|
||||
{ |
|
||||
_delayedValuesHelper.EndInit(value); |
|
||||
|
|
||||
base.OnAfterProperties(value); |
|
||||
} |
|
||||
|
|
||||
protected internal override void OnBeforeProperties(object value) |
|
||||
{ |
|
||||
if (value != null) |
|
||||
_delayedValuesHelper.BeginInit(value); |
|
||||
|
|
||||
base.OnBeforeProperties(value); |
|
||||
} |
|
||||
|
|
||||
protected internal override bool OnSetValue(object target, XamlMember member, object value) |
|
||||
{ |
|
||||
if (_delayedValuesHelper.TryAdd(target, member, value)) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
return base.OnSetValue(target, member, value); |
|
||||
} |
|
||||
|
|
||||
public override void WriteStartMember(XamlMember property) |
|
||||
{ |
|
||||
foreach(var d in DesignDirectives) |
|
||||
if (property == d.Key && _schemaContext.IsDesignMode) |
|
||||
{ |
|
||||
base.WriteStartMember(new XamlMember(d.Value, |
|
||||
typeof(Design).GetMethod("Get" + d.Value, BindingFlags.Static | BindingFlags.Public), |
|
||||
typeof(Design).GetMethod("Set" + d.Value, BindingFlags.Static | BindingFlags.Public), |
|
||||
SchemaContext)); |
|
||||
return; |
|
||||
} |
|
||||
base.WriteStartMember(property); |
|
||||
} |
|
||||
|
|
||||
private class DelayedValuesHelper |
|
||||
{ |
|
||||
private int _cnt; |
|
||||
|
|
||||
private HashSet<object> _targets = new HashSet<object>(); |
|
||||
|
|
||||
private IList<DelayedValue> _values = new List<DelayedValue>(); |
|
||||
|
|
||||
private IEnumerable<DelayedValue> Values => _values; |
|
||||
|
|
||||
public void BeginInit(object target) |
|
||||
{ |
|
||||
++_cnt; |
|
||||
|
|
||||
AddTargetIfNeeded(target); |
|
||||
} |
|
||||
|
|
||||
public void EndInit(object target) |
|
||||
{ |
|
||||
--_cnt; |
|
||||
|
|
||||
if (_cnt == 0) |
|
||||
{ |
|
||||
ApplyAll(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public bool TryAdd(object target, XamlMember member, object value) |
|
||||
{ |
|
||||
if (value is IBinding) |
|
||||
{ |
|
||||
Add(new DelayedValue(target, member, value)); |
|
||||
|
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
private void Add(DelayedValue value) |
|
||||
{ |
|
||||
_values.Add(value); |
|
||||
|
|
||||
var target = value.Target; |
|
||||
|
|
||||
if (!_targets.Contains(value.Target)) |
|
||||
{ |
|
||||
_targets.Add(target); |
|
||||
(target as ISupportInitialize)?.BeginInit(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void AddTargetIfNeeded(object target) |
|
||||
{ |
|
||||
if (!_targets.Contains(target)) |
|
||||
{ |
|
||||
Add(new DelayedValue(target, null, null)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public void ApplyAll() |
|
||||
{ |
|
||||
//TODO: revisit this
|
|
||||
//apply delayed values and clear
|
|
||||
//that's the last object let's set all delayed bindings
|
|
||||
foreach (var dv in Values.Where(v => v.Member != null)) |
|
||||
{ |
|
||||
dv.Member.Invoker.SetValue(dv.Target, dv.Value); |
|
||||
} |
|
||||
|
|
||||
//TODO: check/add some order of end init
|
|
||||
//currently we are sending end init in the order of
|
|
||||
//objects creation
|
|
||||
foreach (var v in Values) |
|
||||
{ |
|
||||
var target = v.Target; |
|
||||
|
|
||||
if (_targets.Contains(target)) |
|
||||
{ |
|
||||
_targets.Remove(target); |
|
||||
(target as ISupportInitialize)?.EndInit(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
_targets.Clear(); |
|
||||
_values.Clear(); |
|
||||
} |
|
||||
|
|
||||
private class DelayedValue |
|
||||
{ |
|
||||
public DelayedValue(object target, XamlMember member, object value) |
|
||||
{ |
|
||||
Target = target; |
|
||||
Member = member; |
|
||||
Value = value; |
|
||||
} |
|
||||
|
|
||||
public XamlMember Member { get; } |
|
||||
public object Target { get; } |
|
||||
public object Value { get; } |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,327 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Reflection; |
|
||||
using Avalonia.Data; |
|
||||
using Avalonia.Markup.Xaml.Context; |
|
||||
using Avalonia.Markup.Xaml.MarkupExtensions; |
|
||||
using Avalonia.Markup.Xaml.Styling; |
|
||||
using Portable.Xaml; |
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.PortableXaml |
|
||||
{ |
|
||||
internal class AvaloniaXamlSchemaContext : XamlSchemaContext |
|
||||
{ |
|
||||
private static AvaloniaXamlSchemaContext s_instance; |
|
||||
private static AvaloniaXamlSchemaContext s_designInstance; |
|
||||
|
|
||||
public static AvaloniaXamlSchemaContext Instance |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
if (s_instance == null) |
|
||||
{ |
|
||||
s_instance = Create(); |
|
||||
} |
|
||||
|
|
||||
return s_instance; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static AvaloniaXamlSchemaContext DesignInstance |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
if (s_designInstance == null) |
|
||||
{ |
|
||||
s_designInstance = Create(); |
|
||||
s_designInstance.IsDesignMode = true; |
|
||||
} |
|
||||
|
|
||||
return s_designInstance; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public bool IsDesignMode { get; private set; } |
|
||||
public static AvaloniaXamlSchemaContext Create(IRuntimeTypeProvider typeProvider = null) |
|
||||
{ |
|
||||
return new AvaloniaXamlSchemaContext(typeProvider ?? new AvaloniaRuntimeTypeProvider()); |
|
||||
} |
|
||||
|
|
||||
private AvaloniaXamlSchemaContext(IRuntimeTypeProvider typeProvider) |
|
||||
//better not set the references assemblies
|
|
||||
//TODO: check this on iOS
|
|
||||
//: base(typeProvider.ReferencedAssemblies)
|
|
||||
{ |
|
||||
Contract.Requires<ArgumentNullException>(typeProvider != null); |
|
||||
|
|
||||
_avaloniaTypeProvider = typeProvider; |
|
||||
} |
|
||||
|
|
||||
private IRuntimeTypeProvider _avaloniaTypeProvider; |
|
||||
|
|
||||
protected override XamlType GetXamlType(string xamlNamespace, string name, params XamlType[] typeArguments) |
|
||||
{ |
|
||||
XamlType type = null; |
|
||||
try |
|
||||
{ |
|
||||
type = ResolveXamlTypeName(xamlNamespace, name, typeArguments, false); |
|
||||
|
|
||||
if (type == null) |
|
||||
{ |
|
||||
type = base.GetXamlType(xamlNamespace, name, typeArguments); |
|
||||
} |
|
||||
} |
|
||||
catch (Exception e) |
|
||||
{ |
|
||||
//TODO: log or wrap exception
|
|
||||
throw e; |
|
||||
} |
|
||||
return type; |
|
||||
} |
|
||||
|
|
||||
private XamlType ResolveXamlTypeName(string xmlNamespace, string xmlLocalName, XamlType[] typeArguments, bool required) |
|
||||
{ |
|
||||
Type[] genArgs = null; |
|
||||
if (typeArguments != null && typeArguments.Any()) |
|
||||
{ |
|
||||
genArgs = typeArguments.Select(t => t?.UnderlyingType).ToArray(); |
|
||||
|
|
||||
if (genArgs.Any(t => t == null)) |
|
||||
{ |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// MarkupExtension type could omit "Extension" part in XML name.
|
|
||||
Type type = _avaloniaTypeProvider.FindType(xmlNamespace, |
|
||||
xmlLocalName, |
|
||||
genArgs) ?? |
|
||||
_avaloniaTypeProvider.FindType(xmlNamespace, |
|
||||
xmlLocalName + "Extension", |
|
||||
genArgs); |
|
||||
|
|
||||
if (type != null) |
|
||||
{ |
|
||||
Type extType; |
|
||||
if (_wellKnownExtensionTypes.TryGetValue(type, out extType)) |
|
||||
{ |
|
||||
type = extType; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (type == null) |
|
||||
{ |
|
||||
//let's try the simple types
|
|
||||
//in Portable xaml like xmlns:sys='clr-namespace:System;assembly=mscorlib'
|
|
||||
//and sys:Double is not resolved properly
|
|
||||
return ResolveSimpleTypeName(xmlNamespace, xmlLocalName); |
|
||||
} |
|
||||
|
|
||||
return GetXamlType(type); |
|
||||
} |
|
||||
|
|
||||
#region Workaround for bug in Portablexaml system types like double,int etc ...
|
|
||||
|
|
||||
private static Type[] _simpleTypes = new Type[] |
|
||||
{ |
|
||||
typeof(bool), |
|
||||
typeof(byte), |
|
||||
typeof(char), |
|
||||
typeof(decimal), |
|
||||
typeof(double), |
|
||||
typeof(Int16), |
|
||||
typeof(Int32), |
|
||||
typeof(Int64), |
|
||||
typeof(float), |
|
||||
typeof(string), |
|
||||
typeof(TimeSpan), |
|
||||
typeof(Uri), |
|
||||
}; |
|
||||
|
|
||||
private static Dictionary<Tuple<string, string>, XamlType> _simpleXamlTypes; |
|
||||
|
|
||||
//in Portable xaml like xmlns:sys='clr-namespace:System;assembly=mscorlib'
|
|
||||
//and sys:Double is not resolved properly
|
|
||||
[Obsolete("TODO: remove once it's fixed in Portable.xaml")] |
|
||||
private static XamlType ResolveSimpleTypeName(string xmlNamespace, string xmlLocalName) |
|
||||
{ |
|
||||
if (_simpleXamlTypes == null) |
|
||||
{ |
|
||||
_simpleXamlTypes = new Dictionary<Tuple<string, string>, XamlType>(); |
|
||||
|
|
||||
foreach (var type in _simpleTypes) |
|
||||
{ |
|
||||
string asmName = type.GetTypeInfo().Assembly.GetName().Name; |
|
||||
string ns = $"clr-namespace:{type.Namespace};assembly={asmName}"; |
|
||||
var xamlType = XamlLanguage.AllTypes.First(t => t.UnderlyingType == type); |
|
||||
_simpleXamlTypes.Add(new Tuple<string, string>(ns, type.Name), xamlType); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
XamlType result; |
|
||||
|
|
||||
var key = new Tuple<string, string>(xmlNamespace, xmlLocalName); |
|
||||
|
|
||||
_simpleXamlTypes.TryGetValue(key, out result); |
|
||||
|
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
#endregion Workaround for bug in Portablexaml system types like double,int etc ...
|
|
||||
|
|
||||
protected internal override ICustomAttributeProvider GetCustomAttributeProvider(Type type) |
|
||||
=> new AvaloniaTypeAttributeProvider(type); |
|
||||
|
|
||||
protected internal override ICustomAttributeProvider GetCustomAttributeProvider(MemberInfo member) |
|
||||
=> new AvaloniaMemberAttributeProvider(member); |
|
||||
|
|
||||
public override XamlType GetXamlType(Type type) |
|
||||
{ |
|
||||
XamlType result = null; |
|
||||
|
|
||||
if (_cachedTypes.TryGetValue(type, out result)) |
|
||||
{ |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
_cachedTypes[type] = result = GetAvaloniaXamlType(type) ?? base.GetXamlType(type); |
|
||||
|
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
private static readonly Dictionary<Type, Type> _wellKnownExtensionTypes = new Dictionary<Type, Type>() |
|
||||
{ |
|
||||
{ typeof(Binding), typeof(BindingExtension) }, |
|
||||
{ typeof(StyleInclude), typeof(StyleIncludeExtension) }, |
|
||||
}; |
|
||||
|
|
||||
private XamlType GetAvaloniaXamlType(Type type) |
|
||||
{ |
|
||||
//if type is extension get the original type to check
|
|
||||
var origType = _wellKnownExtensionTypes.FirstOrDefault(v => v.Value == type).Key; |
|
||||
|
|
||||
if (typeof(IBinding).GetTypeInfo().IsAssignableFrom((origType ?? type).GetTypeInfo())) |
|
||||
{ |
|
||||
return new BindingXamlType(type, this); |
|
||||
} |
|
||||
|
|
||||
if (origType != null || |
|
||||
typeof(AvaloniaObject).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) |
|
||||
{ |
|
||||
return new AvaloniaXamlType(type, this); |
|
||||
} |
|
||||
|
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
protected internal override XamlMember GetAttachableProperty(string attachablePropertyName, MethodInfo getter, MethodInfo setter) |
|
||||
{ |
|
||||
var key = MemberKey.Create(getter ?? setter, attachablePropertyName, "a"); |
|
||||
|
|
||||
XamlMember result; |
|
||||
|
|
||||
if (_cachedMembers.TryGetValue(key, out result)) |
|
||||
{ |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
var type = (getter ?? setter).DeclaringType; |
|
||||
|
|
||||
var prop = AvaloniaPropertyRegistry.Instance.FindRegistered(type, attachablePropertyName); |
|
||||
|
|
||||
if (prop != null) |
|
||||
{ |
|
||||
result = new AvaloniaAttachedPropertyXamlMember( |
|
||||
prop, attachablePropertyName, |
|
||||
getter, setter, this); |
|
||||
} |
|
||||
|
|
||||
if (result == null) |
|
||||
{ |
|
||||
result = base.GetAttachableProperty(attachablePropertyName, getter, setter); |
|
||||
} |
|
||||
|
|
||||
return _cachedMembers[key] = result; |
|
||||
} |
|
||||
|
|
||||
protected internal override XamlMember GetProperty(PropertyInfo pi) |
|
||||
{ |
|
||||
Type objType = pi.DeclaringType; |
|
||||
string name = pi.Name; |
|
||||
|
|
||||
XamlMember result; |
|
||||
|
|
||||
var key = MemberKey.Create(pi, "p"); |
|
||||
|
|
||||
if (_cachedMembers.TryGetValue(key, out result)) |
|
||||
{ |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
var avProp = AvaloniaPropertyRegistry.Instance.FindRegistered(objType, name); |
|
||||
|
|
||||
if (avProp != null) |
|
||||
{ |
|
||||
result = new AvaloniaPropertyXamlMember(avProp, pi, this); |
|
||||
} |
|
||||
|
|
||||
if (result == null) |
|
||||
{ |
|
||||
result = new PropertyXamlMember(pi, this); |
|
||||
} |
|
||||
|
|
||||
return _cachedMembers[key] = result; |
|
||||
} |
|
||||
|
|
||||
private Dictionary<Type, XamlType> _cachedTypes = new Dictionary<Type, XamlType>(); |
|
||||
|
|
||||
private Dictionary<MemberKey, XamlMember> _cachedMembers = new Dictionary<MemberKey, XamlMember>(); |
|
||||
|
|
||||
private struct MemberKey |
|
||||
{ |
|
||||
public static MemberKey Create(MemberInfo m, string name, string memberType) |
|
||||
{ |
|
||||
return new MemberKey(m.DeclaringType, name, memberType); |
|
||||
} |
|
||||
|
|
||||
public static MemberKey Create(MemberInfo m, string memberType) |
|
||||
{ |
|
||||
return Create(m, m.Name, memberType); |
|
||||
} |
|
||||
|
|
||||
public MemberKey(Type type, object member, string memberType) |
|
||||
{ |
|
||||
Type = type; |
|
||||
Member = member; |
|
||||
MemberType = memberType; |
|
||||
} |
|
||||
|
|
||||
public Type Type { get; } |
|
||||
|
|
||||
public object Member { get; } |
|
||||
|
|
||||
public string MemberType { get; } |
|
||||
|
|
||||
public override string ToString() |
|
||||
{ |
|
||||
return $"{MemberType}:{Type.Namespace}:{Type.Name}.{Member}"; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
public override bool TryGetCompatibleXamlNamespace(string xamlNamespace, out string compatibleNamespace) |
|
||||
{ |
|
||||
//Forces XamlXmlReader to not ignore our namespace in design mode if mc:Ignorable is set
|
|
||||
if (IsDesignMode && |
|
||||
xamlNamespace == "http://schemas.microsoft.com/expression/blend/2008") |
|
||||
{ |
|
||||
compatibleNamespace = xamlNamespace; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
return base.TryGetCompatibleXamlNamespace(xamlNamespace, out compatibleNamespace); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
@ -1,388 +0,0 @@ |
|||||
using Avalonia.Controls; |
|
||||
using Avalonia.Data; |
|
||||
using Avalonia.Markup.Data; |
|
||||
using Avalonia.Metadata; |
|
||||
using Avalonia.Styling; |
|
||||
using Portable.Xaml; |
|
||||
using Portable.Xaml.Schema; |
|
||||
using System; |
|
||||
using System.Collections; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Globalization; |
|
||||
using System.Reflection; |
|
||||
using System.Xml.Serialization; |
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.PortableXaml |
|
||||
{ |
|
||||
using Converters; |
|
||||
using PropertyKey = Tuple<Type, string>; |
|
||||
|
|
||||
public class AvaloniaXamlType : XamlType |
|
||||
{ |
|
||||
static readonly AvaloniaPropertyTypeConverter propertyTypeConverter = new AvaloniaPropertyTypeConverter(); |
|
||||
|
|
||||
public AvaloniaXamlType(Type underlyingType, XamlSchemaContext schemaContext) : |
|
||||
base(underlyingType, schemaContext) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected override XamlMember LookupAttachableMember(string name) |
|
||||
{ |
|
||||
var m = base.LookupAttachableMember(name); |
|
||||
|
|
||||
if (m == null) |
|
||||
{ |
|
||||
// Might be an AddOwnered attached property.
|
|
||||
var avProp = AvaloniaPropertyRegistry.Instance.FindRegistered(UnderlyingType, name); |
|
||||
|
|
||||
if (avProp?.IsAttached == true) |
|
||||
{ |
|
||||
return new AvaloniaPropertyXamlMember(avProp, this); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return m; |
|
||||
} |
|
||||
|
|
||||
protected override XamlMember LookupMember(string name, bool skipReadOnlyCheck) |
|
||||
{ |
|
||||
var m = base.LookupMember(name, skipReadOnlyCheck); |
|
||||
|
|
||||
if (m == null && !name.Contains(".")) |
|
||||
{ |
|
||||
//so far Portable.xaml haven't found the member/property
|
|
||||
//but what if we have AvaloniaProperty
|
|
||||
//without setter and/or without getter
|
|
||||
//let's try to find the AvaloniaProperty as a fallback
|
|
||||
var avProp = AvaloniaPropertyRegistry.Instance.FindRegistered(UnderlyingType, name); |
|
||||
|
|
||||
if (avProp != null && !(skipReadOnlyCheck && avProp.IsReadOnly)) |
|
||||
{ |
|
||||
m = new AvaloniaPropertyXamlMember(avProp, this); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return m; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public class BindingXamlType : XamlType |
|
||||
{ |
|
||||
private static List<Type> _notAssignable = |
|
||||
new List<Type> |
|
||||
{ |
|
||||
typeof (IXmlSerializable) |
|
||||
}; |
|
||||
|
|
||||
public BindingXamlType(Type underlyingType, XamlSchemaContext schemaContext) : |
|
||||
base(underlyingType, schemaContext) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override bool CanAssignTo(XamlType xamlType) |
|
||||
{ |
|
||||
return !_notAssignable.Contains(xamlType.UnderlyingType); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public class PropertyXamlMember : XamlMember |
|
||||
{ |
|
||||
public PropertyXamlMember(PropertyInfo propertyInfo, XamlSchemaContext schemaContext) |
|
||||
: base(propertyInfo, schemaContext) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected PropertyXamlMember(string attachablePropertyName, |
|
||||
MethodInfo getter, MethodInfo setter, XamlSchemaContext schemaContext) |
|
||||
: base(attachablePropertyName, getter, setter, schemaContext) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected PropertyXamlMember(string name, XamlType declaringType, bool isAttachable) |
|
||||
: base(name, declaringType, isAttachable) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
private bool IsReadOnlyCollectionProperty |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
//Collection properties like:
|
|
||||
//MultiBinding.Bindings, Panel.Children, Control.Styles,
|
|
||||
//need to be readonly for Portable.Xaml
|
|
||||
//Collection properties like:
|
|
||||
//Grid.RowDefinitions, Grid.ColumnDefinitions
|
|
||||
//need to be set only once, and subsequent changes to be
|
|
||||
//added to collection
|
|
||||
//TODO: investigate is this good enough as solution ???
|
|
||||
//We can add some ReadOnyXamlPropertyCollectionAttribute to cover this
|
|
||||
return Type.IsCollection; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private bool HasCollectionTypeConverter |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return Type.IsCollection && Type.TypeConverter != null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
protected override MethodInfo LookupUnderlyingSetter() |
|
||||
{ |
|
||||
//if we have content property a list
|
|
||||
//we have some issues in portable.xaml
|
|
||||
//but if the list is read only, this is solving the problem
|
|
||||
|
|
||||
if (IsReadOnlyCollectionProperty && |
|
||||
!HasCollectionTypeConverter) |
|
||||
{ |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
return base.LookupUnderlyingSetter(); |
|
||||
} |
|
||||
|
|
||||
protected override XamlMemberInvoker LookupInvoker() |
|
||||
{ |
|
||||
//if we have a IList property and it has TypeConverter
|
|
||||
//Portable.xaml need to be able to set the value
|
|
||||
//but instead directly set new value we'll sync the lists
|
|
||||
bool updateListInsteadSet = HasCollectionTypeConverter; |
|
||||
return new PropertyInvoker(this) |
|
||||
{ |
|
||||
UpdateListInsteadSet = updateListInsteadSet |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
protected override bool LookupIsUnknown() => false; |
|
||||
|
|
||||
protected override XamlType LookupType() |
|
||||
{ |
|
||||
var propType = GetPropertyType(); |
|
||||
|
|
||||
if (propType != null) |
|
||||
{ |
|
||||
if (propType == typeof(IEnumerable)) |
|
||||
{ |
|
||||
//TODO: Portable.xaml is not handling well IEnumerable
|
|
||||
//let's threat IEnumerable property as list
|
|
||||
//revisit this when smarter solution is found
|
|
||||
propType = typeof(IList); |
|
||||
} |
|
||||
|
|
||||
return DeclaringType.SchemaContext.GetXamlType(propType); |
|
||||
} |
|
||||
|
|
||||
return base.LookupType(); |
|
||||
} |
|
||||
|
|
||||
protected virtual Type GetPropertyType() |
|
||||
{ |
|
||||
return (UnderlyingMember as PropertyInfo)?.PropertyType; |
|
||||
} |
|
||||
|
|
||||
private IList<XamlMember> _dependsOn; |
|
||||
|
|
||||
protected override IList<XamlMember> LookupDependsOn() |
|
||||
{ |
|
||||
if (_dependsOn == null) |
|
||||
{ |
|
||||
var attrib = UnderlyingMember.GetCustomAttribute<DependsOnAttribute>(true); |
|
||||
|
|
||||
if (attrib != null) |
|
||||
{ |
|
||||
var member = DeclaringType.GetMember(attrib.Name); |
|
||||
|
|
||||
_dependsOn = new XamlMember[] { member }; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
_dependsOn = base.LookupDependsOn(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return _dependsOn; |
|
||||
} |
|
||||
|
|
||||
private PropertyKey PropertyKey() |
|
||||
=> new PropertyKey(DeclaringType.UnderlyingType, Name); |
|
||||
|
|
||||
private class PropertyInvoker : XamlMemberInvoker |
|
||||
{ |
|
||||
public bool UpdateListInsteadSet { get; set; } = false; |
|
||||
|
|
||||
public PropertyInvoker(XamlMember member) : base(member) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override void SetValue(object instance, object value) |
|
||||
{ |
|
||||
//can't make it work to assign TypeConverter to Setter.Value
|
|
||||
//so we need it hard coded
|
|
||||
//TODO: try to assosiate TypeConverter with Setter.Value
|
|
||||
//and remove this lines
|
|
||||
if (instance is Setter && |
|
||||
Member.Name == nameof(Setter.Value) && |
|
||||
value is string) |
|
||||
{ |
|
||||
value = SetterValueTypeConverter.ConvertSetterValue(null, |
|
||||
Member.DeclaringType.SchemaContext, CultureInfo.InvariantCulture, |
|
||||
instance as Setter, |
|
||||
value); |
|
||||
} |
|
||||
|
|
||||
if (UpdateListInsteadSet && |
|
||||
value != null && |
|
||||
UpdateListInsteadSetValue(instance, value)) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
base.SetValue(instance, value); |
|
||||
} |
|
||||
|
|
||||
private bool UpdateListInsteadSetValue(object instance, object value) |
|
||||
{ |
|
||||
object old = GetValue(instance); |
|
||||
|
|
||||
if (Equals(old, value)) |
|
||||
{ |
|
||||
//don't set the same collection value
|
|
||||
return true; |
|
||||
} |
|
||||
else if (old is IList && value is IList) |
|
||||
{ |
|
||||
var oldList = (IList)old; |
|
||||
var curList = (IList)value; |
|
||||
|
|
||||
oldList.Clear(); |
|
||||
|
|
||||
foreach (object item in curList) |
|
||||
{ |
|
||||
oldList.Add(item); |
|
||||
} |
|
||||
|
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public class AvaloniaPropertyXamlMember : PropertyXamlMember |
|
||||
{ |
|
||||
private bool? _assignBinding; |
|
||||
|
|
||||
public bool AssignBinding => (bool)(_assignBinding ?? (_assignBinding = UnderlyingMember?.GetCustomAttribute<AssignBindingAttribute>() != null)); |
|
||||
|
|
||||
public AvaloniaProperty Property { get; } |
|
||||
|
|
||||
public AvaloniaPropertyXamlMember(AvaloniaProperty property, |
|
||||
PropertyInfo propertyInfo, |
|
||||
XamlSchemaContext schemaContext) : |
|
||||
base(propertyInfo, schemaContext) |
|
||||
{ |
|
||||
Property = property; |
|
||||
} |
|
||||
|
|
||||
public AvaloniaPropertyXamlMember(AvaloniaProperty property, XamlType type) : |
|
||||
base(property.Name, type, false) |
|
||||
{ |
|
||||
Property = property; |
|
||||
} |
|
||||
|
|
||||
protected AvaloniaPropertyXamlMember(AvaloniaProperty property, |
|
||||
string attachablePropertyName, |
|
||||
MethodInfo getter, MethodInfo setter, XamlSchemaContext schemaContext) |
|
||||
: base(attachablePropertyName, getter, setter, schemaContext) |
|
||||
{ |
|
||||
Property = property; |
|
||||
} |
|
||||
|
|
||||
protected override XamlMemberInvoker LookupInvoker() |
|
||||
{ |
|
||||
return new AvaloniaPropertyInvoker(this); |
|
||||
} |
|
||||
|
|
||||
protected override bool LookupIsReadOnly() |
|
||||
{ |
|
||||
return Property.IsReadOnly; |
|
||||
} |
|
||||
|
|
||||
protected override Type GetPropertyType() |
|
||||
{ |
|
||||
return Property.PropertyType; |
|
||||
} |
|
||||
|
|
||||
private class AvaloniaPropertyInvoker : XamlMemberInvoker |
|
||||
{ |
|
||||
public AvaloniaPropertyInvoker(XamlMember member) : base(member) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override void SetValue(object instance, object value) |
|
||||
{ |
|
||||
if (Property != null) |
|
||||
{ |
|
||||
var obj = ((IAvaloniaObject)instance); |
|
||||
if (value is IBinding) |
|
||||
{ |
|
||||
if (!Member.AssignBinding) |
|
||||
ApplyBinding(obj, (IBinding)value); |
|
||||
else |
|
||||
obj.SetValue(Property, value); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
obj.SetValue(Property, value); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
base.SetValue(instance, value); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public override object GetValue(object instance) |
|
||||
{ |
|
||||
if (Property != null && !Property.IsAttached) |
|
||||
{ |
|
||||
return ((IAvaloniaObject)instance).GetValue(Property); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return base.GetValue(instance); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void ApplyBinding(IAvaloniaObject obj, IBinding binding) |
|
||||
{ |
|
||||
var control = obj as IControl; |
|
||||
var property = Property; |
|
||||
|
|
||||
if (control != null && property != Control.DataContextProperty) |
|
||||
DelayedBinding.Add(control, property, binding); |
|
||||
else |
|
||||
obj.Bind(property, binding); |
|
||||
} |
|
||||
|
|
||||
private AvaloniaProperty Property => Member.Property; |
|
||||
|
|
||||
private new AvaloniaPropertyXamlMember Member => |
|
||||
(AvaloniaPropertyXamlMember)base.Member; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public class AvaloniaAttachedPropertyXamlMember : AvaloniaPropertyXamlMember |
|
||||
{ |
|
||||
public AvaloniaAttachedPropertyXamlMember(AvaloniaProperty property, |
|
||||
string attachablePropertyName, |
|
||||
MethodInfo getter, MethodInfo setter, |
|
||||
XamlSchemaContext schemaContext) |
|
||||
: base(property, attachablePropertyName, getter, setter, schemaContext) |
|
||||
{ |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,101 +0,0 @@ |
|||||
using Avalonia.Markup.Xaml.PortableXaml; |
|
||||
using Portable.Xaml.Markup; |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Reflection; |
|
||||
using System.ComponentModel; |
|
||||
|
|
||||
namespace Portable.Xaml.ComponentModel |
|
||||
{ |
|
||||
internal static class TypeDescriptorExtensions |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets the service from ITypeDescriptorContext
|
|
||||
/// usually in TypeConverter in xaml reader context
|
|
||||
/// examples:
|
|
||||
/// context.GetService<IXamlTypeResolver>()
|
|
||||
/// context.GetService<IXamlNamespaceResolver>()
|
|
||||
/// context.GetService<IXamlNameProvider>()
|
|
||||
/// context.GetService<INamespacePrefixLookup>()
|
|
||||
/// context.GetService<IXamlSchemaContextProvider>()
|
|
||||
/// context.GetService<IRootObjectProvider>()
|
|
||||
/// context.GetService<IProvideValueTarget>()
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">Service Type</typeparam>
|
|
||||
/// <param name="ctx">The TypeDescriptor context.</param>
|
|
||||
/// <returns></returns>
|
|
||||
public static T GetService<T>(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<IXamlTypeResolver>(); |
|
||||
|
|
||||
string name = string.IsNullOrEmpty(namespacePrefix) ? type : $"{namespacePrefix}:{type}"; |
|
||||
|
|
||||
return tr?.Resolve(name); |
|
||||
} |
|
||||
|
|
||||
public static T GetFirstAmbientValue<T>(this ITypeDescriptorContext ctx) where T : class |
|
||||
{ |
|
||||
var amb = ctx.GetService<IAmbientProvider>(); |
|
||||
var sc = ctx.GetService<IXamlSchemaContextProvider>().SchemaContext; |
|
||||
|
|
||||
// Because GetFirstParent uses XamlType.CanAssignTo it returns values that
|
|
||||
// aren't actually of the correct type. Use GetAllAmbientValues instead.
|
|
||||
return amb.GetAllAmbientValues(sc.GetXamlType(typeof(T))).OfType<T>().FirstOrDefault(); |
|
||||
} |
|
||||
|
|
||||
public static T GetLastOrDefaultAmbientValue<T>(this ITypeDescriptorContext ctx) where T : class |
|
||||
{ |
|
||||
return ctx.GetAllAmbientValues<T>().LastOrDefault() as T; |
|
||||
} |
|
||||
|
|
||||
public static IEnumerable<T> GetAllAmbientValues<T>(this ITypeDescriptorContext ctx) where T : class |
|
||||
{ |
|
||||
var amb = ctx.GetService<IAmbientProvider>(); |
|
||||
var sc = ctx.GetService<IXamlSchemaContextProvider>().SchemaContext; |
|
||||
|
|
||||
return amb.GetAllAmbientValues(sc.GetXamlType(typeof(T))).OfType<T>(); |
|
||||
} |
|
||||
|
|
||||
public static Uri GetBaseUri(this ITypeDescriptorContext ctx) |
|
||||
{ |
|
||||
return ctx.GetWriterSettings()?.Context?.BaseUri; |
|
||||
} |
|
||||
|
|
||||
public static Assembly GetLocalAssembly(this ITypeDescriptorContext ctx) |
|
||||
{ |
|
||||
return ctx.GetWriterSettings()?.Context?.LocalAssembly; |
|
||||
} |
|
||||
|
|
||||
public static AvaloniaXamlContext GetAvaloniaXamlContext(this ITypeDescriptorContext ctx) |
|
||||
{ |
|
||||
return ctx.GetWriterSettings()?.Context; |
|
||||
} |
|
||||
|
|
||||
public static XamlObjectWriterSettings WithContext(this XamlObjectWriterSettings settings, AvaloniaXamlContext context) |
|
||||
{ |
|
||||
return new AvaloniaXamlObjectWriterSettings(settings, context); |
|
||||
} |
|
||||
|
|
||||
private static AvaloniaXamlObjectWriterSettings GetWriterSettings(this ITypeDescriptorContext ctx) |
|
||||
{ |
|
||||
return ctx.GetService<IXamlObjectWriterFactory>().GetParentSettings() as AvaloniaXamlObjectWriterSettings; |
|
||||
} |
|
||||
|
|
||||
private class AvaloniaXamlObjectWriterSettings : XamlObjectWriterSettings |
|
||||
{ |
|
||||
public AvaloniaXamlObjectWriterSettings(XamlObjectWriterSettings settings, AvaloniaXamlContext context) |
|
||||
: base(settings) |
|
||||
{ |
|
||||
Context = context; |
|
||||
} |
|
||||
|
|
||||
public AvaloniaXamlContext Context { get; } |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1 +0,0 @@ |
|||||
Subproject commit ab5526173722b8988bc5ca3c03c8752ce89c0975 |
|
||||
@ -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.
|
|
||||
|
|
||||
namespace Avalonia.Markup.Xaml.Templates |
|
||||
{ |
|
||||
using Portable.Xaml; |
|
||||
using Portable.Xaml.ComponentModel; |
|
||||
using System.ComponentModel; |
|
||||
using System; |
|
||||
|
|
||||
public class TemplateLoader : XamlDeferringLoader |
|
||||
{ |
|
||||
public override object Load(XamlReader xamlReader, IServiceProvider serviceProvider) |
|
||||
{ |
|
||||
var tdc = (ITypeDescriptorContext)serviceProvider; |
|
||||
var ns = tdc.GetService<IXamlNamespaceResolver>(); |
|
||||
var ambientProvider = tdc.GetService<IAmbientProvider>(); |
|
||||
return new TemplateContent(ns.GetNamespacePrefixes(), xamlReader, ambientProvider); |
|
||||
} |
|
||||
|
|
||||
public override XamlReader Save(object value, IServiceProvider serviceProvider) |
|
||||
{ |
|
||||
return ((TemplateContent)value).List.GetReader(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,34 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Markup.Xaml |
||||
|
{ |
||||
|
public interface IProvideValueTarget |
||||
|
{ |
||||
|
object TargetObject { get; } |
||||
|
object TargetProperty { get; } |
||||
|
} |
||||
|
|
||||
|
public interface IRootObjectProvider |
||||
|
{ |
||||
|
object RootObject { get; } |
||||
|
} |
||||
|
|
||||
|
public interface IUriContext |
||||
|
{ |
||||
|
Uri BaseUri { get; set; } |
||||
|
} |
||||
|
|
||||
|
public interface IXamlTypeResolver |
||||
|
{ |
||||
|
Type Resolve (string qualifiedTypeName); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public class ConstructorArgumentAttribute : Attribute |
||||
|
{ |
||||
|
public ConstructorArgumentAttribute(string name) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,6 +1,8 @@ |
|||||
<Application xmlns="https://github.com/avaloniaui"> |
<Application xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
|
x:Class="Avalonia.DesignerSupport.TestApp.App"> |
||||
<Application.Styles> |
<Application.Styles> |
||||
<StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/> |
<StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/> |
||||
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/> |
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/> |
||||
</Application.Styles> |
</Application.Styles> |
||||
</Application> |
</Application> |
||||
|
|||||
@ -1,5 +1,7 @@ |
|||||
<Window xmlns="https://github.com/avaloniaui" |
<Window xmlns="https://github.com/avaloniaui" |
||||
xmlns:pages="clr-namespace:ControlCatalog.Pages;assembly=ControlCatalog" |
xmlns:pages="clr-namespace:ControlCatalog.Pages;assembly=ControlCatalog" |
||||
Title="TESTAPP"> |
Title="TESTAPP" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
|
x:Class="Avalonia.DesignerSupport.TestApp.MainWindow"> |
||||
<Button/> |
<Button/> |
||||
</Window> |
</Window> |
||||
|
|||||
Loading…
Reference in new issue