diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index 90550a12f2..acc4145dc5 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -70,6 +70,7 @@
+
diff --git a/src/Avalonia.Base/Metadata/TemplateContent.cs b/src/Avalonia.Base/Metadata/TemplateContent.cs
new file mode 100644
index 0000000000..ecc16b04a2
--- /dev/null
+++ b/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
+{
+ ///
+ /// Defines the property that contains the object's content in markup.
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public class TemplateContentAttribute : Attribute
+ {
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
index 4ed20629cf..2f33fb912d 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
@@ -21,7 +21,7 @@
full
false
bin\Debug\
- DEBUG;TRACE
+ TRACE;DEBUG;OMNIXAML_no
prompt
4
CS1591
@@ -40,6 +40,13 @@
Properties\SharedAssemblyInfo.cs
+
+
+
+
+
+
+
@@ -266,8 +273,9 @@
+
-
+
@@ -322,6 +330,10 @@
+
+ ..\..\..\packages\Portable.Xaml.0.15.0\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+xamarinios10+MonoTouch10\Portable.Xaml.dll
+ True
+
..\..\..\packages\Sprache.2.1.0\lib\netstandard1.0\Sprache.dll
diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
index 7b3d0036ef..978048bb9d 100644
--- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
+++ b/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
{
- ///
- /// Loads XAML for a avalonia application.
- ///
- 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 s_uriStack = new Stack();
-
- ///
- /// Initializes a new instance of the class.
- ///
- public AvaloniaXamlLoader()
- : this(GetParserFactory())
- {
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The parser factory to use.
- public AvaloniaXamlLoader(IParserFactory xamlParserFactory)
- : base(xamlParserFactory)
- {
- }
-
- ///
- /// Gets the URI of the XAML file currently being loaded.
- ///
- ///
- /// TODO: Making this internal for now as I'm not sure that this is the correct
- /// thing to do, but its needed by to get the URL of
- /// the currently loading XAML file, as we can't use the OmniXAML parsing context
- /// there. Maybe we need a way to inject OmniXAML context into the objects its
- /// constructing?
- ///
- internal static Uri UriContext => s_uriStack.Count > 0 ? s_uriStack.Peek() : null;
-
- ///
- /// Loads the XAML into a Avalonia component.
- ///
- /// The object to load the XAML into.
- public static void Load(object obj)
- {
- Contract.Requires(obj != null);
-
- var loader = new AvaloniaXamlLoader();
- loader.Load(obj.GetType(), obj);
- }
-
- ///
- /// Loads the XAML for a type.
- ///
- /// The type.
- ///
- /// The optional instance into which the XAML should be loaded.
- ///
- /// The loaded object.
- public object Load(Type type, object rootInstance = null)
- {
- Contract.Requires(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();
-
- 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);
- }
-
- ///
- /// Loads XAML from a URI.
- ///
- /// The URI of the XAML file.
- ///
- /// A base URI to use if is relative.
- ///
- ///
- /// The optional instance into which the XAML should be loaded.
- ///
- /// The loaded object.
- public object Load(Uri uri, Uri baseUri = null, object rootInstance = null)
- {
- Contract.Requires(uri != null);
-
- var assetLocator = AvaloniaLocator.Current.GetService();
-
- 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);
- }
- }
-
- ///
- /// Loads XAML from a string.
- ///
- /// The string containing the XAML.
- ///
- /// The optional instance into which the XAML should be loaded.
- ///
- /// The loaded object.
- public object Load(string xaml, object rootInstance = null)
- {
- Contract.Requires(xaml != null);
-
- using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)))
- {
- return Load(stream, rootInstance);
- }
- }
-
- ///
- /// Loads XAML from a stream.
- ///
- /// The stream containing the XAML.
- ///
- /// The optional instance into which the XAML should be loaded.
- ///
- /// The URI of the XAML
- /// The loaded object.
- 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
- {
- { "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;
- }
-
- ///
- /// Gets the URI for a type.
- ///
- /// The type.
- /// The URI.
- private static IEnumerable 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);
-
- }
}
-}
+}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderOmniXaml.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderOmniXaml.cs
new file mode 100644
index 0000000000..df734dfe39
--- /dev/null
+++ b/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
+ ///
+ /// Loads XAML for a avalonia application.
+ ///
+ public class AvaloniaXamlLoaderOmniXaml : XmlLoader
+ {
+ private static AvaloniaParserFactory s_parserFactory;
+ private static IInstanceLifeCycleListener s_lifeCycleListener = new AvaloniaLifeCycleListener();
+ private static Stack s_uriStack = new Stack();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AvaloniaXamlLoaderOmniXaml()
+ : this(GetParserFactory())
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The parser factory to use.
+ public AvaloniaXamlLoaderOmniXaml(IParserFactory xamlParserFactory)
+ : base(xamlParserFactory)
+ {
+ }
+
+ ///
+ /// Gets the URI of the XAML file currently being loaded.
+ ///
+ ///
+ /// TODO: Making this internal for now as I'm not sure that this is the correct
+ /// thing to do, but its needed by to get the URL of
+ /// the currently loading XAML file, as we can't use the OmniXAML parsing context
+ /// there. Maybe we need a way to inject OmniXAML context into the objects its
+ /// constructing?
+ ///
+ internal static Uri UriContext => s_uriStack.Count > 0 ? s_uriStack.Peek() : null;
+
+ ///
+ /// Loads the XAML into a Avalonia component.
+ ///
+ /// The object to load the XAML into.
+ public static void Load(object obj)
+ {
+ Contract.Requires(obj != null);
+
+ var loader = new AvaloniaXamlLoaderOmniXaml();
+ loader.Load(obj.GetType(), obj);
+ }
+
+ ///
+ /// Loads the XAML for a type.
+ ///
+ /// The type.
+ ///
+ /// The optional instance into which the XAML should be loaded.
+ ///
+ /// The loaded object.
+ public object Load(Type type, object rootInstance = null)
+ {
+ Contract.Requires(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();
+
+ 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);
+ }
+
+ ///
+ /// Loads XAML from a URI.
+ ///
+ /// The URI of the XAML file.
+ ///
+ /// A base URI to use if is relative.
+ ///
+ ///
+ /// The optional instance into which the XAML should be loaded.
+ ///
+ /// The loaded object.
+ public object Load(Uri uri, Uri baseUri = null, object rootInstance = null)
+ {
+ Contract.Requires(uri != null);
+
+ var assetLocator = AvaloniaLocator.Current.GetService();
+
+ 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);
+ }
+ }
+
+ ///
+ /// Loads XAML from a string.
+ ///
+ /// The string containing the XAML.
+ ///
+ /// The optional instance into which the XAML should be loaded.
+ ///
+ /// The loaded object.
+ public object Load(string xaml, object rootInstance = null)
+ {
+ Contract.Requires(xaml != null);
+
+ using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)))
+ {
+ return Load(stream, rootInstance);
+ }
+ }
+
+ ///
+ /// Loads XAML from a stream.
+ ///
+ /// The stream containing the XAML.
+ ///
+ /// The optional instance into which the XAML should be loaded.
+ ///
+ /// The URI of the XAML
+ /// The loaded object.
+ 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
+ {
+ { "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;
+ }
+
+ ///
+ /// Gets the URI for a type.
+ ///
+ /// The type.
+ /// The URI.
+ private static IEnumerable 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
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs
new file mode 100644
index 0000000000..d09787803a
--- /dev/null
+++ b/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
+{
+ ///
+ /// Loads XAML for a avalonia application.
+ ///
+ public class AvaloniaXamlLoaderPortableXaml
+ {
+ internal static readonly AvaloniaXamlSchemaContext _context
+ = new AvaloniaXamlSchemaContext(new AvaloniaRuntimeTypeProvider());
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AvaloniaXamlLoaderPortableXaml()
+ {
+ }
+
+ ///
+ /// Gets the URI of the XAML file currently being loaded.
+ ///
+ ///
+ /// TODO: Making this internal for now as I'm not sure that this is the correct
+ /// thing to do, but its needed by to get the URL of
+ /// the currently loading XAML file, as we can't use the OmniXAML parsing context
+ /// there. Maybe we need a way to inject OmniXAML context into the objects its
+ /// constructing?
+ ///
+ [Obsolete]
+ internal static Uri UriContext => s_uriStack.Count > 0 ? s_uriStack.Peek() : null;
+
+ private static Stack s_uriStack = new Stack();
+
+ ///
+ /// Loads the XAML into a Avalonia component.
+ ///
+ /// The object to load the XAML into.
+ public static void Load(object obj)
+ {
+ Contract.Requires(obj != null);
+
+ var loader = new AvaloniaXamlLoader();
+ loader.Load(obj.GetType(), obj);
+ }
+
+ ///
+ /// Loads the XAML for a type.
+ ///
+ /// The type.
+ ///
+ /// The optional instance into which the XAML should be loaded.
+ ///
+ /// The loaded object.
+ public object Load(Type type, object rootInstance = null)
+ {
+ Contract.Requires(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();
+
+ 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);
+ }
+
+ ///
+ /// Loads XAML from a URI.
+ ///
+ /// The URI of the XAML file.
+ ///
+ /// A base URI to use if is relative.
+ ///
+ ///
+ /// The optional instance into which the XAML should be loaded.
+ ///
+ /// The loaded object.
+ public object Load(Uri uri, Uri baseUri = null, object rootInstance = null)
+ {
+ Contract.Requires(uri != null);
+
+ var assetLocator = AvaloniaLocator.Current.GetService();
+
+ 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);
+ }
+ }
+
+ ///
+ /// Loads XAML from a string.
+ ///
+ /// The string containing the XAML.
+ ///
+ /// The optional instance into which the XAML should be loaded.
+ ///
+ /// The loaded object.
+ public object Load(string xaml, Type type = null, object rootInstance = null)
+ {
+ Contract.Requires(xaml != null);
+
+ using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)))
+ {
+ return Load(stream, type, rootInstance);
+ }
+ }
+
+ ///
+ /// Loads XAML from a stream.
+ ///
+ /// The stream containing the XAML.
+ ///
+ /// The optional instance into which the XAML should be loaded.
+ ///
+ /// The URI of the XAML
+ /// The loaded object.
+ 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;
+ }
+
+ ///
+ /// Gets the URI for a type.
+ ///
+ /// The type.
+ /// The URI.
+ private static IEnumerable 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaObjectAssembler.cs b/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaObjectAssembler.cs
index 1f5d259fc3..fefebdd18d 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaObjectAssembler.cs
+++ b/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
}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaParserFactory.cs b/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaParserFactory.cs
index 9e0c21edd4..69ab0727da 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaParserFactory.cs
+++ b/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
}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaTypeFeatureProvider.cs b/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaTypeFeatureProvider.cs
index fcb5703f83..7828934a62 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaTypeFeatureProvider.cs
+++ b/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());
diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaMemberAttributeProvider.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaMemberAttributeProvider.cs
new file mode 100644
index 0000000000..b878b860cc
--- /dev/null
+++ b/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()
+ .FirstOrDefault();
+
+ if (result == null)
+ {
+ return null;
+ }
+
+ return new pm.XamlDeferLoadAttribute(typeof(TemplateLoader), typeof(TemplateContent));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaNameScope.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaNameScope.cs
new file mode 100644
index 0000000000..6f855bafa1
--- /dev/null
+++ b/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 _names = new Dictionary();
+
+ 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);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaRuntimeTypeProvider.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaRuntimeTypeProvider.cs
new file mode 100644
index 0000000000..b6ffac69ab
--- /dev/null
+++ b/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;
+
+ public interface IRuntimeTypeProvider
+ {
+ Type FindType(string xamlNamespace, string name, Type[] genArgs);
+
+ IEnumerable ReferencedAssemblies { get; }
+ }
+
+ public class AvaloniaRuntimeTypeProvider : IRuntimeTypeProvider
+ {
+ private const string ClrNamespace = "clr-namespace:";
+ // private const string AvaloniaNs = "https://github.com/avaloniaui";
+
+ private static readonly IEnumerable 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> _namespaces = new Dictionary>();
+
+ private List _scanned = new List();
+
+ public IEnumerable 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 assemblies)
+ {
+ foreach (var assembly in assemblies)
+ {
+ var namespaces = assembly.GetCustomAttributes()
+ .Select(x => new { x.XmlNamespace, x.ClrNamespace })
+ .GroupBy(x => x.XmlNamespace);
+
+ foreach (var nsa in namespaces)
+ {
+ HashSet reg;
+
+ if (!_namespaces.TryGetValue(nsa.Key, out reg))
+ {
+ _namespaces[nsa.Key] = reg = new HashSet>();
+ }
+
+ foreach (var child in nsa)
+ {
+ reg.Add(new ClrNamespaceInfo(child.ClrNamespace, assembly));
+ }
+ }
+
+ _scanned.Add(assembly);
+ }
+ }
+
+ private void ScanNewAssemblies()
+ {
+ IEnumerable assemblies = AvaloniaLocator.Current
+ .GetService()
+ ?.GetLoadedAssemblies();
+
+ if (assemblies != null)
+ {
+ assemblies = assemblies.Except(_scanned);
+ ScanAssemblies(assemblies);
+ }
+ }
+
+ private Dictionary _typeCache = new Dictionary();
+
+ 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 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaTypeAttributeProvider.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaTypeAttributeProvider.cs
new file mode 100644
index 0000000000..ba470d521d
--- /dev/null
+++ b/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() != 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs
new file mode 100644
index 0000000000..287e1a140e
--- /dev/null
+++ b/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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs
new file mode 100644
index 0000000000..75ca5dff37
--- /dev/null
+++ b/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);
+ }
+}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
index 1feaed1b27..a1c77450e8 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
+++ b/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();
}
}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
index 325b9bf53f..695661a623 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
+++ b/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 nodes;
@@ -34,4 +59,6 @@ namespace Avalonia.Markup.Xaml.Templates
return (Control)assembler.Result;
}
}
+
+#endif
}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateLoader.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateLoader.cs
index 75f0329629..0f67615527 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateLoader.cs
+++ b/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 nodes, IRuntimeTypeSource runtimeTypeSource)
@@ -13,4 +32,6 @@ namespace Avalonia.Markup.Xaml.Templates
return new TemplateContent(nodes, runtimeTypeSource);
}
}
+
+#endif
}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/packages.config b/src/Markup/Avalonia.Markup.Xaml/packages.config
index 7a9353bb05..89e19db1dc 100644
--- a/src/Markup/Avalonia.Markup.Xaml/packages.config
+++ b/src/Markup/Avalonia.Markup.Xaml/packages.config
@@ -1,5 +1,6 @@
+
diff --git a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
index 5826346fc9..50d5886e10 100644
--- a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
+++ b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
@@ -114,6 +114,14 @@
+
+ ..\..\..\packages\System.ComponentModel.Primitives.4.3.0\lib\netstandard1.0\System.ComponentModel.Primitives.dll
+ True
+
+
+ ..\..\..\packages\System.ComponentModel.TypeConverter.4.3.0\lib\netstandard1.0\System.ComponentModel.TypeConverter.dll
+ True
+
..\..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.1\System.Reactive.Core.dll
True
@@ -134,6 +142,9 @@
+
+
+