Browse Source

initial portable xaml support attempt with datatemplate working and namespace support

pull/916/head
donandren 10 years ago
committed by Andrey Kunchev
parent
commit
d42e6d02cb
  1. 1
      src/Avalonia.Base/Avalonia.Base.csproj
  2. 15
      src/Avalonia.Base/Metadata/TemplateContent.cs
  3. 16
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  4. 230
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  5. 227
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderOmniXaml.cs
  6. 219
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs
  7. 2
      src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaObjectAssembler.cs
  8. 2
      src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaParserFactory.cs
  9. 2
      src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaTypeFeatureProvider.cs
  10. 69
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaMemberAttributeProvider.cs
  11. 56
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaNameScope.cs
  12. 147
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaRuntimeTypeProvider.cs
  13. 91
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaTypeAttributeProvider.cs
  14. 101
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs
  15. 70
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs
  16. 10
      src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
  17. 31
      src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
  18. 23
      src/Markup/Avalonia.Markup.Xaml/Templates/TemplateLoader.cs
  19. 1
      src/Markup/Avalonia.Markup.Xaml/packages.config
  20. 11
      src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
  21. 12
      src/Markup/Avalonia.Markup/packages.config

1
src/Avalonia.Base/Avalonia.Base.csproj

@ -70,6 +70,7 @@
<Compile Include="Logging\LogArea.cs" />
<Compile Include="Logging\LogEventLevel.cs" />
<Compile Include="Logging\Logger.cs" />
<Compile Include="Metadata\TemplateContent.cs" />
<Compile Include="Metadata\DependsOnAttribute.cs" />
<Compile Include="Metadata\ContentAttribute.cs" />
<Compile Include="AvaloniaDisposable.cs" />

15
src/Avalonia.Base/Metadata/TemplateContent.cs

@ -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
{
}
}

16
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -21,7 +21,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DefineConstants>TRACE;DEBUG;OMNIXAML_no</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NoWarn>CS1591</NoWarn>
@ -40,6 +40,13 @@
<Compile Include="..\..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="AvaloniaXamlLoaderPortableXaml.cs" />
<Compile Include="AvaloniaXamlLoader.cs" />
<Compile Include="PortableXaml\AvaloniaMemberAttributeProvider.cs" />
<Compile Include="PortableXaml\AvaloniaNameScope.cs" />
<Compile Include="PortableXaml\AvaloniaXamlObjectWriter.cs" />
<Compile Include="PortableXaml\AvaloniaRuntimeTypeProvider.cs" />
<Compile Include="PortableXaml\AvaloniaXamlSchemaContext.cs" />
<Compile Include="Context\NameScopeWrapper.cs" />
<Compile Include="Context\AvaloniaAttachableXamlMember.cs" />
<Compile Include="Context\AvaloniaLifeCycleListener.cs" />
@ -266,8 +273,9 @@
<Compile Include="OmniXAML\Source\OmniXaml\XmlParser.cs" />
<Compile Include="Parsers\SelectorGrammar.cs" />
<Compile Include="Parsers\SelectorParser.cs" />
<Compile Include="PortableXaml\AvaloniaTypeAttributeProvider.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="AvaloniaXamlLoader.cs" />
<Compile Include="AvaloniaXamlLoaderOmniXaml.cs" />
<Compile Include="Styling\StyleInclude.cs" />
<Compile Include="Templates\ControlTemplate.cs" />
<Compile Include="Templates\DataTemplate.cs" />
@ -322,6 +330,10 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="Portable.Xaml, Version=0.15.0.0, Culture=neutral, PublicKeyToken=ba4456acace7dab7, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Portable.Xaml.0.15.0\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+xamarinios10+MonoTouch10\Portable.Xaml.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Sprache, Version=2.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Sprache.2.1.0\lib\netstandard1.0\Sprache.dll</HintPath>
</Reference>

230
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@ -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);
}
}
}
}

227
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderOmniXaml.cs

@ -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
}

219
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs

@ -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);
}
}
}

2
src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaObjectAssembler.cs

@ -12,6 +12,7 @@ using System.Collections.ObjectModel;
namespace Avalonia.Markup.Xaml.Context
{
#if OMNIXAML
public class AvaloniaObjectAssembler : IObjectAssembler
{
private readonly TemplateHostingObjectAssembler objectAssembler;
@ -76,4 +77,5 @@ namespace Avalonia.Markup.Xaml.Context
return dict;
}
}
#endif
}

2
src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaParserFactory.cs

@ -8,6 +8,7 @@ using OmniXaml.Parsers.ProtoParser;
namespace Avalonia.Markup.Xaml.Context
{
#if OMNIXAML
public class AvaloniaParserFactory : IParserFactory
{
private readonly IRuntimeTypeSource runtimeTypeSource;
@ -38,4 +39,5 @@ namespace Avalonia.Markup.Xaml.Context
return new XmlParser(phaseParserKit);
}
}
#endif
}

2
src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaTypeFeatureProvider.cs

@ -171,7 +171,7 @@ namespace Avalonia.Markup.Xaml.Context
RegisterTypeConverter(typeof(SolidColorBrush), new BrushTypeConverter());
RegisterTypeConverter(typeof(Thickness), new ThicknessTypeConverter());
RegisterTypeConverter(typeof(TimeSpan), new TimeSpanTypeConverter());
RegisterTypeConverter(typeof(Uri), new UriTypeConverter());
RegisterTypeConverter(typeof(Uri), new Converters.UriTypeConverter());
RegisterTypeConverter(typeof(Cursor), new CursorTypeConverter());
RegisterTypeConverter(typeof(WindowIcon), new IconTypeConverter());
RegisterTypeConverter(typeof(FontWeight), new FontWeightConverter());

69
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaMemberAttributeProvider.cs

@ -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));
}
}
}

56
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaNameScope.cs

@ -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);
}
}
}
}
}

147
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaRuntimeTypeProvider.cs

@ -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;
}
}
}

91
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaTypeAttributeProvider.cs

@ -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;
}
}
}

101
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs

@ -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);
}
}
}

70
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs

@ -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);
}
}

10
src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs

@ -13,10 +13,14 @@ namespace Avalonia.Markup.Xaml.Templates
{
public Type DataType { get; set; }
//we need content to be object otherwise portable.xaml is crashing
[Content]
public TemplateContent Content { get; set; }
[TemplateContent]
public object Content { get; set; }
public bool SupportsRecycling => true;
private TemplateContent TemplateContent => (TemplateContent)Content;
public bool SupportsRecycling { get; set; }
public bool Match(object data)
{
@ -30,6 +34,6 @@ namespace Avalonia.Markup.Xaml.Templates
}
}
public IControl Build(object data) => Content.Load();
public IControl Build(object data) => TemplateContent.Load();
}
}

31
src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs

@ -2,13 +2,38 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Collections.Generic;
using OmniXaml;
using OmniXaml.ObjectAssembler;
using Avalonia.Controls;
using Avalonia.Markup.Xaml.Context;
namespace Avalonia.Markup.Xaml.Templates
{
#if !OMNIXAML
using Portable.Xaml;
public class TemplateContent
{
public TemplateContent()
{
}
public TemplateContent(XamlReader reader)
{
List = new XamlNodeList(reader.SchemaContext);
XamlServices.Transform(reader, List.Writer);
}
public XamlNodeList List { get; set; }
public IControl Load()
{
return (IControl)XamlServices.Load(List.GetReader());
}
}
#else
using OmniXaml;
using OmniXaml.ObjectAssembler;
public class TemplateContent
{
private readonly IEnumerable<Instruction> nodes;
@ -34,4 +59,6 @@ namespace Avalonia.Markup.Xaml.Templates
return (Control)assembler.Result;
}
}
#endif
}

23
src/Markup/Avalonia.Markup.Xaml/Templates/TemplateLoader.cs

@ -2,10 +2,29 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Collections.Generic;
using OmniXaml;
namespace Avalonia.Markup.Xaml.Templates
{
#if !OMNIXAML
using Portable.Xaml;
using System;
public class TemplateLoader : XamlDeferringLoader
{
public override object Load(XamlReader xamlReader, IServiceProvider serviceProvider)
{
return new TemplateContent(xamlReader);
}
public override XamlReader Save(object value, IServiceProvider serviceProvider)
{
return ((TemplateContent)value).List.GetReader();
}
}
#else
using OmniXaml;
public class TemplateLoader : IDeferredLoader
{
public object Load(IEnumerable<Instruction> nodes, IRuntimeTypeSource runtimeTypeSource)
@ -13,4 +32,6 @@ namespace Avalonia.Markup.Xaml.Templates
return new TemplateContent(nodes, runtimeTypeSource);
}
}
#endif
}

1
src/Markup/Avalonia.Markup.Xaml/packages.config

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Portable.Xaml" version="0.15.0" targetFramework="portable45-net45+win8" />
<package id="Sprache" version="2.1.0" targetFramework="portable45-net45+win8" />
<package id="System.Globalization" version="4.0.11" targetFramework="portable45-net45+win8" />
<package id="System.Linq" version="4.1.0" targetFramework="portable45-net45+win8" />

11
src/Markup/Avalonia.Markup/Avalonia.Markup.csproj

@ -114,6 +114,14 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="System.ComponentModel.Primitives, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\System.ComponentModel.Primitives.4.3.0\lib\netstandard1.0\System.ComponentModel.Primitives.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.ComponentModel.TypeConverter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\System.ComponentModel.TypeConverter.4.3.0\lib\netstandard1.0\System.ComponentModel.TypeConverter.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Reactive.Core, Version=3.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.1\System.Reactive.Core.dll</HintPath>
<Private>True</Private>
@ -134,6 +142,9 @@
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Folder Include="Common\" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

12
src/Markup/Avalonia.Markup/packages.config

@ -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…
Cancel
Save