|
|
@@ -1,11 +1,237 @@
|
|
|
-namespace Avalonia.Markup.Xaml
|
|
|
+// 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.Controls;
|
|
|
+using Avalonia.Markup.Xaml.Data;
|
|
|
+using Avalonia.Markup.Xaml.PortableXaml;
|
|
|
+using Avalonia.Platform;
|
|
|
+using Portable.Xaml;
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.IO;
|
|
|
+using System.Reflection;
|
|
|
+using System.Text;
|
|
|
+
|
|
|
+namespace Avalonia.Markup.Xaml
|
|
|
{
|
|
|
- public class AvaloniaXamlLoader : AvaloniaXamlLoaderPortableXaml
|
|
|
+ /// <summary>
|
|
|
+ /// Loads XAML for a avalonia application.
|
|
|
+ /// </summary>
|
|
|
+ public class AvaloniaXamlLoader
|
|
|
{
|
|
|
- public static object Parse(string xaml)
|
|
|
- => new AvaloniaXamlLoader().Load(xaml);
|
|
|
+ private readonly AvaloniaXamlSchemaContext _context = GetContext();
|
|
|
+
|
|
|
+ private static AvaloniaXamlSchemaContext GetContext()
|
|
|
+ {
|
|
|
+ var result = AvaloniaLocator.Current.GetService<AvaloniaXamlSchemaContext>();
|
|
|
+
|
|
|
+ if (result == null)
|
|
|
+ {
|
|
|
+ result = AvaloniaXamlSchemaContext.Create();
|
|
|
+
|
|
|
+ AvaloniaLocator.CurrentMutable
|
|
|
+ .Bind<AvaloniaXamlSchemaContext>()
|
|
|
+ .ToConstant(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Initializes a new instance of the <see cref="AvaloniaXamlLoader"/> class.
|
|
|
+ /// </summary>
|
|
|
+ public AvaloniaXamlLoader()
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <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();
|
|
|
+ try
|
|
|
+ {
|
|
|
+ return Load(stream, type.Assembly, rootInstance, uri);
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ initialize?.EndInit();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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?");
|
|
|
+ }
|
|
|
+
|
|
|
+ var asset = assetLocator.OpenAndGetAssembly(uri, baseUri);
|
|
|
+ using (var stream = asset.Item1)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ return Load(stream, asset.Item2, rootInstance, uri);
|
|
|
+ }
|
|
|
+ catch (Exception e)
|
|
|
+ {
|
|
|
+ var uriString = uri.ToString();
|
|
|
+ if (!uri.IsAbsoluteUri)
|
|
|
+ {
|
|
|
+ uriString = new Uri(baseUri, uri).AbsoluteUri;
|
|
|
+ }
|
|
|
+ throw new XamlLoadException("Error loading xaml at " + uriString, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Loads XAML from a string.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="xaml">The string containing the XAML.</param>
|
|
|
+ /// <param name="localAssembly">Default assembly for clr-namespace:</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, Assembly localAssembly = null, object rootInstance = null)
|
|
|
+ {
|
|
|
+ Contract.Requires<ArgumentNullException>(xaml != null);
|
|
|
+
|
|
|
+ using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)))
|
|
|
+ {
|
|
|
+ return Load(stream, localAssembly, rootInstance);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Loads XAML from a stream.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="stream">The stream containing the XAML.</param>
|
|
|
+ /// <param name="localAssembly">Default assembly for clr-namespace</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, Assembly localAssembly, object rootInstance = null, Uri uri = null)
|
|
|
+ {
|
|
|
+ var readerSettings = new XamlXmlReaderSettings()
|
|
|
+ {
|
|
|
+ BaseUri = uri,
|
|
|
+ LocalAssembly = localAssembly
|
|
|
+ };
|
|
|
+
|
|
|
+ var reader = new XamlXmlReader(stream, _context, readerSettings);
|
|
|
+
|
|
|
+ object result = LoadFromReader(
|
|
|
+ reader,
|
|
|
+ AvaloniaXamlContext.For(readerSettings, rootInstance));
|
|
|
+
|
|
|
+ var topLevel = result as TopLevel;
|
|
|
+
|
|
|
+ if (topLevel != null)
|
|
|
+ {
|
|
|
+ DelayedBinding.ApplyBindings(topLevel);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ internal static object LoadFromReader(XamlReader reader, AvaloniaXamlContext context = null, IAmbientProvider parentAmbientProvider = null)
|
|
|
+ {
|
|
|
+ var writer = AvaloniaXamlObjectWriter.Create(
|
|
|
+ reader.SchemaContext,
|
|
|
+ context,
|
|
|
+ parentAmbientProvider);
|
|
|
+
|
|
|
+ XamlServices.Transform(reader, writer);
|
|
|
+ writer.ApplyAllDelayedProperties();
|
|
|
+ return writer.Result;
|
|
|
+ }
|
|
|
+
|
|
|
+ internal static object LoadFromReader(XamlReader reader)
|
|
|
+ {
|
|
|
+ //return XamlServices.Load(reader);
|
|
|
+ return LoadFromReader(reader, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <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);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static object Parse(string xaml, Assembly localAssembly = null)
|
|
|
+ => new AvaloniaXamlLoader().Load(xaml, localAssembly);
|
|
|
|
|
|
- public static T Parse<T>(string xaml)
|
|
|
- => (T)Parse(xaml);
|
|
|
+ public static T Parse<T>(string xaml, Assembly localAssembly = null)
|
|
|
+ => (T)Parse(xaml, localAssembly);
|
|
|
}
|
|
|
}
|