diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 9e9bd33046..01717af372 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -53,6 +53,8 @@ + + diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs index 84d65e20b3..f23ce9d8b3 100644 --- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs +++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using Avalonia.Controls; -using Avalonia.Markup.Xaml.Context; using Avalonia.Markup.Xaml.Data; using Avalonia.Markup.Xaml.PortableXaml; using Avalonia.Platform; @@ -45,21 +44,6 @@ namespace Avalonia.Markup.Xaml { } - /// - /// Gets the URI of the XAML file currently being loaded. - /// - /// - /// TODO: Making this internal for now as I'm not sure that this is the correct - /// thing to do, but its needed by to get the URL of - /// the currently loading XAML file, as we can't use the OmniXAML parsing context - /// there. Maybe we need a way to inject OmniXAML context into the objects its - /// constructing? - /// - [Obsolete] - internal static Uri UriContext => s_uriStack.Count > 0 ? s_uriStack.Peek() : null; - - private static Stack s_uriStack = new Stack(); - /// /// Loads the XAML into a Avalonia component. /// @@ -176,47 +160,34 @@ namespace Avalonia.Markup.Xaml /// The loaded object. public object Load(Stream stream, object rootInstance = null, Uri uri = null) { - try + var readerSettings = new XamlXmlReaderSettings() { - if (uri != null) - { - s_uriStack.Push(uri); - } - - var readerSettings = new XamlXmlReaderSettings(); - - Type type = rootInstance?.GetType(); - - if (type != null) - { - readerSettings.LocalAssembly = type.GetTypeInfo().Assembly; - } + BaseUri = uri, + LocalAssembly = rootInstance?.GetType().GetTypeInfo().Assembly + }; - var reader = new XamlXmlReader(stream, _context, readerSettings); + var reader = new XamlXmlReader(stream, _context, readerSettings); - object result = LoadFromReader(reader, rootInstance); + object result = LoadFromReader(reader, rootInstance, readerSettings); - var topLevel = result as TopLevel; + var topLevel = result as TopLevel; - if (topLevel != null) - { - DelayedBinding.ApplyBindings(topLevel); - } - - return result; - } - finally + if (topLevel != null) { - if (uri != null) - { - s_uriStack.Pop(); - } + DelayedBinding.ApplyBindings(topLevel); } + + return result; } - internal static object LoadFromReader(XamlReader reader, object instance) + internal static object LoadFromReader(XamlReader reader, + object instance, + AvaloniaXamlContext context = null) { - var writer = AvaloniaXamlObjectWriter.Create(reader.SchemaContext, instance); + var writer = AvaloniaXamlObjectWriter.Create( + reader.SchemaContext, + instance, + context); XamlServices.Transform(reader, writer); diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleIncludeExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleIncludeExtension.cs new file mode 100644 index 0000000000..1355ba0597 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleIncludeExtension.cs @@ -0,0 +1,29 @@ +// 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.Markup.Xaml.Styling; +using Avalonia.Styling; +using Portable.Xaml; +using Portable.Xaml.ComponentModel; +using Portable.Xaml.Markup; +using System; + +namespace Avalonia.Markup.Xaml.MarkupExtensions +{ + [MarkupExtensionReturnType(typeof(IStyle))] + public class StyleIncludeExtension : MarkupExtension + { + public StyleIncludeExtension() + { + } + + public override object ProvideValue(IServiceProvider serviceProvider) + { + var tdc = (ITypeDescriptorContext)serviceProvider; + + return new StyleInclude(tdc.GetBaseUri()) { Source = Source }; + } + + public Uri Source { get; set; } + } +} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlContext.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlContext.cs new file mode 100644 index 0000000000..7196b841b0 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlContext.cs @@ -0,0 +1,24 @@ +using Portable.Xaml; +using System; +using System.Reflection; + +namespace Avalonia.Markup.Xaml.PortableXaml +{ + public class AvaloniaXamlContext + { + internal AvaloniaXamlContext(Uri baseUri, Assembly localAssembly) + { + LocalAssembly = localAssembly; + BaseUri = baseUri; + } + + public Assembly LocalAssembly { get; } + + public Uri BaseUri { get; } + + public static implicit operator AvaloniaXamlContext(XamlXmlReaderSettings sett) + { + return new AvaloniaXamlContext(sett.BaseUri, sett.LocalAssembly); + } + } +} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs index 53b3a8585a..c011be67cb 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs @@ -1,5 +1,6 @@ using Avalonia.Data; using Portable.Xaml; +using Portable.Xaml.ComponentModel; using System.Collections.Generic; using System.Linq; @@ -7,15 +8,23 @@ namespace Avalonia.Markup.Xaml.PortableXaml { public class AvaloniaXamlObjectWriter : XamlObjectWriter { - public static AvaloniaXamlObjectWriter Create(XamlSchemaContext schemaContext, object instance) + public static AvaloniaXamlObjectWriter Create( + XamlSchemaContext schemaContext, + object instance, + AvaloniaXamlContext context) { - var writerSettings = new XamlObjectWriterSettings(); var nameScope = new AvaloniaNameScope { Instance = instance }; - writerSettings.ExternalNameScope = nameScope; - writerSettings.RegisterNamesOnExternalNamescope = true; - writerSettings.RootObjectInstance = instance; - return new AvaloniaXamlObjectWriter(schemaContext, writerSettings, nameScope); + var writerSettings = new XamlObjectWriterSettings() + { + ExternalNameScope = nameScope, + RegisterNamesOnExternalNamescope = true, + RootObjectInstance = instance + }; + + return new AvaloniaXamlObjectWriter(schemaContext, + writerSettings.WithContext(context), + nameScope); } private readonly DelayedValuesHelper _delayedValuesHelper = new DelayedValuesHelper(); diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs index 85731abf37..1e7d78de72 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs @@ -1,12 +1,14 @@ using Avalonia.Data; using Avalonia.Markup.Xaml.Context; +using Avalonia.Markup.Xaml.Data; +using Avalonia.Markup.Xaml.MarkupExtensions; +using Avalonia.Markup.Xaml.Styling; using Portable.Xaml; using Portable.Xaml.ComponentModel; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; -using am = Avalonia.Metadata; namespace Avalonia.Markup.Xaml.PortableXaml { @@ -19,7 +21,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml private AvaloniaXamlSchemaContext(IRuntimeTypeProvider typeProvider) //better not set the references assemblies - //TODO: check this on iOS + //TODO: check this on iOS //: base(typeProvider.ReferencedAssemblies) { Contract.Requires(typeProvider != null); @@ -150,16 +152,27 @@ namespace Avalonia.Markup.Xaml.PortableXaml return result; } + private static readonly Dictionary _wellKnownExtensionTypes = new Dictionary() + { + { typeof(Binding), typeof(BindingExtension) }, + { typeof(StyleInclude), typeof(StyleIncludeExtension) }, + }; + private XamlType GetAvaloniaXamlType(Type type) { + Type extType; + + _wellKnownExtensionTypes.TryGetValue(type, out extType); + if (typeof(IBinding).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - return BindingXamlType.Create(type, this); + return new BindingXamlType(extType ?? type, this); } - if (typeof(AvaloniaObject).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) + if (extType != null || + typeof(AvaloniaObject).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - return new AvaloniaXamlType(type, this); + return new AvaloniaXamlType(extType ?? type, this); } return null; diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs index 507631d053..53374711f6 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs @@ -1,7 +1,6 @@ using Avalonia.Controls; using Avalonia.Data; using Avalonia.Markup.Xaml.Data; -using Avalonia.Markup.Xaml.MarkupExtensions; using Avalonia.Metadata; using Avalonia.Styling; using Portable.Xaml; @@ -48,24 +47,13 @@ namespace Avalonia.Markup.Xaml.PortableXaml public class BindingXamlType : XamlType { - public static BindingXamlType Create(Type type, XamlSchemaContext schemaContext) - { - if (type == typeof(Binding)) - { - //in xaml we need to use the extension - type = typeof(BindingExtension); - } - - return new BindingXamlType(type, schemaContext); - } - private static List _notAssignable = new List { typeof (IXmlSerializable) }; - private BindingXamlType(Type underlyingType, XamlSchemaContext schemaContext) : + public BindingXamlType(Type underlyingType, XamlSchemaContext schemaContext) : base(underlyingType, schemaContext) { } @@ -194,7 +182,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml { //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 + //TODO: try to assosiate TypeConverter with Setter.Value //and remove this lines if (instance is Setter && Member.Name == nameof(Setter.Value) && diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs index 71330ddbbd..ebe1264856 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs @@ -1,7 +1,9 @@ -using System; +using Avalonia.Markup.Xaml.PortableXaml; using Portable.Xaml.Markup; -using System.Linq; +using System; using System.Collections.Generic; +using System.Linq; +using System.Reflection; namespace Portable.Xaml.ComponentModel { @@ -56,5 +58,41 @@ namespace Portable.Xaml.ComponentModel return amb.GetAllAmbientValues(sc.GetXamlType(typeof(T))).OfType(); } + + 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().GetParentSettings() as AvaloniaXamlObjectWriterSettings; + } + + private class AvaloniaXamlObjectWriterSettings : XamlObjectWriterSettings + { + public AvaloniaXamlObjectWriterSettings(XamlObjectWriterSettings settings, AvaloniaXamlContext context) + : base(settings) + { + Context = context; + } + + public AvaloniaXamlContext Context { get; } + } } } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index c6c69973cc..04bf28f1ee 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -1,8 +1,8 @@ // 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 Avalonia.Styling; +using System; namespace Avalonia.Markup.Xaml.Styling { @@ -17,6 +17,9 @@ namespace Avalonia.Markup.Xaml.Styling /// /// Initializes a new instance of the class. /// + /// +#if OMNIXAML + public StyleInclude() { // StyleInclude will usually be loaded from XAML and its URI can be relative to the @@ -25,6 +28,15 @@ namespace Avalonia.Markup.Xaml.Styling _baseUri = AvaloniaXamlLoader.UriContext; } +#else + + public StyleInclude(Uri baseUri) + { + _baseUri = baseUri; + } + +#endif + /// /// Gets or sets the source URL. /// @@ -68,4 +80,4 @@ namespace Avalonia.Markup.Xaml.Styling return Loaded.FindResource(name); } } -} +} \ No newline at end of file