committed by
Andrey Kunchev
21 changed files with 1105 additions and 231 deletions
@ -0,0 +1,15 @@ |
|||
// 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; |
|||
|
|||
namespace Avalonia.Metadata |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the property that contains the object's content in markup.
|
|||
/// </summary>
|
|||
[AttributeUsage(AttributeTargets.Property)] |
|||
public class TemplateContentAttribute : Attribute |
|||
{ |
|||
} |
|||
} |
|||
@ -1,225 +1,11 @@ |
|||
// 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.IO; |
|||
using System.Reflection; |
|||
using System.Text; |
|||
using OmniXaml; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Markup.Xaml.Context; |
|||
using Avalonia.Markup.Xaml.Styling; |
|||
using OmniXaml.ObjectAssembler; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml.Data; |
|||
|
|||
namespace Avalonia.Markup.Xaml |
|||
namespace Avalonia.Markup.Xaml |
|||
{ |
|||
/// <summary>
|
|||
/// Loads XAML for a avalonia application.
|
|||
/// </summary>
|
|||
public class AvaloniaXamlLoader : XmlLoader |
|||
public class AvaloniaXamlLoader : |
|||
#if OMNIXAML
|
|||
AvaloniaXamlLoaderOmniXaml |
|||
#else
|
|||
AvaloniaXamlLoaderPortableXaml |
|||
#endif
|
|||
{ |
|||
private static AvaloniaParserFactory s_parserFactory; |
|||
private static IInstanceLifeCycleListener s_lifeCycleListener = new AvaloniaLifeCycleListener(); |
|||
private static Stack<Uri> s_uriStack = new Stack<Uri>(); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="AvaloniaXamlLoader"/> class.
|
|||
/// </summary>
|
|||
public AvaloniaXamlLoader() |
|||
: this(GetParserFactory()) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="AvaloniaXamlLoader"/> class.
|
|||
/// </summary>
|
|||
/// <param name="xamlParserFactory">The parser factory to use.</param>
|
|||
public AvaloniaXamlLoader(IParserFactory xamlParserFactory) |
|||
: base(xamlParserFactory) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the URI of the XAML file currently being loaded.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// TODO: Making this internal for now as I'm not sure that this is the correct
|
|||
/// thing to do, but its needed by <see cref="StyleInclude"/> 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?
|
|||
/// </remarks>
|
|||
internal static Uri UriContext => s_uriStack.Count > 0 ? s_uriStack.Peek() : null; |
|||
|
|||
/// <summary>
|
|||
/// Loads the XAML into a Avalonia component.
|
|||
/// </summary>
|
|||
/// <param name="obj">The object to load the XAML into.</param>
|
|||
public static void Load(object obj) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(obj != null); |
|||
|
|||
var loader = new AvaloniaXamlLoader(); |
|||
loader.Load(obj.GetType(), obj); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads the XAML for a type.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <param name="rootInstance">
|
|||
/// The optional instance into which the XAML should be loaded.
|
|||
/// </param>
|
|||
/// <returns>The loaded object.</returns>
|
|||
public object Load(Type type, object rootInstance = null) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(type != null); |
|||
|
|||
// HACK: Currently Visual Studio is forcing us to change the extension of xaml files
|
|||
// in certain situations, so we try to load .xaml and if that's not found we try .xaml.
|
|||
// Ideally we'd be able to use .xaml everywhere
|
|||
var assetLocator = AvaloniaLocator.Current.GetService<IAssetLoader>(); |
|||
|
|||
if (assetLocator == null) |
|||
{ |
|||
throw new InvalidOperationException( |
|||
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?"); |
|||
} |
|||
|
|||
foreach (var uri in GetUrisFor(type)) |
|||
{ |
|||
if (assetLocator.Exists(uri)) |
|||
{ |
|||
using (var stream = assetLocator.Open(uri)) |
|||
{ |
|||
var initialize = rootInstance as ISupportInitialize; |
|||
initialize?.BeginInit(); |
|||
return Load(stream, rootInstance, uri); |
|||
} |
|||
} |
|||
} |
|||
|
|||
throw new FileNotFoundException("Unable to find view for " + type.FullName); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads XAML from a URI.
|
|||
/// </summary>
|
|||
/// <param name="uri">The URI of the XAML file.</param>
|
|||
/// <param name="baseUri">
|
|||
/// A base URI to use if <paramref name="uri"/> is relative.
|
|||
/// </param>
|
|||
/// <param name="rootInstance">
|
|||
/// The optional instance into which the XAML should be loaded.
|
|||
/// </param>
|
|||
/// <returns>The loaded object.</returns>
|
|||
public object Load(Uri uri, Uri baseUri = null, object rootInstance = null) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(uri != null); |
|||
|
|||
var assetLocator = AvaloniaLocator.Current.GetService<IAssetLoader>(); |
|||
|
|||
if (assetLocator == null) |
|||
{ |
|||
throw new InvalidOperationException( |
|||
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?"); |
|||
} |
|||
|
|||
using (var stream = assetLocator.Open(uri, baseUri)) |
|||
{ |
|||
return Load(stream, rootInstance, uri); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads XAML from a string.
|
|||
/// </summary>
|
|||
/// <param name="xaml">The string containing the XAML.</param>
|
|||
/// <param name="rootInstance">
|
|||
/// The optional instance into which the XAML should be loaded.
|
|||
/// </param>
|
|||
/// <returns>The loaded object.</returns>
|
|||
public object Load(string xaml, object rootInstance = null) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(xaml != null); |
|||
|
|||
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml))) |
|||
{ |
|||
return Load(stream, rootInstance); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads XAML from a stream.
|
|||
/// </summary>
|
|||
/// <param name="stream">The stream containing the XAML.</param>
|
|||
/// <param name="rootInstance">
|
|||
/// The optional instance into which the XAML should be loaded.
|
|||
/// </param>
|
|||
/// <param name="uri">The URI of the XAML</param>
|
|||
/// <returns>The loaded object.</returns>
|
|||
public object Load(Stream stream, object rootInstance = null, Uri uri = null) |
|||
{ |
|||
try |
|||
{ |
|||
if (uri != null) |
|||
{ |
|||
s_uriStack.Push(uri); |
|||
} |
|||
|
|||
var result = base.Load(stream, new Settings |
|||
{ |
|||
RootInstance = rootInstance, |
|||
InstanceLifeCycleListener = s_lifeCycleListener, |
|||
ParsingContext = new Dictionary<string, object> |
|||
{ |
|||
{ "Uri", uri } |
|||
} |
|||
}); |
|||
|
|||
var topLevel = result as TopLevel; |
|||
|
|||
if (topLevel != null) |
|||
{ |
|||
DelayedBinding.ApplyBindings(topLevel); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
finally |
|||
{ |
|||
if (uri != null) |
|||
{ |
|||
s_uriStack.Pop(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static AvaloniaParserFactory GetParserFactory() |
|||
{ |
|||
if (s_parserFactory == null) |
|||
{ |
|||
s_parserFactory = new AvaloniaParserFactory(); |
|||
} |
|||
|
|||
return s_parserFactory; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the URI for a type.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <returns>The URI.</returns>
|
|||
private static IEnumerable<Uri> GetUrisFor(Type type) |
|||
{ |
|||
var asm = type.GetTypeInfo().Assembly.GetName().Name; |
|||
var typeName = type.FullName; |
|||
yield return new Uri("resm:" + typeName + ".xaml?assembly=" + asm); |
|||
yield return new Uri("resm:" + typeName + ".paml?assembly=" + asm); |
|||
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,227 @@ |
|||
// 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.IO; |
|||
using System.Reflection; |
|||
using System.Text; |
|||
using OmniXaml; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Markup.Xaml.Context; |
|||
using Avalonia.Markup.Xaml.Styling; |
|||
using OmniXaml.ObjectAssembler; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml.Data; |
|||
|
|||
namespace Avalonia.Markup.Xaml |
|||
{ |
|||
#if OMNIXAML
|
|||
/// <summary>
|
|||
/// Loads XAML for a avalonia application.
|
|||
/// </summary>
|
|||
public class AvaloniaXamlLoaderOmniXaml : XmlLoader |
|||
{ |
|||
private static AvaloniaParserFactory s_parserFactory; |
|||
private static IInstanceLifeCycleListener s_lifeCycleListener = new AvaloniaLifeCycleListener(); |
|||
private static Stack<Uri> s_uriStack = new Stack<Uri>(); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="AvaloniaXamlLoader"/> class.
|
|||
/// </summary>
|
|||
public AvaloniaXamlLoaderOmniXaml() |
|||
: this(GetParserFactory()) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="AvaloniaXamlLoader"/> class.
|
|||
/// </summary>
|
|||
/// <param name="xamlParserFactory">The parser factory to use.</param>
|
|||
public AvaloniaXamlLoaderOmniXaml(IParserFactory xamlParserFactory) |
|||
: base(xamlParserFactory) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the URI of the XAML file currently being loaded.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// TODO: Making this internal for now as I'm not sure that this is the correct
|
|||
/// thing to do, but its needed by <see cref="StyleInclude"/> 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?
|
|||
/// </remarks>
|
|||
internal static Uri UriContext => s_uriStack.Count > 0 ? s_uriStack.Peek() : null; |
|||
|
|||
/// <summary>
|
|||
/// Loads the XAML into a Avalonia component.
|
|||
/// </summary>
|
|||
/// <param name="obj">The object to load the XAML into.</param>
|
|||
public static void Load(object obj) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(obj != null); |
|||
|
|||
var loader = new AvaloniaXamlLoaderOmniXaml(); |
|||
loader.Load(obj.GetType(), obj); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads the XAML for a type.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <param name="rootInstance">
|
|||
/// The optional instance into which the XAML should be loaded.
|
|||
/// </param>
|
|||
/// <returns>The loaded object.</returns>
|
|||
public object Load(Type type, object rootInstance = null) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(type != null); |
|||
|
|||
// HACK: Currently Visual Studio is forcing us to change the extension of xaml files
|
|||
// in certain situations, so we try to load .xaml and if that's not found we try .xaml.
|
|||
// Ideally we'd be able to use .xaml everywhere
|
|||
var assetLocator = AvaloniaLocator.Current.GetService<IAssetLoader>(); |
|||
|
|||
if (assetLocator == null) |
|||
{ |
|||
throw new InvalidOperationException( |
|||
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?"); |
|||
} |
|||
|
|||
foreach (var uri in GetUrisFor(type)) |
|||
{ |
|||
if (assetLocator.Exists(uri)) |
|||
{ |
|||
using (var stream = assetLocator.Open(uri)) |
|||
{ |
|||
var initialize = rootInstance as ISupportInitialize; |
|||
initialize?.BeginInit(); |
|||
return Load(stream, rootInstance, uri); |
|||
} |
|||
} |
|||
} |
|||
|
|||
throw new FileNotFoundException("Unable to find view for " + type.FullName); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads XAML from a URI.
|
|||
/// </summary>
|
|||
/// <param name="uri">The URI of the XAML file.</param>
|
|||
/// <param name="baseUri">
|
|||
/// A base URI to use if <paramref name="uri"/> is relative.
|
|||
/// </param>
|
|||
/// <param name="rootInstance">
|
|||
/// The optional instance into which the XAML should be loaded.
|
|||
/// </param>
|
|||
/// <returns>The loaded object.</returns>
|
|||
public object Load(Uri uri, Uri baseUri = null, object rootInstance = null) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(uri != null); |
|||
|
|||
var assetLocator = AvaloniaLocator.Current.GetService<IAssetLoader>(); |
|||
|
|||
if (assetLocator == null) |
|||
{ |
|||
throw new InvalidOperationException( |
|||
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?"); |
|||
} |
|||
|
|||
using (var stream = assetLocator.Open(uri, baseUri)) |
|||
{ |
|||
return Load(stream, rootInstance, uri); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads XAML from a string.
|
|||
/// </summary>
|
|||
/// <param name="xaml">The string containing the XAML.</param>
|
|||
/// <param name="rootInstance">
|
|||
/// The optional instance into which the XAML should be loaded.
|
|||
/// </param>
|
|||
/// <returns>The loaded object.</returns>
|
|||
public object Load(string xaml, object rootInstance = null) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(xaml != null); |
|||
|
|||
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml))) |
|||
{ |
|||
return Load(stream, rootInstance); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads XAML from a stream.
|
|||
/// </summary>
|
|||
/// <param name="stream">The stream containing the XAML.</param>
|
|||
/// <param name="rootInstance">
|
|||
/// The optional instance into which the XAML should be loaded.
|
|||
/// </param>
|
|||
/// <param name="uri">The URI of the XAML</param>
|
|||
/// <returns>The loaded object.</returns>
|
|||
public object Load(Stream stream, object rootInstance = null, Uri uri = null) |
|||
{ |
|||
try |
|||
{ |
|||
if (uri != null) |
|||
{ |
|||
s_uriStack.Push(uri); |
|||
} |
|||
|
|||
var result = base.Load(stream, new Settings |
|||
{ |
|||
RootInstance = rootInstance, |
|||
InstanceLifeCycleListener = s_lifeCycleListener, |
|||
ParsingContext = new Dictionary<string, object> |
|||
{ |
|||
{ "Uri", uri } |
|||
} |
|||
}); |
|||
|
|||
var topLevel = result as TopLevel; |
|||
|
|||
if (topLevel != null) |
|||
{ |
|||
DelayedBinding.ApplyBindings(topLevel); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
finally |
|||
{ |
|||
if (uri != null) |
|||
{ |
|||
s_uriStack.Pop(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static AvaloniaParserFactory GetParserFactory() |
|||
{ |
|||
if (s_parserFactory == null) |
|||
{ |
|||
s_parserFactory = new AvaloniaParserFactory(); |
|||
} |
|||
|
|||
return s_parserFactory; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the URI for a type.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <returns>The URI.</returns>
|
|||
private static IEnumerable<Uri> GetUrisFor(Type type) |
|||
{ |
|||
var asm = type.GetTypeInfo().Assembly.GetName().Name; |
|||
var typeName = type.FullName; |
|||
yield return new Uri("resm:" + typeName + ".xaml?assembly=" + asm); |
|||
yield return new Uri("resm:" + typeName + ".paml?assembly=" + asm); |
|||
|
|||
} |
|||
} |
|||
#endif
|
|||
} |
|||
@ -0,0 +1,219 @@ |
|||
// 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.IO; |
|||
using System.Reflection; |
|||
using System.Text; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml.Context; |
|||
using Avalonia.Markup.Xaml.Data; |
|||
using Avalonia.Markup.Xaml.PortableXaml; |
|||
using Avalonia.Platform; |
|||
using Portable.Xaml; |
|||
|
|||
namespace Avalonia.Markup.Xaml |
|||
{ |
|||
/// <summary>
|
|||
/// Loads XAML for a avalonia application.
|
|||
/// </summary>
|
|||
public class AvaloniaXamlLoaderPortableXaml |
|||
{ |
|||
internal static readonly AvaloniaXamlSchemaContext _context |
|||
= new AvaloniaXamlSchemaContext(new AvaloniaRuntimeTypeProvider()); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="AvaloniaXamlLoader"/> class.
|
|||
/// </summary>
|
|||
public AvaloniaXamlLoaderPortableXaml() |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the URI of the XAML file currently being loaded.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// TODO: Making this internal for now as I'm not sure that this is the correct
|
|||
/// thing to do, but its needed by <see cref="StyleInclude"/> 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?
|
|||
/// </remarks>
|
|||
[Obsolete] |
|||
internal static Uri UriContext => s_uriStack.Count > 0 ? s_uriStack.Peek() : null; |
|||
|
|||
private static Stack<Uri> s_uriStack = new Stack<Uri>(); |
|||
|
|||
/// <summary>
|
|||
/// Loads the XAML into a Avalonia component.
|
|||
/// </summary>
|
|||
/// <param name="obj">The object to load the XAML into.</param>
|
|||
public static void Load(object obj) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(obj != null); |
|||
|
|||
var loader = new AvaloniaXamlLoader(); |
|||
loader.Load(obj.GetType(), obj); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads the XAML for a type.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <param name="rootInstance">
|
|||
/// The optional instance into which the XAML should be loaded.
|
|||
/// </param>
|
|||
/// <returns>The loaded object.</returns>
|
|||
public object Load(Type type, object rootInstance = null) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(type != null); |
|||
|
|||
// HACK: Currently Visual Studio is forcing us to change the extension of xaml files
|
|||
// in certain situations, so we try to load .xaml and if that's not found we try .xaml.
|
|||
// Ideally we'd be able to use .xaml everywhere
|
|||
var assetLocator = AvaloniaLocator.Current.GetService<IAssetLoader>(); |
|||
|
|||
if (assetLocator == null) |
|||
{ |
|||
throw new InvalidOperationException( |
|||
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?"); |
|||
} |
|||
|
|||
foreach (var uri in GetUrisFor(type)) |
|||
{ |
|||
if (assetLocator.Exists(uri)) |
|||
{ |
|||
using (var stream = assetLocator.Open(uri)) |
|||
{ |
|||
var initialize = rootInstance as ISupportInitialize; |
|||
initialize?.BeginInit(); |
|||
return Load(stream, type, rootInstance, uri); |
|||
} |
|||
} |
|||
} |
|||
|
|||
throw new FileNotFoundException("Unable to find view for " + type.FullName); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads XAML from a URI.
|
|||
/// </summary>
|
|||
/// <param name="uri">The URI of the XAML file.</param>
|
|||
/// <param name="baseUri">
|
|||
/// A base URI to use if <paramref name="uri"/> is relative.
|
|||
/// </param>
|
|||
/// <param name="rootInstance">
|
|||
/// The optional instance into which the XAML should be loaded.
|
|||
/// </param>
|
|||
/// <returns>The loaded object.</returns>
|
|||
public object Load(Uri uri, Uri baseUri = null, object rootInstance = null) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(uri != null); |
|||
|
|||
var assetLocator = AvaloniaLocator.Current.GetService<IAssetLoader>(); |
|||
|
|||
if (assetLocator == null) |
|||
{ |
|||
throw new InvalidOperationException( |
|||
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?"); |
|||
} |
|||
|
|||
using (var stream = assetLocator.Open(uri, baseUri)) |
|||
{ |
|||
return Load(stream, null, rootInstance, uri); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads XAML from a string.
|
|||
/// </summary>
|
|||
/// <param name="xaml">The string containing the XAML.</param>
|
|||
/// <param name="rootInstance">
|
|||
/// The optional instance into which the XAML should be loaded.
|
|||
/// </param>
|
|||
/// <returns>The loaded object.</returns>
|
|||
public object Load(string xaml, Type type = null, object rootInstance = null) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(xaml != null); |
|||
|
|||
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml))) |
|||
{ |
|||
return Load(stream, type, rootInstance); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads XAML from a stream.
|
|||
/// </summary>
|
|||
/// <param name="stream">The stream containing the XAML.</param>
|
|||
/// <param name="rootInstance">
|
|||
/// The optional instance into which the XAML should be loaded.
|
|||
/// </param>
|
|||
/// <param name="uri">The URI of the XAML</param>
|
|||
/// <returns>The loaded object.</returns>
|
|||
public object Load(Stream stream, Type type = null, object rootInstance = null, Uri uri = null) |
|||
{ |
|||
try |
|||
{ |
|||
if (uri != null) |
|||
{ |
|||
s_uriStack.Push(uri); |
|||
} |
|||
|
|||
var readerSettings = new XamlXmlReaderSettings(); |
|||
|
|||
if (rootInstance != null && type == null) |
|||
{ |
|||
type = rootInstance.GetType(); |
|||
} |
|||
|
|||
if (type != null) |
|||
{ |
|||
readerSettings.LocalAssembly = type.GetTypeInfo().Assembly; |
|||
} |
|||
|
|||
var reader = new XamlXmlReader(stream, _context, readerSettings); |
|||
object result = Load(reader, rootInstance); |
|||
|
|||
var topLevel = result as TopLevel; |
|||
|
|||
if (topLevel != null) |
|||
{ |
|||
DelayedBinding.ApplyBindings(topLevel); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
finally |
|||
{ |
|||
if (uri != null) |
|||
{ |
|||
s_uriStack.Pop(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal static object Load(XamlXmlReader reader, object instance) |
|||
{ |
|||
var writer = AvaloniaXamlObjectWriter.Create(_context, instance); |
|||
|
|||
XamlServices.Transform(reader, writer); |
|||
|
|||
return writer.Result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the URI for a type.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <returns>The URI.</returns>
|
|||
private static IEnumerable<Uri> GetUrisFor(Type type) |
|||
{ |
|||
var asm = type.GetTypeInfo().Assembly.GetName().Name; |
|||
var typeName = type.FullName; |
|||
yield return new Uri("resm:" + typeName + ".xaml?assembly=" + asm); |
|||
yield return new Uri("resm:" + typeName + ".paml?assembly=" + asm); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using Avalonia.Markup.Xaml.Templates; |
|||
using Portable.Xaml.ComponentModel; |
|||
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) |
|||
{ |
|||
object[] result = null; |
|||
|
|||
if (attributeType == typeof(pm.XamlDeferLoadAttribute)) |
|||
{ |
|||
var attr = GetXamlDeferLoadAttribute(inherit); |
|||
|
|||
if (attr != null) |
|||
{ |
|||
result = new object[] { attr }; |
|||
} |
|||
} |
|||
|
|||
if (result == null || result.Length == 0) |
|||
{ |
|||
var attr = _info.GetCustomAttributes(attributeType, inherit); |
|||
return (attr as object[]) ?? attr.ToArray(); |
|||
} |
|||
else |
|||
{ |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public bool IsDefined(Type attributeType, bool inherit) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
private readonly MemberInfo _info; |
|||
|
|||
private Attribute GetXamlDeferLoadAttribute(bool inherit) |
|||
{ |
|||
var result = _info.GetCustomAttributes(typeof(avm.TemplateContentAttribute), inherit) |
|||
.Cast<avm.TemplateContentAttribute>() |
|||
.FirstOrDefault(); |
|||
|
|||
if (result == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return new pm.XamlDeferLoadAttribute(typeof(TemplateLoader), typeof(TemplateContent)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,147 @@ |
|||
// 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.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(Control).GetTypeInfo().Assembly, |
|||
typeof(Style).GetTypeInfo().Assembly, |
|||
typeof(DataTemplate).GetTypeInfo().Assembly, |
|||
typeof(SolidColorBrush).GetTypeInfo().Assembly, |
|||
typeof(IValueConverter).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 = AvaloniaLocator.Current |
|||
.GetService<IRuntimePlatform>() |
|||
?.GetLoadedAssemblies(); |
|||
|
|||
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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
// 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 System; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using Avalonia.Metadata; |
|||
using Portable.Xaml.ComponentModel; |
|||
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) |
|||
{ |
|||
if (attributeType == typeof(pm.ContentPropertyAttribute)) |
|||
{ |
|||
var cp = GetContentPropertyAttribute(inherit); |
|||
|
|||
if (cp != null) |
|||
{ |
|||
return new object[] { cp }; |
|||
} |
|||
} |
|||
else if (attributeType == typeof(pm.RuntimeNamePropertyAttribute)) |
|||
{ |
|||
if (_namedType.IsAssignableFrom(_type.GetTypeInfo())) |
|||
{ |
|||
return new object[] |
|||
{ |
|||
new pm.RuntimeNamePropertyAttribute(nameof(INamed.Name)) |
|||
}; |
|||
} |
|||
} |
|||
|
|||
var attr = _type.GetTypeInfo().GetCustomAttributes(attributeType, inherit); |
|||
return (attr as object[]) ?? attr.ToArray(); |
|||
} |
|||
|
|||
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<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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
using Portable.Xaml; |
|||
|
|||
namespace Avalonia.Markup.Xaml.PortableXaml |
|||
{ |
|||
public class AvaloniaXamlObjectWriter : XamlObjectWriter |
|||
{ |
|||
public static AvaloniaXamlObjectWriter Create(XamlSchemaContext schemaContext, object instance) |
|||
{ |
|||
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); |
|||
} |
|||
|
|||
private AvaloniaXamlObjectWriter( |
|||
XamlSchemaContext schemaContext, |
|||
XamlObjectWriterSettings settings, |
|||
AvaloniaNameScope nameScope |
|||
) |
|||
: base(schemaContext, settings) |
|||
{ |
|||
_nameScope = nameScope; |
|||
} |
|||
|
|||
protected override void OnAfterBeginInit(object value) |
|||
{ |
|||
base.OnAfterBeginInit(value); |
|||
} |
|||
|
|||
protected override void OnAfterEndInit(object value) |
|||
{ |
|||
base.OnAfterEndInit(value); |
|||
} |
|||
|
|||
protected override void OnAfterProperties(object value) |
|||
{ |
|||
base.OnAfterProperties(value); |
|||
|
|||
//AfterEndInit is not called as it supports only
|
|||
//Portable.Xaml.ComponentModel.ISupportInitialize
|
|||
//and we have Avalonia.ISupportInitialize so we need some hacks
|
|||
_endEditValue = value; |
|||
} |
|||
|
|||
protected override void OnBeforeProperties(object value) |
|||
{ |
|||
//OnAfterBeginInit is not called as it supports only
|
|||
//Portable.Xaml.ComponentModel.ISupportInitialize
|
|||
//and we have Avalonia.ISupportInitialize so we need some hacks
|
|||
HandleBeginInit(value); |
|||
|
|||
base.OnBeforeProperties(value); |
|||
} |
|||
|
|||
public override void WriteEndObject() |
|||
{ |
|||
base.WriteEndObject(); |
|||
|
|||
//AfterEndInit is not called as it supports only
|
|||
//Portable.Xaml.ComponentModel.ISupportInitialize
|
|||
//and we have Avalonia.ISupportInitialize so we need some hacks
|
|||
HandleEndEdit(_endEditValue); |
|||
_endEditValue = null; |
|||
} |
|||
|
|||
private object _endEditValue; |
|||
|
|||
private AvaloniaNameScope _nameScope; |
|||
|
|||
private void HandleBeginInit(object value) |
|||
{ |
|||
(value as Avalonia.ISupportInitialize)?.BeginInit(); |
|||
} |
|||
|
|||
private void HandleEndEdit(object value) |
|||
{ |
|||
(value as Avalonia.ISupportInitialize)?.EndInit(); |
|||
} |
|||
|
|||
private void HandleFinished() |
|||
{ |
|||
if(_nameScope != null && Result != null) |
|||
{ |
|||
_nameScope.RegisterOnNameScope(Result); |
|||
} |
|||
} |
|||
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
if (disposing) |
|||
{ |
|||
HandleFinished(); |
|||
} |
|||
|
|||
base.Dispose(disposing); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using Avalonia.Markup.Xaml.Context; |
|||
using Portable.Xaml; |
|||
using Portable.Xaml.ComponentModel; |
|||
|
|||
namespace Avalonia.Markup.Xaml.PortableXaml |
|||
{ |
|||
internal class AvaloniaXamlSchemaContext : XamlSchemaContext |
|||
{ |
|||
public AvaloniaXamlSchemaContext(IRuntimeTypeProvider typeProvider) |
|||
: base(typeProvider.ReferencedAssemblies) |
|||
{ |
|||
_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; |
|||
} |
|||
} |
|||
|
|||
Type type = _avaloniaTypeProvider.FindType(xmlNamespace, xmlLocalName, genArgs); |
|||
|
|||
if (type == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return GetXamlType(type); |
|||
} |
|||
|
|||
protected override ICustomAttributeProvider GetCustomAttributeProvider(Type type) |
|||
=> new AvaloniaTypeAttributeProvider(type); |
|||
|
|||
protected override ICustomAttributeProvider GetCustomAttributeProvider(MemberInfo member) |
|||
=> new AvaloniaMemberAttributeProvider(member); |
|||
} |
|||
} |
|||
@ -1,9 +1,21 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<packages> |
|||
<package id="System.Collections" version="4.3.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.ComponentModel" version="4.3.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.ComponentModel.Annotations" version="4.1.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.ComponentModel.Primitives" version="4.3.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.ComponentModel.TypeConverter" version="4.3.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.Globalization" version="4.3.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.Reactive" version="3.0.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.Reactive.Core" version="3.0.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.Reactive.Interfaces" version="3.0.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.Reactive.Linq" version="3.0.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.Reactive.PlatformServices" version="3.0.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.Reflection" version="4.3.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.Reflection.Primitives" version="4.3.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.Runtime" version="4.3.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="portable45-net45+win8" /> |
|||
<package id="System.Threading" version="4.3.0" targetFramework="portable45-net45+win8" /> |
|||
</packages> |
|||
Loading…
Reference in new issue