csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
237 lines
8.0 KiB
237 lines
8.0 KiB
// 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.Collections.Generic;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using OmniXaml;
|
|
using Perspex.Platform;
|
|
|
|
namespace Perspex.Markup.Xaml
|
|
{
|
|
using Context;
|
|
using Controls;
|
|
using Data;
|
|
using OmniXaml.ObjectAssembler;
|
|
using System.Linq;
|
|
/// <summary>
|
|
/// Loads XAML for a perspex application.
|
|
/// </summary>
|
|
public class PerspexXamlLoader : XmlLoader
|
|
{
|
|
private static PerspexParserFactory s_parserFactory;
|
|
private static IInstanceLifeCycleListener s_lifeCycleListener = new PerspexLifeCycleListener();
|
|
private static Stack<Uri> s_uriStack = new Stack<Uri>();
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="PerspexXamlLoader"/> class.
|
|
/// </summary>
|
|
public PerspexXamlLoader()
|
|
: this(GetParserFactory())
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="PerspexXamlLoader"/> class.
|
|
/// </summary>
|
|
/// <param name="xamlParserFactory">The parser factory to use.</param>
|
|
public PerspexXamlLoader(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 needd 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 Perspex 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 PerspexXamlLoader();
|
|
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 = PerspexLocator.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 = PerspexLocator.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 PerspexParserFactory GetParserFactory()
|
|
{
|
|
if (s_parserFactory == null)
|
|
{
|
|
s_parserFactory = new PerspexParserFactory();
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace Perspex
|
|
{
|
|
public static class XamlObjectExtensions
|
|
{
|
|
public static TObject LoadFromXaml<TObject>(this TObject obj)
|
|
{
|
|
Markup.Xaml.PerspexXamlLoader.Load(obj);
|
|
return obj;
|
|
}
|
|
}
|
|
}
|
|
|