Pārlūkot izejas kodu

Support default clr-namespace when loading XAML from Uri or previewer

Nikita Tsukanov 7 gadi atpakaļ
vecāks
revīzija
fee5b4e594

+ 1 - 1
samples/ControlCatalog/MainWindow.xaml

@@ -1,6 +1,6 @@
 <Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
         Title="Avalonia Control Gallery"
         Icon="resm:ControlCatalog.Assets.test_icon.ico?assembly=ControlCatalog"
-        xmlns:local="clr-namespace:ControlCatalog;assembly=ControlCatalog">
+        xmlns:local="clr-namespace:ControlCatalog">
     <local:MainView/>
 </Window>

+ 2 - 0
src/Avalonia.Base/Platform/IAssetLoader.cs

@@ -43,5 +43,7 @@ namespace Avalonia.Platform
         /// The resource was not found.
         /// </exception>
         Stream Open(Uri uri, Uri baseUri = null);
+
+        Tuple<Assembly, Stream> OpenWithAssembly(Uri uri, Uri baseUri = null);
     }
 }

+ 3 - 1
src/Avalonia.DesignerSupport/DesignWindowLoader.cs

@@ -1,6 +1,7 @@
 using System;
 using System.IO;
 using System.Linq;
+using System.Reflection;
 using System.Text;
 using Avalonia.Controls;
 using Avalonia.Controls.Platform;
@@ -30,7 +31,8 @@ namespace Avalonia.DesignerSupport
                         new Uri("resm:Fake.xaml?assembly=" + Path.GetFileNameWithoutExtension(assemblyPath));
                 }
 
-                var loaded = loader.Load(stream, null, baseUri);
+                var localAsm = assemblyPath != null ? Assembly.LoadFile(assemblyPath) : null;
+                var loaded = loader.Load(stream, localAsm, null, baseUri);
                 var styles = loaded as Styles;
                 if (styles != null)
                 {

+ 1 - 1
src/Avalonia.DesignerSupport/DesignerAssist.cs

@@ -24,7 +24,7 @@ namespace Avalonia.DesignerSupport
 
                 var loader = new AvaloniaXamlLoader();
                 var baseLight = (IStyle)loader.Load(
-                    new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"));
+                    new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"), null);
                 Styles.Add(baseLight);
             }
         }

+ 0 - 1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@@ -30,7 +30,6 @@
             <Link>Properties\SharedAssemblyInfo.cs</Link>
         </Compile>
         <Compile Include="AvaloniaXamlLoaderPortableXaml.cs" />
-        <Compile Include="AvaloniaXamlLoader.cs" />
         <Compile Include="Converters\MatrixTypeConverter.cs" />
         <Compile Include="Converters\RectTypeConverter.cs" />
         <Compile Include="Converters\SetterValueTypeConverter.cs" />

+ 0 - 11
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@@ -1,11 +0,0 @@
-namespace Avalonia.Markup.Xaml
-{
-    public class AvaloniaXamlLoader : AvaloniaXamlLoaderPortableXaml
-    {
-        public static object Parse(string xaml)
-                => new AvaloniaXamlLoader().Load(xaml);
-
-        public static T Parse<T>(string xaml)
-                     => (T)Parse(xaml);
-    }
-}

+ 18 - 9
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs

@@ -17,7 +17,7 @@ namespace Avalonia.Markup.Xaml
     /// <summary>
     /// Loads XAML for a avalonia application.
     /// </summary>
-    public class AvaloniaXamlLoaderPortableXaml
+    public class AvaloniaXamlLoader
     {
         private readonly AvaloniaXamlSchemaContext _context = GetContext();
 
@@ -40,7 +40,7 @@ namespace Avalonia.Markup.Xaml
         /// <summary>
         /// Initializes a new instance of the <see cref="AvaloniaXamlLoader"/> class.
         /// </summary>
-        public AvaloniaXamlLoaderPortableXaml()
+        public AvaloniaXamlLoader()
         {
         }
 
@@ -89,7 +89,7 @@ namespace Avalonia.Markup.Xaml
                         initialize?.BeginInit();
                         try
                         {
-                            return Load(stream, rootInstance, uri);
+                            return Load(stream, type.Assembly, rootInstance, uri);
                         }
                         finally
                         {
@@ -125,11 +125,12 @@ namespace Avalonia.Markup.Xaml
                     "Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?");
             }
 
-            using (var stream = assetLocator.Open(uri, baseUri))
+            var asset = assetLocator.OpenWithAssembly(uri, baseUri);
+            using (var stream = asset.Item2)
             {
                 try
                 {
-                    return Load(stream, rootInstance, uri);
+                    return Load(stream, asset.Item1, rootInstance, uri);
                 }
                 catch (Exception e)
                 {
@@ -147,17 +148,18 @@ namespace Avalonia.Markup.Xaml
         /// 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, object rootInstance = null)
+        public object Load(string xaml, Assembly localAssembly, object rootInstance = null)
         {
             Contract.Requires<ArgumentNullException>(xaml != null);
 
             using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)))
             {
-                return Load(stream, rootInstance);
+                return Load(stream, localAssembly, rootInstance);
             }
         }
 
@@ -165,17 +167,18 @@ namespace Avalonia.Markup.Xaml
         /// 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, object rootInstance = null, Uri uri = null)
+        public object Load(Stream stream, Assembly localAssembly, object rootInstance = null, Uri uri = null)
         {
             var readerSettings = new XamlXmlReaderSettings()
             {
                 BaseUri = uri,
-                LocalAssembly = rootInstance?.GetType().GetTypeInfo().Assembly
+                LocalAssembly = localAssembly
             };
 
             var reader = new XamlXmlReader(stream, _context, readerSettings);
@@ -223,5 +226,11 @@ namespace Avalonia.Markup.Xaml
             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, Assembly localAssembly)
+            => (T)Parse(xaml, localAssembly);
     }
 }

+ 18 - 2
src/Shared/PlatformSupport/AssetLoader.cs

@@ -67,7 +67,20 @@ namespace Avalonia.Shared.PlatformSupport
         /// <exception cref="FileNotFoundException">
         /// The resource was not found.
         /// </exception>
-        public Stream Open(Uri uri, Uri baseUri = null)
+        public Stream Open(Uri uri, Uri baseUri = null) => OpenWithAssembly(uri, baseUri).Item2;
+        
+        /// <summary>
+        /// Opens the resource with the requested URI.
+        /// </summary>
+        /// <param name="uri">The URI.</param>
+        /// <param name="baseUri">
+        /// A base URI to use if <paramref name="uri"/> is relative.
+        /// </param>
+        /// <returns>An assembly (optional) and a stream containing the resource contents.</returns>
+        /// <exception cref="FileNotFoundException">
+        /// The resource was not found.
+        /// </exception>
+        public Tuple<Assembly, Stream> OpenWithAssembly(Uri uri, Uri baseUri = null)
         {
             var asset = GetAsset(uri, baseUri);
 
@@ -76,7 +89,7 @@ namespace Avalonia.Shared.PlatformSupport
                 throw new FileNotFoundException($"The resource {uri} could not be found.");
             }
 
-            return asset.GetStream();
+            return Tuple.Create(asset.Assembly, asset.GetStream());
         }
 
         private IAssetDescriptor GetAsset(Uri uri, Uri baseUri)
@@ -162,6 +175,7 @@ namespace Avalonia.Shared.PlatformSupport
         private interface IAssetDescriptor
         {
             Stream GetStream();
+            Assembly Assembly { get; }
         }
 
         private class AssemblyResourceDescriptor : IAssetDescriptor
@@ -179,6 +193,8 @@ namespace Avalonia.Shared.PlatformSupport
             {
                 return _asm.GetManifestResourceStream(_name);
             }
+
+            public Assembly Assembly => _asm;
         }
 
         private class AssemblyDescriptor

+ 5 - 0
tests/Avalonia.UnitTests/MockAssetLoader.cs

@@ -27,6 +27,11 @@ namespace Avalonia.UnitTests
         {
             return new MemoryStream(Encoding.UTF8.GetBytes(_assets[uri]));
         }
+        
+        public Tuple<Assembly, Stream> OpenWithAssembly(Uri uri, Uri baseUri = null)
+        {
+            return Tuple.Create((Assembly) null, Open(uri, baseUri));
+        }
 
         public void SetDefaultAssembly(Assembly asm)
         {