Browse Source

Merge branch 'omnixaml-update'

pull/525/merge
Steven Kirk 10 years ago
parent
commit
aeba2db8c0
  1. 3
      .gitmodules
  2. 3
      Perspex.sln
  3. 19
      samples/ControlCatalog.Desktop/Program.cs
  4. 21
      src/Markup/Perspex.Markup.Xaml/Context/PerspexObjectAssembler.cs
  5. 10
      src/Markup/Perspex.Markup.Xaml/Converters/BitmapTypeConverter.cs
  6. 2
      src/Markup/Perspex.Markup.Xaml/MarkupExtensions/StaticExtension.cs
  7. 2
      src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TypeExtension.cs
  8. 2
      src/Markup/Perspex.Markup.Xaml/OmniXAML
  9. 28
      src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj
  10. 66
      src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs
  11. 14
      src/Markup/Perspex.Markup.Xaml/Styling/StyleInclude.cs
  12. 1
      src/Markup/Perspex.Markup.Xaml/glass
  13. 1
      src/Markup/Perspex.Markup.Xaml/packages.config
  14. 11
      src/Perspex.Base/Platform/IAssetLoader.cs
  15. 187
      src/Shared/PlatformSupport/AssetLoader.cs

3
.gitmodules

@ -8,3 +8,6 @@
[submodule "src/Markup/Perspex.Markup.Xaml/OmniXAML"] [submodule "src/Markup/Perspex.Markup.Xaml/OmniXAML"]
path = src/Markup/Perspex.Markup.Xaml/OmniXAML path = src/Markup/Perspex.Markup.Xaml/OmniXAML
url = https://github.com/Perspex/OmniXAML.git url = https://github.com/Perspex/OmniXAML.git
[submodule "src/Markup/Perspex.Markup.Xaml/glass"]
path = src/Markup/Perspex.Markup.Xaml/glass
url = https://github.com/SuperJMN/glass

3
Perspex.sln

@ -60,9 +60,6 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Markup.Xaml", "src\Markup\Perspex.Markup.Xaml\Perspex.Markup.Xaml.csproj", "{3E53A01A-B331-47F3-B828-4A5717E77A24}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Markup.Xaml", "src\Markup\Perspex.Markup.Xaml\Perspex.Markup.Xaml.csproj", "{3E53A01A-B331-47F3-B828-4A5717E77A24}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{9B9E3891-2366-4253-A952-D08BCEB71098}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{9B9E3891-2366-4253-A952-D08BCEB71098}"
ProjectSection(SolutionItems) = preProject
samples\ControlCatalog\ControlCatalog.csproj = samples\ControlCatalog\ControlCatalog.csproj
EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication", "samples\TestApplication\TestApplication.csproj", "{E3A1060B-50D0-44E8-88B6-F44EF2E5BD72}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication", "samples\TestApplication\TestApplication.csproj", "{E3A1060B-50D0-44E8-88B6-F44EF2E5BD72}"
ProjectSection(ProjectDependencies) = postProject ProjectSection(ProjectDependencies) = postProject

19
samples/ControlCatalog.Desktop/Program.cs

@ -6,24 +6,6 @@ using Perspex;
using System.Reflection; using System.Reflection;
using Perspex.Platform; using Perspex.Platform;
// Not sure where the best home for this is
namespace Perspex
{
public static class SharedApplicationExtensions
{
// For true Portable apps we need to select the PCL assembly NOT the host platform exe. Unfortunately
// Win32 subsystem registers one by default (the wrong one) and so this can override that.
//
public static AppT UseAssetAssembly<AppT>(this AppT app, Assembly assembly) where AppT : Application
{
// Asset loading searches our own assembly?
PerspexLocator.CurrentMutable.GetService<IAssetLoader>().SetDefaultAssembly(assembly);
return app;
}
}
}
namespace ControlCatalog namespace ControlCatalog
{ {
internal class Program internal class Program
@ -34,7 +16,6 @@ namespace ControlCatalog
new App() new App()
.ConfigureRenderSystem(args) .ConfigureRenderSystem(args)
.UseAssetAssembly(typeof(App).Assembly)
.LoadFromXaml() .LoadFromXaml()
.RunWithMainWindow<MainWindow>(); .RunWithMainWindow<MainWindow>();
} }

21
src/Markup/Perspex.Markup.Xaml/Context/PerspexObjectAssembler.cs

@ -7,6 +7,8 @@ using OmniXaml.ObjectAssembler;
using OmniXaml.ObjectAssembler.Commands; using OmniXaml.ObjectAssembler.Commands;
using OmniXaml.TypeConversion; using OmniXaml.TypeConversion;
using Perspex.Markup.Xaml.Templates; using Perspex.Markup.Xaml.Templates;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Perspex.Markup.Xaml.Context namespace Perspex.Markup.Xaml.Context
{ {
@ -27,7 +29,8 @@ namespace Perspex.Markup.Xaml.Context
mapping.Map<TreeDataTemplate>(x => x.Content, new TemplateLoader()); mapping.Map<TreeDataTemplate>(x => x.Content, new TemplateLoader());
mapping.Map<ItemsPanelTemplate>(x => x.Content, new TemplateLoader()); mapping.Map<ItemsPanelTemplate>(x => x.Content, new TemplateLoader());
var valueContext = new ValueContext(typeSource, topDownValueContext); var parsingDictionary = GetDictionary(settings);
var valueContext = new ValueContext(typeSource, topDownValueContext, parsingDictionary);
assembler = new ObjectAssembler(typeSource, valueContext, settings); assembler = new ObjectAssembler(typeSource, valueContext, settings);
objectAssembler = new TemplateHostingObjectAssembler(assembler, mapping); objectAssembler = new TemplateHostingObjectAssembler(assembler, mapping);
} }
@ -55,5 +58,21 @@ namespace Perspex.Markup.Xaml.Context
{ {
objectAssembler.OverrideInstance(instance); objectAssembler.OverrideInstance(instance);
} }
private static IReadOnlyDictionary<string, object> GetDictionary(Settings settings)
{
IReadOnlyDictionary<string, object> dict;
if (settings != null)
{
dict = settings.ParsingContext;
}
else
{
dict = new ReadOnlyDictionary<string, object>(new Dictionary<string, object>());
}
return dict;
}
} }
} }

10
src/Markup/Perspex.Markup.Xaml/Converters/BitmapTypeConverter.cs

@ -24,6 +24,7 @@ namespace Perspex.Markup.Xaml.Converters
public object ConvertFrom(IValueContext context, CultureInfo culture, object value) public object ConvertFrom(IValueContext context, CultureInfo culture, object value)
{ {
var uri = new Uri((string)value, UriKind.RelativeOrAbsolute); var uri = new Uri((string)value, UriKind.RelativeOrAbsolute);
var baseUri = GetBaseUri(context);
var scheme = uri.IsAbsoluteUri ? uri.Scheme : "file"; var scheme = uri.IsAbsoluteUri ? uri.Scheme : "file";
switch (scheme) switch (scheme)
@ -32,7 +33,7 @@ namespace Perspex.Markup.Xaml.Converters
return new Bitmap((string)value); return new Bitmap((string)value);
default: default:
var assets = PerspexLocator.Current.GetService<IAssetLoader>(); var assets = PerspexLocator.Current.GetService<IAssetLoader>();
return new Bitmap(assets.Open(uri)); return new Bitmap(assets.Open(uri, baseUri));
} }
} }
@ -40,5 +41,12 @@ namespace Perspex.Markup.Xaml.Converters
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
private Uri GetBaseUri(IValueContext context)
{
object result;
context.ParsingDictionary.TryGetValue("Uri", out result);
return result as Uri;
}
} }
} }

2
src/Markup/Perspex.Markup.Xaml/MarkupExtensions/StaticExtension.cs

@ -24,7 +24,7 @@ namespace Perspex.Markup.Xaml.MarkupExtensions
public override object ProvideValue(MarkupExtensionContext markupExtensionContext) public override object ProvideValue(MarkupExtensionContext markupExtensionContext)
{ {
var typeRepository = markupExtensionContext.TypeRepository; var typeRepository = markupExtensionContext.ValueContext.TypeRepository;
var typeAndMember = GetTypeAndMember(Identifier); var typeAndMember = GetTypeAndMember(Identifier);
var prefixAndType = GetPrefixAndType(typeAndMember.Item1); var prefixAndType = GetPrefixAndType(typeAndMember.Item1);
var xamlType = typeRepository.GetByPrefix(prefixAndType.Item1, prefixAndType.Item2); var xamlType = typeRepository.GetByPrefix(prefixAndType.Item1, prefixAndType.Item2);

2
src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TypeExtension.cs

@ -43,7 +43,7 @@ namespace Perspex.Markup.Xaml.MarkupExtensions
return Type; return Type;
} }
return ResolveFromString(TypeName, markupExtensionContext.TypeRepository); return ResolveFromString(TypeName, markupExtensionContext.ValueContext.TypeRepository);
} }
} }
} }

2
src/Markup/Perspex.Markup.Xaml/OmniXAML

@ -1 +1 @@
Subproject commit 75e0dc32fe9a6d97f5b59d2b7d689db2475f444f Subproject commit b122549406107170bbe6e67c0d6a1a4252beef77

28
src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

@ -82,22 +82,22 @@
<Compile Include="Data\SourceBindingEndpoint.cs" /> <Compile Include="Data\SourceBindingEndpoint.cs" />
<Compile Include="Data\StyleResourceBinding.cs" /> <Compile Include="Data\StyleResourceBinding.cs" />
<Compile Include="Data\TargetBindingEndpoint.cs" /> <Compile Include="Data\TargetBindingEndpoint.cs" />
<Compile Include="glass\Glass.Core\AutoKeyDictionary.cs" />
<Compile Include="glass\Glass.Core\DependencySorter.cs" />
<Compile Include="glass\Glass.Core\EnumExtensions.cs" />
<Compile Include="glass\Glass.Core\Extensions.cs" />
<Compile Include="glass\Glass.Core\Guard.cs" />
<Compile Include="glass\Glass.Core\IAdd.cs" />
<Compile Include="glass\Glass.Core\IDependency.cs" />
<Compile Include="glass\Glass.Core\ReflectionExtensions.cs" />
<Compile Include="glass\Glass.Core\StackingLinkedList.cs" />
<Compile Include="glass\Glass.Core\StackingLinkedListMixin.cs" />
<Compile Include="MarkupExtensions\StyleResourceExtension.cs" /> <Compile Include="MarkupExtensions\StyleResourceExtension.cs" />
<Compile Include="MarkupExtensions\BindingExtension.cs" /> <Compile Include="MarkupExtensions\BindingExtension.cs" />
<Compile Include="MarkupExtensions\RelativeSourceExtension.cs" /> <Compile Include="MarkupExtensions\RelativeSourceExtension.cs" />
<Compile Include="MarkupExtensions\StaticExtension.cs" /> <Compile Include="MarkupExtensions\StaticExtension.cs" />
<Compile Include="MarkupExtensions\TemplateBindingExtension.cs" /> <Compile Include="MarkupExtensions\TemplateBindingExtension.cs" />
<Compile Include="MarkupExtensions\TypeExtension.cs" /> <Compile Include="MarkupExtensions\TypeExtension.cs" />
<Compile Include="OmniXAML\Source\Glass\AutoKeyDictionary.cs" />
<Compile Include="OmniXAML\Source\Glass\DependencySorter.cs" />
<Compile Include="OmniXAML\Source\Glass\EnumExtensions.cs" />
<Compile Include="OmniXAML\Source\Glass\Extensions.cs" />
<Compile Include="OmniXAML\Source\Glass\Guard.cs" />
<Compile Include="OmniXAML\Source\Glass\IAdd.cs" />
<Compile Include="OmniXAML\Source\Glass\IDependency.cs" />
<Compile Include="OmniXAML\Source\Glass\ReflectionExtensions.cs" />
<Compile Include="OmniXAML\Source\Glass\StackingLinkedList.cs" />
<Compile Include="OmniXAML\Source\Glass\StackingLinkedListMixin.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Attributes\ContentPropertyAttribute.cs" /> <Compile Include="OmniXAML\Source\OmniXaml\Attributes\ContentPropertyAttribute.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Attributes\DependsOnAttribute.cs" /> <Compile Include="OmniXAML\Source\OmniXaml\Attributes\DependsOnAttribute.cs" />
<Compile Include="OmniXAML\Source\OmniXaml\Attributes\XmlnsDefinitionAttribute.cs" /> <Compile Include="OmniXAML\Source\OmniXaml\Attributes\XmlnsDefinitionAttribute.cs" />
@ -276,6 +276,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" /> <None Include="app.config" />
<None Include="glass\Glass.Core\Glass.Core.nuspec" />
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -317,6 +318,10 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Glass, Version=1.6.0.113, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Glass.1.6.0.113\lib\portable45-net45+win8\Glass.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Sprache, Version=2.0.0.50, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Sprache, Version=2.0.0.50, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Sprache.2.0.0.50\lib\portable-net4+netcore45+win8+wp8+sl5+MonoAndroid+Xamarin.iOS10+MonoTouch\Sprache.dll</HintPath> <HintPath>..\..\..\packages\Sprache.2.0.0.50\lib\portable-net4+netcore45+win8+wp8+sl5+MonoAndroid+Xamarin.iOS10+MonoTouch\Sprache.dll</HintPath>
<Private>True</Private> <Private>True</Private>
@ -334,6 +339,9 @@
<HintPath>..\..\..\packages\Rx-PlatformServices.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.PlatformServices.dll</HintPath> <HintPath>..\..\..\packages\Rx-PlatformServices.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.PlatformServices.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="glass\Glass.Core\Glass.Core.csproj" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> <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. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.

66
src/Markup/Perspex.Markup.Xaml/PerspexXamlLoader.cs

@ -15,7 +15,7 @@ namespace Perspex.Markup.Xaml
using Controls; using Controls;
using Data; using Data;
using OmniXaml.ObjectAssembler; using OmniXaml.ObjectAssembler;
using System.Linq;
/// <summary> /// <summary>
/// Loads XAML for a perspex application. /// Loads XAML for a perspex application.
/// </summary> /// </summary>
@ -23,6 +23,7 @@ namespace Perspex.Markup.Xaml
{ {
private static PerspexParserFactory s_parserFactory; private static PerspexParserFactory s_parserFactory;
private static IInstanceLifeCycleListener s_lifeCycleListener = new PerspexLifeCycleListener(); private static IInstanceLifeCycleListener s_lifeCycleListener = new PerspexLifeCycleListener();
private static Stack<Uri> s_uriStack = new Stack<Uri>();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PerspexXamlLoader"/> class. /// Initializes a new instance of the <see cref="PerspexXamlLoader"/> class.
@ -41,6 +42,18 @@ namespace Perspex.Markup.Xaml
{ {
} }
/// <summary>
/// Gets the URI of the XAML file currently being loaded.
/// </summary>
/// <remarks>
/// TODO: Making this internal for now as I'm not sure that this is the correct
/// thing to do, but its needd by <see cref="StyleInclude"/> to get the URL of
/// the currently loading XAML file, as we can't use the OmniXAML parsing context
/// there. Maybe we need a way to inject OmniXAML context into the objects its
/// constructing?
/// </remarks>
internal static Uri UriContext => s_uriStack.Count > 0 ? s_uriStack.Peek() : null;
/// <summary> /// <summary>
/// Loads the XAML into a Perspex component. /// Loads the XAML into a Perspex component.
/// </summary> /// </summary>
@ -84,7 +97,7 @@ namespace Perspex.Markup.Xaml
{ {
var initialize = rootInstance as ISupportInitialize; var initialize = rootInstance as ISupportInitialize;
initialize?.BeginInit(); initialize?.BeginInit();
return Load(stream, rootInstance); return Load(stream, rootInstance, uri);
} }
} }
} }
@ -96,11 +109,14 @@ namespace Perspex.Markup.Xaml
/// Loads XAML from a URI. /// Loads XAML from a URI.
/// </summary> /// </summary>
/// <param name="uri">The URI of the XAML file.</param> /// <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"> /// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded. /// The optional instance into which the XAML should be loaded.
/// </param> /// </param>
/// <returns>The loaded object.</returns> /// <returns>The loaded object.</returns>
public object Load(Uri uri, object rootInstance = null) public object Load(Uri uri, Uri baseUri = null, object rootInstance = null)
{ {
Contract.Requires<ArgumentNullException>(uri != null); Contract.Requires<ArgumentNullException>(uri != null);
@ -112,9 +128,9 @@ namespace Perspex.Markup.Xaml
"Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?"); "Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?");
} }
using (var stream = assetLocator.Open(uri)) using (var stream = assetLocator.Open(uri, baseUri))
{ {
return Load(stream, rootInstance); return Load(stream, rootInstance, uri);
} }
} }
@ -143,23 +159,43 @@ namespace Perspex.Markup.Xaml
/// <param name="rootInstance"> /// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded. /// The optional instance into which the XAML should be loaded.
/// </param> /// </param>
/// <param name="uri">The URI of the XAML</param>
/// <returns>The loaded object.</returns> /// <returns>The loaded object.</returns>
public object Load(Stream stream, object rootInstance = null) public object Load(Stream stream, object rootInstance = null, Uri uri = null)
{ {
var result = base.Load(stream, new Settings try
{ {
RootInstance = rootInstance, if (uri != null)
InstanceLifeCycleListener = s_lifeCycleListener, {
}); s_uriStack.Push(uri);
}
var topLevel = result as TopLevel; 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);
}
if (topLevel != null) return result;
}
finally
{ {
DelayedBinding.ApplyBindings(topLevel); if (uri != null)
{
s_uriStack.Pop();
}
} }
return result;
} }
private static PerspexParserFactory GetParserFactory() private static PerspexParserFactory GetParserFactory()

14
src/Markup/Perspex.Markup.Xaml/Styling/StyleInclude.cs

@ -11,8 +11,20 @@ namespace Perspex.Markup.Xaml.Styling
/// </summary> /// </summary>
public class StyleInclude : IStyle public class StyleInclude : IStyle
{ {
private Uri _baseUri;
private IStyle _loaded; private IStyle _loaded;
/// <summary>
/// Initializes a new instance of the <see cref="StyleInclude"/> class.
/// </summary>
public StyleInclude()
{
// StyleInclude will usually be loaded from XAML and its URI can be relative to the
// XAML file that its included in, so store the current XAML file's URI if any as
// a base URI.
_baseUri = PerspexXamlLoader.UriContext;
}
/// <summary> /// <summary>
/// Gets or sets the source URL. /// Gets or sets the source URL.
/// </summary> /// </summary>
@ -28,7 +40,7 @@ namespace Perspex.Markup.Xaml.Styling
if (_loaded == null) if (_loaded == null)
{ {
var loader = new PerspexXamlLoader(); var loader = new PerspexXamlLoader();
_loaded = (IStyle)loader.Load(Source); _loaded = (IStyle)loader.Load(Source, _baseUri);
} }
return _loaded; return _loaded;

1
src/Markup/Perspex.Markup.Xaml/glass

@ -0,0 +1 @@
Subproject commit 9e17adbd3ac0b342e7922ee2d5a11e5d50b7e687

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

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Glass" version="1.6.0.113" targetFramework="portable45-net45+win8" />
<package id="Rx-Core" version="2.2.5" targetFramework="portable46-net451+win81" /> <package id="Rx-Core" version="2.2.5" targetFramework="portable46-net451+win81" />
<package id="Rx-Interfaces" version="2.2.5" targetFramework="portable46-net451+win81" /> <package id="Rx-Interfaces" version="2.2.5" targetFramework="portable46-net451+win81" />
<package id="Rx-Linq" version="2.2.5" targetFramework="portable46-net451+win81" /> <package id="Rx-Linq" version="2.2.5" targetFramework="portable46-net451+win81" />

11
src/Perspex.Base/Platform/IAssetLoader.cs

@ -21,22 +21,27 @@ namespace Perspex.Platform
/// <param name="asm"></param> /// <param name="asm"></param>
void SetDefaultAssembly(Assembly asm); void SetDefaultAssembly(Assembly asm);
/// <summary> /// <summary>
/// Checks if an asset with the specified URI exists. /// Checks if an asset with the specified URI exists.
/// </summary> /// </summary>
/// <param name="uri">The URI.</param> /// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>True if the asset could be found; otherwise false.</returns> /// <returns>True if the asset could be found; otherwise false.</returns>
bool Exists(Uri uri); bool Exists(Uri uri, Uri baseUri = null);
/// <summary> /// <summary>
/// Opens the resource with the requested URI. /// Opens the resource with the requested URI.
/// </summary> /// </summary>
/// <param name="uri">The URI.</param> /// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>A stream containing the resource contents.</returns> /// <returns>A stream containing the resource contents.</returns>
/// <exception cref="FileNotFoundException"> /// <exception cref="FileNotFoundException">
/// The resource was not found. /// The resource was not found.
/// </exception> /// </exception>
Stream Open(Uri uri); Stream Open(Uri uri, Uri baseUri = null);
} }
} }

187
src/Shared/PlatformSupport/AssetLoader.cs

@ -15,26 +15,6 @@ namespace Perspex.Shared.PlatformSupport
/// </summary> /// </summary>
public class AssetLoader : IAssetLoader public class AssetLoader : IAssetLoader
{ {
class AssemblyDescriptor
{
public AssemblyDescriptor(Assembly assembly)
{
Assembly = assembly;
if (assembly != null)
{
Resources = assembly.GetManifestResourceNames()
.ToDictionary(n => n, n => (IAssetDescriptor)new AssemblyResourceDescriptor(assembly, n));
Name = assembly.GetName().Name;
}
}
public Assembly Assembly { get; }
public Dictionary<string, IAssetDescriptor> Resources { get; }
public string Name { get; }
}
private static readonly Dictionary<string, AssemblyDescriptor> AssemblyNameCache private static readonly Dictionary<string, AssemblyDescriptor> AssemblyNameCache
= new Dictionary<string, AssemblyDescriptor>(); = new Dictionary<string, AssemblyDescriptor>();
@ -53,7 +33,94 @@ namespace Perspex.Shared.PlatformSupport
_defaultAssembly = new AssemblyDescriptor(assembly); _defaultAssembly = new AssemblyDescriptor(assembly);
} }
AssemblyDescriptor GetAssembly(string name) /// <summary>
/// Checks if an asset with the specified URI exists.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>True if the asset could be found; otherwise false.</returns>
public bool Exists(Uri uri, Uri baseUri = null)
{
return GetAsset(uri, baseUri) != null;
}
/// <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>A stream containing the resource contents.</returns>
/// <exception cref="FileNotFoundException">
/// The resource was not found.
/// </exception>
public Stream Open(Uri uri, Uri baseUri = null)
{
var asset = GetAsset(uri, baseUri);
if (asset == null)
{
throw new FileNotFoundException($"The resource {uri} could not be found.");
}
return asset.GetStream();
}
private IAssetDescriptor GetAsset(Uri uri, Uri baseUri)
{
if (!uri.IsAbsoluteUri || uri.Scheme == "resm")
{
var uriQueryParams = ParseQueryString(uri);
var baseUriQueryParams = uri != null ? ParseQueryString(uri) : null;
var asm = GetAssembly(uri) ?? GetAssembly(baseUri) ?? _defaultAssembly;
if (asm == null && _defaultAssembly == null)
{
throw new ArgumentException(
"No default assembly, entry assembly or explicit assembly specified; " +
"don't know where to look up for the resource, try specifiyng assembly explicitly.");
}
IAssetDescriptor rv;
var resourceKey = uri.AbsolutePath;
#if __IOS__
// TODO: HACK: to get iOS up and running. Using Shared projects for resources
// is flawed as this alters the reource key locations across platforms
// I think we need to use Portable libraries from now on to avoid that.
if(asm.Name.Contains("iOS"))
{
resourceKey = resourceKey.Replace("TestApplication", "Perspex.iOSTestApplication");
}
#endif
asm.Resources.TryGetValue(resourceKey, out rv);
return rv;
}
throw new ArgumentException($"Invalid uri, see https://github.com/Perspex/Perspex/issues/282#issuecomment-166982104", nameof(uri));
}
private AssemblyDescriptor GetAssembly(Uri uri)
{
if (uri != null)
{
var qs = ParseQueryString(uri);
string assemblyName;
if (qs.TryGetValue("assembly", out assemblyName))
{
return GetAssembly(assemblyName);
}
}
return null;
}
private AssemblyDescriptor GetAssembly(string name)
{ {
if (name == null) if (name == null)
{ {
@ -82,13 +149,20 @@ namespace Perspex.Shared.PlatformSupport
return rv; return rv;
} }
interface IAssetDescriptor private Dictionary<string, string> ParseQueryString(Uri uri)
{ {
Stream GetStream(); return uri.Query.TrimStart('?')
.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Split('='))
.ToDictionary(p => p[0], p => p[1]);
} }
private interface IAssetDescriptor
{
Stream GetStream();
}
class AssemblyResourceDescriptor : IAssetDescriptor private class AssemblyResourceDescriptor : IAssetDescriptor
{ {
private readonly Assembly _asm; private readonly Assembly _asm;
private readonly string _name; private readonly string _name;
@ -104,69 +178,24 @@ namespace Perspex.Shared.PlatformSupport
return _asm.GetManifestResourceStream(_name); return _asm.GetManifestResourceStream(_name);
} }
} }
IAssetDescriptor GetAsset(Uri uri) private class AssemblyDescriptor
{ {
if (!uri.IsAbsoluteUri || uri.Scheme == "resm") public AssemblyDescriptor(Assembly assembly)
{ {
var qs = uri.Query.TrimStart('?') Assembly = assembly;
.Split(new[] {'&'}, StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Split('='))
.ToDictionary(p => p[0], p => p[1]);
//TODO: Replace _defaultAssembly by current one (need support from OmniXAML)
var asm = _defaultAssembly;
if (qs.ContainsKey("assembly"))
asm = GetAssembly(qs["assembly"]);
if (asm == null && _defaultAssembly == null)
throw new ArgumentException(
"No defaultAssembly, entry assembly or explicit assembly specified, don't know where to look up for the resource, try specifiyng assembly explicitly");
IAssetDescriptor rv;
var resourceKey = uri.AbsolutePath;
#if __IOS__ if (assembly != null)
// TODO: HACK: to get iOS up and running. Using Shared projects for resources
// is flawed as this alters the reource key locations across platforms
// I think we need to use Portable libraries from now on to avoid that.
if(asm.Name.Contains("iOS"))
{ {
resourceKey = resourceKey.Replace("TestApplication", "Perspex.iOSTestApplication"); Resources = assembly.GetManifestResourceNames()
.ToDictionary(n => n, n => (IAssetDescriptor)new AssemblyResourceDescriptor(assembly, n));
Name = assembly.GetName().Name;
} }
#endif
asm.Resources.TryGetValue(resourceKey, out rv);
return rv;
} }
throw new ArgumentException($"Invalid uri, see https://github.com/Perspex/Perspex/issues/282#issuecomment-166982104", nameof(uri));
}
/// <summary>
/// Checks if an asset with the specified URI exists.
/// </summary>
/// <param name="uri">The URI.</param>
/// <returns>True if the asset could be found; otherwise false.</returns>
public bool Exists(Uri uri)
{
return GetAsset(uri) != null;
}
/// <summary> public Assembly Assembly { get; }
/// Opens the resource with the requested URI. public Dictionary<string, IAssetDescriptor> Resources { get; }
/// </summary> public string Name { get; }
/// <param name="uri">The URI.</param>
/// <returns>A stream containing the resource contents.</returns>
/// <exception cref="FileNotFoundException">
/// The resource was not found.
/// </exception>
public Stream Open(Uri uri)
{
var asset = GetAsset(uri);
if (asset == null)
throw new FileNotFoundException($"The resource {uri} could not be found.");
return asset.GetStream();
} }
} }
} }

Loading…
Cancel
Save