diff --git a/build/jobs/uno-uitest.yml b/build/jobs/uno-uitest.yml index 783a89ae85..9d87a4ac91 100644 --- a/build/jobs/uno-uitest.yml +++ b/build/jobs/uno-uitest.yml @@ -2,24 +2,18 @@ jobs: - job: UnoUITest_Wasm displayName: Uno UITest WebAssembly - container: unoplatform/wasm-build:2.0 - + condition: and(succeeded(), eq(variables['EnableUITest'], 'true')) pool: vmImage: $(LinuxImage) - workspace: clean: all - steps: - - bash: | build/scripts/wasm-uitest-run.sh - env: BUILD_SOURCESDIRECTORY: "$(build.sourcesdirectory)" BUILD_ARTIFACTSTAGINGDIRECTORY: "$(build.artifactstagingdirectory)" - displayName: 'Run UI Tests' - task: PublishTestResults@2 @@ -37,10 +31,9 @@ jobs: ArtifactName: UnoUITests ArtifactType: Container - - job: UnoUITest_Android displayName: Uno UITest Android - + condition: and(succeeded(), eq(variables['EnableUITest'], 'true')) pool: vmImage: $(MacImage) @@ -77,13 +70,11 @@ jobs: - job: UnoUITest_iOS displayName: Uno UITest iOS - + condition: and(succeeded(), eq(variables['EnableUITest'], 'true')) pool: vmImage: $(MacImage) - workspace: clean: all - steps: - template: ../steps/set-runtime.yml diff --git a/e2e/Forms/HelloWorld.sln b/e2e/Forms/HelloWorld.sln index 971618b89e..47030d4adb 100644 --- a/e2e/Forms/HelloWorld.sln +++ b/e2e/Forms/HelloWorld.sln @@ -32,7 +32,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.Forms", "..\..\src\Fo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.DryIoc.Forms", "..\..\src\Forms\Prism.DryIoc.Forms\Prism.DryIoc.Forms.csproj", "{74F5E189-BF46-4D6A-BDF9-752D44E3E2DD}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "containers", "containers", "{65A6E069-67F8-494E-9B34-FFBDBD587A75}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Prism.DryIoc.Shared", "..\..\src\Containers\Prism.DryIoc.Shared\Prism.DryIoc.Shared.shproj", "{6E7EC81D-DA39-4C4F-A898-0148558C34F4}" +EndProject Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\..\src\Containers\Prism.DryIoc.Shared\Prism.DryIoc.Shared.projitems*{6e7ec81d-da39-4c4f-a898-0148558c34f4}*SharedItemsImports = 13 + ..\..\src\Containers\Prism.DryIoc.Shared\Prism.DryIoc.Shared.projitems*{74f5e189-bf46-4d6a-bdf9-752d44e3e2dd}*SharedItemsImports = 5 + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU Ad-Hoc|ARM = Ad-Hoc|ARM @@ -701,6 +709,8 @@ Global {57CAB459-1FE8-45FB-9E73-EDBE84F7E190} = {FAF925D4-455B-448F-B048-6469DE52D341} {966CA02B-30A1-41A6-B514-BE8BDF942EF3} = {FAF925D4-455B-448F-B048-6469DE52D341} {74F5E189-BF46-4D6A-BDF9-752D44E3E2DD} = {FAF925D4-455B-448F-B048-6469DE52D341} + {65A6E069-67F8-494E-9B34-FFBDBD587A75} = {FAF925D4-455B-448F-B048-6469DE52D341} + {6E7EC81D-DA39-4C4F-A898-0148558C34F4} = {65A6E069-67F8-494E-9B34-FFBDBD587A75} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9E406BB3-20B9-4659-9660-A29BE45C4626} diff --git a/e2e/Forms/src/HelloDialog/HelloDialog.csproj b/e2e/Forms/src/HelloDialog/HelloDialog.csproj index e986bdad08..8371e68c39 100644 --- a/e2e/Forms/src/HelloDialog/HelloDialog.csproj +++ b/e2e/Forms/src/HelloDialog/HelloDialog.csproj @@ -6,10 +6,6 @@ true - - - - diff --git a/e2e/Forms/src/HelloPageDialog/HelloPageDialog.csproj b/e2e/Forms/src/HelloPageDialog/HelloPageDialog.csproj index e6fa848bee..0461d5dfc7 100644 --- a/e2e/Forms/src/HelloPageDialog/HelloPageDialog.csproj +++ b/e2e/Forms/src/HelloPageDialog/HelloPageDialog.csproj @@ -6,10 +6,6 @@ true - - - - diff --git a/e2e/Forms/src/HelloWorld.Android/HelloWorld.Android.csproj b/e2e/Forms/src/HelloWorld.Android/HelloWorld.Android.csproj index 4053bbd2fd..82e32f6e21 100644 --- a/e2e/Forms/src/HelloWorld.Android/HelloWorld.Android.csproj +++ b/e2e/Forms/src/HelloWorld.Android/HelloWorld.Android.csproj @@ -47,14 +47,6 @@ - - - - - - - - diff --git a/e2e/Forms/src/HelloWorld.UWP/HelloWorld.UWP.csproj b/e2e/Forms/src/HelloWorld.UWP/HelloWorld.UWP.csproj index 2cad718ef3..33f544ece7 100644 --- a/e2e/Forms/src/HelloWorld.UWP/HelloWorld.UWP.csproj +++ b/e2e/Forms/src/HelloWorld.UWP/HelloWorld.UWP.csproj @@ -146,7 +146,6 @@ - diff --git a/e2e/Forms/src/HelloWorld.iOS/HelloWorld.iOS.csproj b/e2e/Forms/src/HelloWorld.iOS/HelloWorld.iOS.csproj index 785f7c81f2..11c295365c 100644 --- a/e2e/Forms/src/HelloWorld.iOS/HelloWorld.iOS.csproj +++ b/e2e/Forms/src/HelloWorld.iOS/HelloWorld.iOS.csproj @@ -165,7 +165,6 @@ - diff --git a/e2e/Forms/src/HelloWorld/App.xaml.cs b/e2e/Forms/src/HelloWorld/App.xaml.cs index 726f2d7ace..76a850102b 100644 --- a/e2e/Forms/src/HelloWorld/App.xaml.cs +++ b/e2e/Forms/src/HelloWorld/App.xaml.cs @@ -1,67 +1,73 @@ using System; +using System.Threading.Tasks; using HelloWorld.ViewModels; using HelloWorld.Views; using Prism; using Prism.Ioc; using Prism.Modularity; +using Prism.Navigation; using Xamarin.Forms; using Xamarin.Forms.Xaml; -[assembly: XamlCompilation (XamlCompilationOptions.Compile)] +[assembly: XamlCompilation(XamlCompilationOptions.Compile)] namespace HelloWorld { + #region Test Navigation Calls + + //NavigationService.NavigateAsync("NavigationPage/MyTabbedPage"); //works + //NavigationService.NavigateAsync("NavigationPage/MyTabbedPage/ViewC"); //works + //NavigationService.NavigateAsync("NavigationPage/MyTabbedPage/ViewC/ViewA"); //works + //NavigationService.NavigateAsync("NavigationPage/ViewA/MyTabbedPage"); //works + //NavigationService.NavigateAsync("NavigationPage/ViewA/MyTabbedPage/ViewC"); //works + //NavigationService.NavigateAsync("NavigationPage/ViewA/MyTabbedPage/ViewC/ViewA/ViewB"); //works + //NavigationService.NavigateAsync("MyMasterDetail/NavigationPage/MyTabbedPage/ViewC"); //works + + //NavigationService.NavigateAsync($"MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC/ViewA"); //works -- + //NavigationService.NavigateAsync($"NavigationPage/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC"); //works + //NavigationService.NavigateAsync($"NavigationPage/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC/ViewC"); //works + //NavigationService.NavigateAsync($"NavigationPage/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC/ViewC/ViewA"); //works + //NavigationService.NavigateAsync($"NavigationPage/ViewA/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC"); //works + //NavigationService.NavigateAsync($"NavigationPage/ViewA/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC/ViewC"); //works + //NavigationService.NavigateAsync($"NavigationPage/ViewA/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC/ViewC/ViewA/ViewB"); //works + //NavigationService.NavigateAsync($"MyMasterDetail/NavigationPage/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC/ViewC"); //works + + //NavigationService.NavigateAsync($"NavigationPage/ViewA/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC/ViewC"); + //NavigationService.NavigateAsync($"NavigationPage/ViewA/MyTabbedPage/ViewA/ViewB/ViewC"); + //NavigationService.NavigateAsync($"NavigationPage/ViewA/ViewB/NavigationPage?{KnownNavigationParameters.UseModalNavigation}=true/ViewB/ViewC"); + //NavigationService.NavigateAsync($"NavigationPage/ViewA/ViewB/ViewC?{KnownNavigationParameters.UseModalNavigation}=true"); + //NavigationService.NavigateAsync($"MyMasterDetail/NavigationPage/MyTabbedPage/ViewA/ViewC?{KnownNavigationParameters.UseModalNavigation}=true"); + //NavigationService.NavigateAsync($"ViewA/ViewB/MyMasterDetail/NavigationPage/ViewA/ViewC"); + //NavigationService.NavigateAsync($"ViewA/ViewB/MyMasterDetail/ViewA/ViewC"); + //NavigationService.NavigateAsync($"ViewA/ViewB/MyMasterDetail/NavigationPage/ViewA/ViewB?{KnownNavigationParameters.UseModalNavigation}=true/ViewA/ViewC"); + //NavigationService.NavigateAsync($"MyMasterDetail/NavigationPage/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC"); + #endregion public sealed partial class App { public App() : this(null) { - } public App(IPlatformInitializer initializer) : this(initializer, true) { - } public App(IPlatformInitializer initializer, bool setFormsDependencyResolver) : base(initializer, setFormsDependencyResolver) { - } protected override void OnInitialized() { InitializeComponent(); - //NavigationService.NavigateAsync("NavigationPage/MyTabbedPage"); //works - //NavigationService.NavigateAsync("NavigationPage/MyTabbedPage/ViewC"); //works - //NavigationService.NavigateAsync("NavigationPage/MyTabbedPage/ViewC/ViewA"); //works - //NavigationService.NavigateAsync("NavigationPage/ViewA/MyTabbedPage"); //works - //NavigationService.NavigateAsync("NavigationPage/ViewA/MyTabbedPage/ViewC"); //works - //NavigationService.NavigateAsync("NavigationPage/ViewA/MyTabbedPage/ViewC/ViewA/ViewB"); //works - //NavigationService.NavigateAsync("MyMasterDetail/NavigationPage/MyTabbedPage/ViewC"); //works - - //NavigationService.NavigateAsync($"MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC/ViewA"); //works -- - //NavigationService.NavigateAsync($"NavigationPage/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC"); //works - //NavigationService.NavigateAsync($"NavigationPage/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC/ViewC"); //works - //NavigationService.NavigateAsync($"NavigationPage/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC/ViewC/ViewA"); //works - //NavigationService.NavigateAsync($"NavigationPage/ViewA/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC"); //works - //NavigationService.NavigateAsync($"NavigationPage/ViewA/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC/ViewC"); //works - //NavigationService.NavigateAsync($"NavigationPage/ViewA/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC/ViewC/ViewA/ViewB"); //works - //NavigationService.NavigateAsync($"MyMasterDetail/NavigationPage/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC/ViewC"); //works - - //NavigationService.NavigateAsync($"NavigationPage/ViewA/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC/ViewC"); - //NavigationService.NavigateAsync($"NavigationPage/ViewA/MyTabbedPage/ViewA/ViewB/ViewC"); - //NavigationService.NavigateAsync($"NavigationPage/ViewA/ViewB/NavigationPage?{KnownNavigationParameters.UseModalNavigation}=true/ViewB/ViewC"); - //NavigationService.NavigateAsync($"NavigationPage/ViewA/ViewB/ViewC?{KnownNavigationParameters.UseModalNavigation}=true"); - //NavigationService.NavigateAsync($"MyMasterDetail/NavigationPage/MyTabbedPage/ViewA/ViewC?{KnownNavigationParameters.UseModalNavigation}=true"); - //NavigationService.NavigateAsync($"ViewA/ViewB/MyMasterDetail/NavigationPage/ViewA/ViewC"); - //NavigationService.NavigateAsync($"ViewA/ViewB/MyMasterDetail/ViewA/ViewC"); - //NavigationService.NavigateAsync($"ViewA/ViewB/MyMasterDetail/NavigationPage/ViewA/ViewB?{KnownNavigationParameters.UseModalNavigation}=true/ViewA/ViewC"); - //NavigationService.NavigateAsync($"MyMasterDetail/NavigationPage/MyTabbedPage?{KnownNavigationParameters.SelectedTab}=ViewC"); - - NavigationService.NavigateAsync($"MyMasterDetail/MyTabbedPage"); + NavigationService.NavigateAsync($"MyMasterDetail/MyTabbedPage").OnNavigationError(OnNavigationError); + } + + private void OnNavigationError(Exception ex) + { + } protected override void RegisterTypes(IContainerRegistry containerRegistry) diff --git a/e2e/Forms/src/HelloWorld/HelloWorld.csproj b/e2e/Forms/src/HelloWorld/HelloWorld.csproj index 314c0d051f..9f957a021e 100644 --- a/e2e/Forms/src/HelloWorld/HelloWorld.csproj +++ b/e2e/Forms/src/HelloWorld/HelloWorld.csproj @@ -6,10 +6,6 @@ true - - - - diff --git a/e2e/Forms/src/ModuleA/ModuleA.csproj b/e2e/Forms/src/ModuleA/ModuleA.csproj index e4fa71d3c7..f838870cd1 100644 --- a/e2e/Forms/src/ModuleA/ModuleA.csproj +++ b/e2e/Forms/src/ModuleA/ModuleA.csproj @@ -6,10 +6,6 @@ true - - - - diff --git a/src/Containers/Prism.DryIoc.Shared/DryIocContainerExtension.cs b/src/Containers/Prism.DryIoc.Shared/DryIocContainerExtension.cs index 2152da51c8..3c9a16bf67 100644 --- a/src/Containers/Prism.DryIoc.Shared/DryIocContainerExtension.cs +++ b/src/Containers/Prism.DryIoc.Shared/DryIocContainerExtension.cs @@ -16,7 +16,7 @@ internal partial #endif class DryIocContainerExtension : IContainerExtension, IContainerInfo { - private IResolverContext _currentScope; + private DryIocScopedProvider _currentScope; /// /// Gets the Default DryIoc Container Rules used by Prism @@ -57,6 +57,11 @@ public DryIocContainerExtension(IContainer container) } #endif + /// + /// Gets the current scope + /// + public IScopedProvider CurrentScope => _currentScope; + /// /// Used to perform any final steps for configuring the extension that may be required by the container. /// @@ -284,7 +289,7 @@ public object Resolve(Type type, params (Type Type, object Instance)[] parameter { try { - var container = _currentScope ?? Instance; + var container = _currentScope?.Resolver ?? Instance; return container.Resolve(type, args: parameters.Select(p => p.Instance).ToArray()); } catch (Exception ex) @@ -304,7 +309,7 @@ public object Resolve(Type type, string name, params (Type Type, object Instance { try { - var container = _currentScope ?? Instance; + var container = _currentScope?.Resolver ?? Instance; return container.Resolve(type, name, args: parameters.Select(p => p.Instance).ToArray()); } catch (Exception ex) @@ -352,7 +357,7 @@ Type IContainerInfo.GetRegistrationType(Type serviceType) /// /// Creates a new Scope /// - public virtual void CreateScope() => + public virtual IScopedProvider CreateScope() => CreateScopeInternal(); /// @@ -362,17 +367,62 @@ public virtual void CreateScope() => /// /// This should be called by custom implementations that Implement IServiceScopeFactory /// - protected IResolverContext CreateScopeInternal() + protected IScopedProvider CreateScopeInternal() { - if (_currentScope != null) + var resolver = Instance.OpenScope(); + _currentScope = new DryIocScopedProvider(resolver); + return _currentScope; + } + + private class DryIocScopedProvider : IScopedProvider + { + public DryIocScopedProvider(IResolverContext resolver) { - _currentScope.Dispose(); - _currentScope = null; - GC.Collect(); + Resolver = resolver; } - _currentScope = Instance.OpenScope(); - return _currentScope; + public bool IsAttached { get; set; } + + public IResolverContext Resolver { get; private set; } + public IScopedProvider CurrentScope => this; + + public IScopedProvider CreateScope() => this; + + public void Dispose() + { + Resolver.Dispose(); + Resolver = null; + } + + public object Resolve(Type type) => + Resolve(type, Array.Empty<(Type, object)>()); + + public object Resolve(Type type, string name) => + Resolve(type, name, Array.Empty<(Type, object)>()); + + public object Resolve(Type type, params (Type Type, object Instance)[] parameters) + { + try + { + return Resolver.Resolve(type, args: parameters.Select(p => p.Instance).ToArray()); + } + catch (Exception ex) + { + throw new ContainerResolutionException(type, ex); + } + } + + public object Resolve(Type type, string name, params (Type Type, object Instance)[] parameters) + { + try + { + return Resolver.Resolve(type, name, args: parameters.Select(p => p.Instance).ToArray()); + } + catch (Exception ex) + { + throw new ContainerResolutionException(type, name, ex); + } + } } } } diff --git a/src/Containers/Prism.Unity.Shared/UnityContainerExtension.cs b/src/Containers/Prism.Unity.Shared/UnityContainerExtension.cs index 69dd92f2de..0367a9e48b 100644 --- a/src/Containers/Prism.Unity.Shared/UnityContainerExtension.cs +++ b/src/Containers/Prism.Unity.Shared/UnityContainerExtension.cs @@ -19,7 +19,7 @@ internal partial #endif class UnityContainerExtension : IContainerExtension, IContainerInfo { - private IUnityContainer _currentScope; + private UnityScopedProvider _currentScope; /// /// The instance of the wrapped container @@ -49,6 +49,11 @@ public UnityContainerExtension(IUnityContainer container) } #endif + /// + /// Gets the current + /// + public IScopedProvider CurrentScope => _currentScope; + /// /// Used to perform any final steps for configuring the extension that may be required by the container. /// @@ -281,7 +286,7 @@ public object Resolve(Type type, params (Type Type, object Instance)[] parameter { try { - var c = _currentScope ?? Instance; + var c = _currentScope?.Container ?? Instance; var overrides = parameters.Select(p => new DependencyOverride(p.Type, p.Instance)).ToArray(); return c.Resolve(type, overrides); } @@ -302,9 +307,9 @@ public object Resolve(Type type, string name, params (Type Type, object Instance { try { - var c = _currentScope ?? Instance; + var c = _currentScope?.Container ?? Instance; - // Unit will simply return a new object() for unregistered Views + // Unity will simply return a new object() for unregistered Views if (!c.IsRegistered(type, name)) throw new KeyNotFoundException($"No registered type {type.Name} with the key {name}."); @@ -359,7 +364,7 @@ Type IContainerInfo.GetRegistrationType(Type serviceType) /// /// Creates a new Scope /// - public virtual void CreateScope() => + public virtual IScopedProvider CreateScope() => CreateScopeInternal(); /// @@ -369,17 +374,67 @@ public virtual void CreateScope() => /// /// This should be called by custom implementations that Implement IServiceScopeFactory /// - protected IUnityContainer CreateScopeInternal() + protected IScopedProvider CreateScopeInternal() + { + var child = Instance.CreateChildContainer(); + _currentScope = new UnityScopedProvider(child); + return _currentScope; + } + + private class UnityScopedProvider : IScopedProvider { - if (_currentScope != null) + public UnityScopedProvider(IUnityContainer container) { - _currentScope.Dispose(); - _currentScope = null; - GC.Collect(); + Container = container; } - _currentScope = Instance.CreateChildContainer(); - return _currentScope; + public IUnityContainer Container { get; private set; } + public bool IsAttached { get; set; } + public IScopedProvider CurrentScope => this; + + public IScopedProvider CreateScope() => this; + + public void Dispose() + { + Container.Dispose(); + Container = null; + } + + public object Resolve(Type type) => + Resolve(type, Array.Empty<(Type, object)>()); + + public object Resolve(Type type, string name) => + Resolve(type, name, Array.Empty<(Type, object)>()); + + public object Resolve(Type type, params (Type Type, object Instance)[] parameters) + { + try + { + var overrides = parameters.Select(p => new DependencyOverride(p.Type, p.Instance)).ToArray(); + return Container.Resolve(type, overrides); + } + catch (Exception ex) + { + throw new ContainerResolutionException(type, ex); + } + } + + public object Resolve(Type type, string name, params (Type Type, object Instance)[] parameters) + { + try + { + // Unity will simply return a new object() for unregistered Views + if (!Container.IsRegistered(type, name)) + throw new KeyNotFoundException($"No registered type {type.Name} with the key {name}."); + + var overrides = parameters.Select(p => new DependencyOverride(p.Type, p.Instance)).ToArray(); + return Container.Resolve(type, name, overrides); + } + catch (Exception ex) + { + throw new ContainerResolutionException(type, name, ex); + } + } } } } diff --git a/src/Forms/Prism.DryIoc.Forms/PrismApplication.cs b/src/Forms/Prism.DryIoc.Forms/PrismApplication.cs index 3c79f46aaf..042b1df52b 100644 --- a/src/Forms/Prism.DryIoc.Forms/PrismApplication.cs +++ b/src/Forms/Prism.DryIoc.Forms/PrismApplication.cs @@ -1,12 +1,5 @@ -using System; -using System.Linq; -using System.Reflection; -using DryIoc; -using Prism.Common; +using DryIoc; using Prism.Ioc; -using Prism.Logging; -using Prism.Navigation; -using Xamarin.Forms; using Xamarin.Forms.Internals; [assembly: Xamarin.Forms.XmlnsDefinition("http://prismlibrary.com", "Prism.DryIoc")] @@ -55,28 +48,5 @@ protected override IContainerExtension CreateContainerExtension() /// /// An instance of protected virtual Rules CreateContainerRules() => DryIocContainerExtension.DefaultRules; - - /// - /// Configures the Container. - /// - /// - protected override void RegisterRequiredTypes(IContainerRegistry containerRegistry) - { - base.RegisterRequiredTypes(containerRegistry); - containerRegistry.GetContainer().Register(); - containerRegistry.GetContainer().Register( - made: Made.Of(() => SetPage(Arg.Of(), Arg.Of())), - setup: Setup.Decorator); - } - - internal static INavigationService SetPage(INavigationService navigationService, Page page) - { - if (navigationService is IPageAware pageAware) - { - pageAware.Page = page; - } - - return navigationService; - } } } \ No newline at end of file diff --git a/src/Forms/Prism.Forms/Behaviors/PageBehaviorFactory.cs b/src/Forms/Prism.Forms/Behaviors/PageBehaviorFactory.cs index 108ae7b7b1..6ce0551d53 100644 --- a/src/Forms/Prism.Forms/Behaviors/PageBehaviorFactory.cs +++ b/src/Forms/Prism.Forms/Behaviors/PageBehaviorFactory.cs @@ -80,6 +80,7 @@ void IPageBehaviorFactory.ApplyPageBehaviors(Page page) } page.Behaviors.Add(new PageLifeCycleAwareBehavior()); + page.Behaviors.Add(new PageScopeBehavior()); ApplyPageBehaviors(page); } diff --git a/src/Forms/Prism.Forms/Behaviors/PageScopeBehavior.cs b/src/Forms/Prism.Forms/Behaviors/PageScopeBehavior.cs new file mode 100644 index 0000000000..4de5eda985 --- /dev/null +++ b/src/Forms/Prism.Forms/Behaviors/PageScopeBehavior.cs @@ -0,0 +1,24 @@ +using Xamarin.Forms; + +namespace Prism.Behaviors +{ + /// + /// Controls the Page container Scope + /// + public sealed class PageScopeBehavior : BehaviorBase + { + protected override void OnAttachedTo(Page page) + { + base.OnAttachedTo(page); + // Ensure the scope gets created and NavigationService is created + Navigation.Xaml.Navigation.GetNavigationService(page); + } + + protected override void OnDetachingFrom(Page page) + { + base.OnDetachingFrom(page); + // This forces the Attached Property to get cleaned up. + page.SetValue(Navigation.Xaml.Navigation.NavigationScopeProperty, null); + } + } +} diff --git a/src/Forms/Prism.Forms/Ioc/IContainerExtensionExtensions.cs b/src/Forms/Prism.Forms/Ioc/IContainerExtensionExtensions.cs deleted file mode 100644 index 6a5de8d541..0000000000 --- a/src/Forms/Prism.Forms/Ioc/IContainerExtensionExtensions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Prism.Common; -using Prism.Navigation; -using Xamarin.Forms; - -namespace Prism.Ioc -{ - public static class IContainerExtensionExtensions - { - public static INavigationService CreateNavigationService(this IContainerExtension containerExtension, Page page) - { - var navigationService = containerExtension.Resolve(PrismApplicationBase.NavigationServiceName); - ((IPageAware)navigationService).Page = page; - return navigationService; - } - } -} diff --git a/src/Forms/Prism.Forms/Ioc/IResolverOverridesHelper.cs b/src/Forms/Prism.Forms/Ioc/IResolverOverridesHelper.cs new file mode 100644 index 0000000000..5e5ecb1be0 --- /dev/null +++ b/src/Forms/Prism.Forms/Ioc/IResolverOverridesHelper.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Prism.Ioc +{ + /// + /// Provides a helper interface for Regions to be able to inject the current Region + /// + public interface IResolverOverridesHelper + { + IEnumerable<(Type Type, object Instance)> GetOverrides(); + } +} diff --git a/src/Forms/Prism.Forms/Navigation/INavigationServiceExtensions.cs b/src/Forms/Prism.Forms/Navigation/INavigationServiceExtensions.cs index bb07b0fe66..9f4c5cceea 100644 --- a/src/Forms/Prism.Forms/Navigation/INavigationServiceExtensions.cs +++ b/src/Forms/Prism.Forms/Navigation/INavigationServiceExtensions.cs @@ -10,6 +10,20 @@ namespace Prism.Navigation { public static class INavigationServiceExtensions { + /// + /// Provides an easy to use way to provide an Error Callback without using await NavigationService + /// + /// The current Navigation Task + /// The handler + public static void OnNavigationError(this Task navigationTask, Action errorCallback) + { + navigationTask.Await(r => + { + if (!r.Success) + errorCallback?.Invoke(r.Exception); + }); + } + /// /// Navigates to the most recent entry in the back navigation history by popping the calling Page off the navigation stack. /// diff --git a/src/Forms/Prism.Forms/Navigation/PageNavigationService.cs b/src/Forms/Prism.Forms/Navigation/PageNavigationService.cs index 9ad8ab8d73..8f2afe8ad6 100644 --- a/src/Forms/Prism.Forms/Navigation/PageNavigationService.cs +++ b/src/Forms/Prism.Forms/Navigation/PageNavigationService.cs @@ -34,7 +34,7 @@ Page IPageAware.Page set { _page = value; } } - public PageNavigationService(IContainerExtension container, IApplicationProvider applicationProvider, IPageBehaviorFactory pageBehaviorFactory, ILoggerFacade logger) + public PageNavigationService(IContainerProvider container, IApplicationProvider applicationProvider, IPageBehaviorFactory pageBehaviorFactory, ILoggerFacade logger) { _container = container; _applicationProvider = applicationProvider; @@ -781,7 +781,13 @@ protected virtual Page CreatePage(string segmentName) { try { - return _container.Resolve(segmentName) as Page; + _container.CreateScope(); + var page = (Page)_container.Resolve(segmentName); + + if (page is null) + throw new NullReferenceException($"The resolved type for {segmentName} was null. You may be attempting to navigate to a Non-Page type"); + + return SetNavigationServiceForPage(page); } catch (Exception ex) { @@ -822,6 +828,22 @@ protected virtual Page CreatePageFromSegment(string segment) } } + private Page SetNavigationServiceForPage(Page page) + { + // Someone explicitly set Autowire ViewModel + if (page.GetValue(Xaml.Navigation.NavigationServiceProperty) != null) + return page; + + // This will wireup the Navigation Service in case you have something injected that + // actually required the Nav Service + var childNavService = _container.Resolve(); + if (childNavService is IPageAware pa) + pa.Page = page; + + page.SetValue(Xaml.Navigation.NavigationServiceProperty, childNavService); + return page; + } + void ConfigurePages(Page page, string segment) { if (page is TabbedPage) diff --git a/src/Forms/Prism.Forms/Navigation/Xaml/Navigation.cs b/src/Forms/Prism.Forms/Navigation/Xaml/Navigation.cs index 894615bc35..12af1750ae 100644 --- a/src/Forms/Prism.Forms/Navigation/Xaml/Navigation.cs +++ b/src/Forms/Prism.Forms/Navigation/Xaml/Navigation.cs @@ -1,57 +1,110 @@ using System; +using System.ComponentModel; using Prism.Common; using Prism.Ioc; using Xamarin.Forms; namespace Prism.Navigation.Xaml { + /// + /// Provides Attachable properties for Navigation + /// public static class Navigation { internal static readonly BindableProperty NavigationServiceProperty = BindableProperty.CreateAttached("NavigationService", typeof(INavigationService), - typeof(NavigationExtensionBase), + typeof(Navigation), default(INavigationService)); + internal static readonly BindableProperty NavigationScopeProperty = + BindableProperty.CreateAttached("NavigationScope", + typeof(IScopedProvider), + typeof(Navigation), + default(IScopedProvider), + propertyChanged: OnNavigationScopeChanged); + + private static void OnNavigationScopeChanged(BindableObject bindable, object oldValue, object newValue) + { + if (oldValue == newValue) + { + return; + } + + if (oldValue != null && newValue is null && oldValue is IScopedProvider oldProvider) + { + oldProvider.Dispose(); + return; + } + + if(newValue != null && newValue is IScopedProvider scopedProvider) + { + scopedProvider.IsAttached = true; + } + } + + /// + /// Provides bindable CanNavigate Bindable Property + /// public static readonly BindableProperty CanNavigateProperty = BindableProperty.CreateAttached("CanNavigate", typeof(bool), - typeof(NavigationExtensionBase), + typeof(Navigation), true, propertyChanged: OnCanNavigatePropertyChanged); internal static readonly BindableProperty RaiseCanExecuteChangedInternalProperty = BindableProperty.CreateAttached("RaiseCanExecuteChangedInternal", typeof(Action), - typeof(NavigationExtensionBase), + typeof(Navigation), default(Action)); + /// + /// Gets the Bindable Can Navigate property for an element + /// + /// The bindable element public static bool GetCanNavigate(BindableObject view) => (bool) view.GetValue(CanNavigateProperty); + /// + /// Sets the Bindable Can Navigate property for an element + /// + /// The bindable element + /// The Can Navigate value public static void SetCanNavigate(BindableObject view, bool value) => view.SetValue(CanNavigateProperty, value); internal static INavigationService GetNavigationService(Page page) { if(page == null) throw new ArgumentNullException(nameof(page)); - var navigationService = (INavigationService) page.GetValue(NavigationServiceProperty); - if (navigationService == null) page.SetValue(NavigationServiceProperty, navigationService = CreateNavigationService(page)); + var navService = (INavigationService) page.GetValue(NavigationServiceProperty); + if(navService is null) + { + var currentScope = (IScopedProvider)page.GetValue(NavigationScopeProperty) ?? ContainerLocator.Container.CurrentScope; + + if(currentScope is null) + currentScope = ContainerLocator.Container.CreateScope(); - return navigationService; + if (!currentScope.IsAttached) + page.SetValue(NavigationScopeProperty, currentScope); + + currentScope.IsAttached = true; + + navService = currentScope.Resolve(); + if(navService is IPageAware pa) + { + pa.Page = page; + } + + page.SetValue(NavigationServiceProperty, navService); + } + + return navService; } internal static Action GetRaiseCanExecuteChangedInternal(BindableObject view) => (Action) view.GetValue(RaiseCanExecuteChangedInternalProperty); internal static void SetRaiseCanExecuteChangedInternal(BindableObject view, Action value) => view.SetValue(RaiseCanExecuteChangedInternalProperty, value); - private static INavigationService CreateNavigationService(Page view) - { - var context = (PrismApplicationBase) Application.Current; - var navigationService = context.Container.Resolve("PageNavigationService"); - if (navigationService is IPageAware pageAware) pageAware.Page = view; - return navigationService; - } - private static void OnCanNavigatePropertyChanged(BindableObject bindable, object oldvalue, object newvalue) { var action = GetRaiseCanExecuteChangedInternal(bindable); diff --git a/src/Forms/Prism.Forms/Platform{T}.cs b/src/Forms/Prism.Forms/Platform{T}.cs index aca96db6ee..f4bc26eaa7 100644 --- a/src/Forms/Prism.Forms/Platform{T}.cs +++ b/src/Forms/Prism.Forms/Platform{T}.cs @@ -3,22 +3,22 @@ namespace Prism { - public interface IPlatform - { - RuntimePlatform RuntimePlatform { get; } + public interface IPlatform + { + RuntimePlatform RuntimePlatform { get; } - Type ViewType { get; } - } + Type ViewType { get; } + } - public class Platform : IPlatform - { - public RuntimePlatform RuntimePlatform { get; } + public class Platform : IPlatform + { + public RuntimePlatform RuntimePlatform { get; } - public Type ViewType { get { return typeof(TView); } } + public Type ViewType => typeof(TView); - public Platform(RuntimePlatform runtimePlatform) - { - RuntimePlatform = runtimePlatform; - } - } + public Platform(RuntimePlatform runtimePlatform) + { + RuntimePlatform = runtimePlatform; + } + } } diff --git a/src/Forms/Prism.Forms/PrismApplicationBase.cs b/src/Forms/Prism.Forms/PrismApplicationBase.cs index a527c59276..12b3b6a451 100644 --- a/src/Forms/Prism.Forms/PrismApplicationBase.cs +++ b/src/Forms/Prism.Forms/PrismApplicationBase.cs @@ -1,4 +1,4 @@ -using Prism.AppModel; +using Prism.AppModel; using Prism.Behaviors; using Prism.Common; using Prism.Events; @@ -10,6 +10,7 @@ using Prism.Services; using Prism.Services.Dialogs; using System; +using System.Collections.Generic; using System.Linq; using Xamarin.Forms; using Xamarin.Forms.Internals; @@ -25,12 +26,16 @@ public abstract class PrismApplicationBase : Application /// Gets the Current PrismApplication /// public new static PrismApplicationBase Current => (PrismApplicationBase) Application.Current; + + /// + /// The registration name to create a new transient instance of the + /// public const string NavigationServiceName = "PageNavigationService"; - public const string NavigationServiceParameterName = "navigationService"; - IContainerExtension _containerExtension; - IModuleCatalog _moduleCatalog; - Page _previousPage = null; - bool _setFormsDependencyResolver { get; } + + private IContainerExtension _containerExtension; + private IModuleCatalog _moduleCatalog; + private Page _previousPage = null; + private bool _setFormsDependencyResolver { get; } /// /// The dependency injection container used to resolve objects @@ -83,7 +88,7 @@ protected PrismApplicationBase(IPlatformInitializer platformInitializer, bool se /// /// Run the initialization process. /// - void InitializeInternal() + private void InitializeInternal() { ConfigureViewModelLocator(); Initialize(); @@ -97,38 +102,41 @@ protected virtual void ConfigureViewModelLocator() { ViewModelLocationProvider.SetDefaultViewModelFactory((view, type) => { - INavigationService navigationService = null; - switch (view) + List<(Type Type, object Instance)> overrides = new List<(Type, object)>(); + if(Container.IsRegistered()) { - case Page page: - navigationService = CreateNavigationService(page); - break; - case BindableObject bindable: - if (bindable.GetValue(ViewModelLocator.AutowirePartialViewProperty) is Page attachedPage) - { - navigationService = CreateNavigationService(attachedPage); - } - break; + var resolver = Container.Resolve(); + overrides.AddRange(resolver.GetOverrides()); } - return Container.Resolve(type, (typeof(INavigationService), navigationService)); + if(!overrides.Any(x => x.Type == typeof(INavigationService))) + { + var navService = CreateNavigationService(view); + overrides.Add((typeof(INavigationService), navService)); + } + + return Container.Resolve(type, overrides.ToArray()); }); } - /// - /// Creates a new with the proper context of which to navigation from. - /// - /// The current that the will navigation from. - /// The - protected INavigationService CreateNavigationService(Page page) + private INavigationService CreateNavigationService(object view) { - var navService = Container.Resolve(NavigationServiceName); - if(navService is IPageAware pa) + if(view is Page page) { - pa.Page = page; + return Navigation.Xaml.Navigation.GetNavigationService(page); + } + else if (view is VisualElement visualElement) + { + page = ViewModelLocator.GetAutowirePartialView(visualElement); + if (page != null) + { + return CreateNavigationService(page); + } + + return CreateNavigationService(visualElement.Parent); } - return navService; + return Container.Resolve(); } /// @@ -151,7 +159,8 @@ protected virtual void Initialize() _moduleCatalog = Container.Resolve(); ConfigureModuleCatalog(_moduleCatalog); - NavigationService = _containerExtension.CreateNavigationService(null); + _containerExtension.CreateScope(); + NavigationService = _containerExtension.Resolve(); InitializeModules(); } @@ -178,10 +187,17 @@ protected virtual void SetDependencyResolver(IContainerProvider containerProvide #endif } + /// + /// Customizes the registration name when using + /// + /// + /// This will be removed in a future version once we have a separate code gen tool + /// + /// The page + /// The name used while navigating protected virtual string GetNavigationSegmentNameFromType(Type pageType) => pageType.Name; - /// /// Creates the container used by Prism. /// @@ -205,6 +221,7 @@ protected virtual void RegisterRequiredTypes(IContainerRegistry containerRegistr containerRegistry.RegisterSingleton(); containerRegistry.RegisterSingleton(); containerRegistry.RegisterSingleton(); + containerRegistry.RegisterScoped(); containerRegistry.Register(NavigationServiceName); } @@ -236,6 +253,13 @@ protected virtual void InitializeModules() /// protected abstract void OnInitialized(); + /// + /// Application developers override this method to perform actions when the application + /// resumes from a sleeping state + /// + /// + /// Be sure to call base.OnResume() or you will lose support for IApplicationLifecycleAware + /// protected override void OnResume() { if (MainPage != null) @@ -245,6 +269,13 @@ protected override void OnResume() } } + /// + /// Application developers override this method to perform actions when the application + /// enters the sleeping state + /// + /// + /// Be sure to call base.OnSleep() or you will lose support for IApplicationLifecycleAware + /// protected override void OnSleep() { if (MainPage != null) diff --git a/src/Forms/Prism.Unity.Forms/Prism.Unity.Forms.csproj b/src/Forms/Prism.Unity.Forms/Prism.Unity.Forms.csproj index ffc4623ea9..b6f4212a1d 100644 --- a/src/Forms/Prism.Unity.Forms/Prism.Unity.Forms.csproj +++ b/src/Forms/Prism.Unity.Forms/Prism.Unity.Forms.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/Prism.Core/Ioc/IContainerProvider.cs b/src/Prism.Core/Ioc/IContainerProvider.cs index 95f1fca461..5fbd036a9a 100644 --- a/src/Prism.Core/Ioc/IContainerProvider.cs +++ b/src/Prism.Core/Ioc/IContainerProvider.cs @@ -42,6 +42,11 @@ public interface IContainerProvider /// /// Creates a new scope /// - void CreateScope(); + IScopedProvider CreateScope(); + + /// + /// Gets the Current Scope + /// + IScopedProvider CurrentScope { get; } } } diff --git a/src/Prism.Core/Ioc/IScopedProvider.cs b/src/Prism.Core/Ioc/IScopedProvider.cs new file mode 100644 index 0000000000..bbf3dcc99c --- /dev/null +++ b/src/Prism.Core/Ioc/IScopedProvider.cs @@ -0,0 +1,18 @@ +using System; + +namespace Prism.Ioc +{ + /// + /// Defines a Container Scope + /// + public interface IScopedProvider : IContainerProvider, IDisposable + { + /// + /// Gets or Sets the IsAttached property. + /// + /// + /// Indicates that Prism is tracking the scope + /// + bool IsAttached { get; set; } + } +} diff --git a/src/Uno/Prism.Unity.Uno/Prism.Unity.Uno.csproj b/src/Uno/Prism.Unity.Uno/Prism.Unity.Uno.csproj index d2eeccc35b..7646578d84 100644 --- a/src/Uno/Prism.Unity.Uno/Prism.Unity.Uno.csproj +++ b/src/Uno/Prism.Unity.Uno/Prism.Unity.Uno.csproj @@ -31,7 +31,7 @@ - + diff --git a/src/Wpf/Prism.Unity.Wpf/Prism.Unity.Wpf.csproj b/src/Wpf/Prism.Unity.Wpf/Prism.Unity.Wpf.csproj index 848dad51cd..813411385f 100644 --- a/src/Wpf/Prism.Unity.Wpf/Prism.Unity.Wpf.csproj +++ b/src/Wpf/Prism.Unity.Wpf/Prism.Unity.Wpf.csproj @@ -13,7 +13,7 @@ - + diff --git a/tests/Forms/MockApp/MockApp.csproj b/tests/Forms/MockApp/MockApp.csproj index 2c374e3262..bab2af9d80 100644 --- a/tests/Forms/MockApp/MockApp.csproj +++ b/tests/Forms/MockApp/MockApp.csproj @@ -5,7 +5,7 @@ - + diff --git a/tests/Forms/Prism.DI.Forms.Tests/Fixtures/FixtureBase.cs b/tests/Forms/Prism.DI.Forms.Tests/Fixtures/FixtureBase.cs index e5a5200b88..66e5ff1380 100644 --- a/tests/Forms/Prism.DI.Forms.Tests/Fixtures/FixtureBase.cs +++ b/tests/Forms/Prism.DI.Forms.Tests/Fixtures/FixtureBase.cs @@ -1,24 +1,31 @@ +using System; using Prism.DI.Forms.Tests.Mocks; +using Prism.Ioc; using Xamarin.Forms; -using Xunit; using Xunit.Abstractions; namespace Prism.DI.Forms.Tests.Fixtures { - public abstract class FixtureBase + public abstract class FixtureBase : IDisposable { protected ITestOutputHelper _testOutputHelper { get; } - public FixtureBase(ITestOutputHelper testOutputHelper) + protected FixtureBase(ITestOutputHelper testOutputHelper) { _testOutputHelper = testOutputHelper; + ContainerLocator.ResetContainer(); Xamarin.Forms.Mocks.MockForms.Init(); } - protected PrismApplicationMock CreateMockApplication(Page view = null) + protected PrismApplicationMock CreateMockApplication(Func viewFactory = null) { var initializer = new XunitPlatformInitializer(_testOutputHelper); - return view == null ? new PrismApplicationMock(initializer) : new PrismApplicationMock(initializer, view); + return viewFactory == null ? new PrismApplicationMock(initializer) : new PrismApplicationMock(initializer, viewFactory); + } + + public void Dispose() + { + ContainerLocator.ResetContainer(); } } } \ No newline at end of file diff --git a/tests/Forms/Prism.DI.Forms.Tests/Fixtures/PrismApplicationFixture.cs b/tests/Forms/Prism.DI.Forms.Tests/Fixtures/PrismApplicationFixture.cs index 4007c3d76e..d7f81ecf43 100644 --- a/tests/Forms/Prism.DI.Forms.Tests/Fixtures/PrismApplicationFixture.cs +++ b/tests/Forms/Prism.DI.Forms.Tests/Fixtures/PrismApplicationFixture.cs @@ -59,8 +59,8 @@ public void ResolvesCustom_ILoggerFacade() [Fact] public void OnInitialized_SetPage() { - var view = new ViewMock(); - var app = CreateMockApplication(view); + Page view = null; + var app = CreateMockApplication(() => view = new ViewMock()); Assert.True(app.Initialized); Assert.NotNull(Application.Current.MainPage); Assert.Same(view, Application.Current.MainPage); @@ -224,7 +224,8 @@ public void CustomNavigation_Resolved_In_ViewModel() [Fact] public void CustomNamedNavigationService_Resolved_In_ViewModel() { - var app = CreateMockApplication(new CustomNamedNavService()); + var app = CreateMockApplication(); + app.MainPage = new CustomNamedNavService(); var vm = app.MainPage.BindingContext as CustomNamedNavServiceViewModel; Assert.NotNull(vm); diff --git a/tests/Forms/Prism.DI.Forms.Tests/Mocks/PrismApplicationCustomNavMock.cs b/tests/Forms/Prism.DI.Forms.Tests/Mocks/PrismApplicationCustomNavMock.cs index 5f12a240cd..87743ec184 100644 --- a/tests/Forms/Prism.DI.Forms.Tests/Mocks/PrismApplicationCustomNavMock.cs +++ b/tests/Forms/Prism.DI.Forms.Tests/Mocks/PrismApplicationCustomNavMock.cs @@ -15,10 +15,7 @@ public PrismApplicationCustomNavMock(IPlatformInitializer initializer) protected override void RegisterTypes(IContainerRegistry containerRegistry) { -#if DryIoc - containerRegistry.Register(); -#endif - containerRegistry.Register(NavigationServiceName); + containerRegistry.RegisterScoped(); } public INavigationService GetNavigationService() => NavigationService; diff --git a/tests/Forms/Prism.DI.Forms.Tests/Mocks/PrismApplicationMock.cs b/tests/Forms/Prism.DI.Forms.Tests/Mocks/PrismApplicationMock.cs index 462bc3ab72..83f1093f73 100644 --- a/tests/Forms/Prism.DI.Forms.Tests/Mocks/PrismApplicationMock.cs +++ b/tests/Forms/Prism.DI.Forms.Tests/Mocks/PrismApplicationMock.cs @@ -9,6 +9,7 @@ using Prism.Logging; using Prism.Forms.Tests.Mocks.Logging; using Prism.Mvvm; +using System; #if Autofac using Prism.Autofac; using Autofac; @@ -32,10 +33,10 @@ public PrismApplicationMock(IPlatformInitializer platformInitializer) { } - public PrismApplicationMock(IPlatformInitializer platformInitializer, Page startPage) + public PrismApplicationMock(IPlatformInitializer platformInitializer, Func startPage) : this(platformInitializer) { - Current.MainPage = startPage; + MainPage = startPage?.Invoke(); } public new INavigationService NavigationService => base.NavigationService; diff --git a/tests/Forms/Prism.DI.Forms.Tests/Mocks/PrismApplicationMockPlatformAware.cs b/tests/Forms/Prism.DI.Forms.Tests/Mocks/PrismApplicationMockPlatformAware.cs index bb86a63522..82de877ade 100644 --- a/tests/Forms/Prism.DI.Forms.Tests/Mocks/PrismApplicationMockPlatformAware.cs +++ b/tests/Forms/Prism.DI.Forms.Tests/Mocks/PrismApplicationMockPlatformAware.cs @@ -9,15 +9,10 @@ using Prism.Logging; using Prism.Forms.Tests.Mocks.Logging; using Prism.Mvvm; -#if Autofac -using Prism.Autofac; -using Autofac; -#elif DryIoc +using System; +#if DryIoc using Prism.DryIoc; using DryIoc; -#elif Ninject -using Prism.Ninject; -using Ninject; #elif Unity using Prism.Unity; using Unity; @@ -32,7 +27,7 @@ public PrismApplicationMockPlatformAware(IPlatformInitializer platformInitialize { } - public PrismApplicationMockPlatformAware(IPlatformInitializer platformInitializer, Page startPage) + public PrismApplicationMockPlatformAware(IPlatformInitializer platformInitializer, Func startPage) : base(platformInitializer, startPage) { } diff --git a/tests/Forms/Prism.DryIoc.Forms.Tests/Prism.DryIoc.Forms.Tests.csproj b/tests/Forms/Prism.DryIoc.Forms.Tests/Prism.DryIoc.Forms.Tests.csproj index 4df2eda48c..25b351361a 100644 --- a/tests/Forms/Prism.DryIoc.Forms.Tests/Prism.DryIoc.Forms.Tests.csproj +++ b/tests/Forms/Prism.DryIoc.Forms.Tests/Prism.DryIoc.Forms.Tests.csproj @@ -8,10 +8,9 @@ - - - - + + + all diff --git a/tests/Forms/Prism.Forms.Tests/Common/PageUtilitiesFixture.cs b/tests/Forms/Prism.Forms.Tests/Common/PageUtilitiesFixture.cs index c62eee44ab..7d2784f36d 100644 --- a/tests/Forms/Prism.Forms.Tests/Common/PageUtilitiesFixture.cs +++ b/tests/Forms/Prism.Forms.Tests/Common/PageUtilitiesFixture.cs @@ -16,6 +16,7 @@ public class PageUtilitiesFixture public PageUtilitiesFixture(ITestOutputHelper output) { _output = output; + Xamarin.Forms.Mocks.MockForms.Init(); } [Fact] diff --git a/tests/Forms/Prism.Forms.Tests/Mocks/PageNavigationContainerMock.cs b/tests/Forms/Prism.Forms.Tests/Mocks/PageNavigationContainerMock.cs index f64de962b2..c1e0d8368a 100644 --- a/tests/Forms/Prism.Forms.Tests/Mocks/PageNavigationContainerMock.cs +++ b/tests/Forms/Prism.Forms.Tests/Mocks/PageNavigationContainerMock.cs @@ -1,4 +1,5 @@ -using Prism.Ioc; +using Moq; +using Prism.Ioc; using Prism.Navigation; using System; using System.Collections.Generic; @@ -11,6 +12,8 @@ public class PageNavigationContainerMock : IContainerExtension, IDisposable public object Instance => throw new NotImplementedException(); + public IScopedProvider CurrentScope { get; private set; } + public IContainerRegistry Register(string key, Type type) { if (!_registeredPages.ContainsKey(key)) @@ -109,9 +112,10 @@ public object Resolve(Type type, string name, params (Type Type, object Instance throw new NotImplementedException(); } - public void CreateScope() + public IScopedProvider CreateScope() { - throw new NotImplementedException(); + CurrentScope = Mock.Of(); + return CurrentScope; } public IContainerRegistry RegisterSingleton(Type type, Func factoryMethod) diff --git a/tests/Forms/Prism.Forms.Tests/Modularity/ModuleFixture.cs b/tests/Forms/Prism.Forms.Tests/Modularity/ModuleFixture.cs index ea715d1279..39f63befcc 100644 --- a/tests/Forms/Prism.Forms.Tests/Modularity/ModuleFixture.cs +++ b/tests/Forms/Prism.Forms.Tests/Modularity/ModuleFixture.cs @@ -9,6 +9,11 @@ namespace Prism.Forms.Tests.Modularity { public class ModuleFixture { + public ModuleFixture() + { + Xamarin.Forms.Mocks.MockForms.Init(); + } + [Fact] public void BasicCatalogHasThreeModules() { diff --git a/tests/Forms/Prism.Forms.Tests/Navigation/INavigationServiceExtensionsFixture.cs b/tests/Forms/Prism.Forms.Tests/Navigation/INavigationServiceExtensionsFixture.cs index f826bb92d0..f6795939c2 100644 --- a/tests/Forms/Prism.Forms.Tests/Navigation/INavigationServiceExtensionsFixture.cs +++ b/tests/Forms/Prism.Forms.Tests/Navigation/INavigationServiceExtensionsFixture.cs @@ -1,4 +1,5 @@ -using Prism.Common; +using Moq; +using Prism.Common; using Prism.Forms.Tests.Mocks; using Prism.Forms.Tests.Mocks.Views; using Prism.Forms.Tests.Navigation.Mocks.Views; @@ -13,10 +14,14 @@ namespace Prism.Forms.Tests.Navigation { [Collection(nameof(PageNavigation))] - public class INavigationServiceExtensionsFixture + public class INavigationServiceExtensionsFixture : IDisposable { public INavigationServiceExtensionsFixture() { + ContainerLocator.ResetContainer(); + var container = new Mock(); + container.Setup(x => x.CreateScope()).Returns(Mock.Of()); + ContainerLocator.SetContainerExtension(() => container.Object); PageNavigationRegistry.ClearRegistrationCache(); PageNavigationRegistry.Register("NavigationPage", typeof(NavigationPage)); @@ -234,5 +239,10 @@ public void GetNavigationUriPath5() path = ((NavigationPathPageMock)tabbedpage.Children[0]).ViewModel.NavigationService.GetNavigationUriPath(); Assert.Equal("/MasterDetailPage/NavigationPage/TabbedPage1?selectedTab=Page1", path); } + + public void Dispose() + { + ContainerLocator.ResetContainer(); + } } } diff --git a/tests/Forms/Prism.Forms.Tests/Navigation/PageNavigationServiceFixture.cs b/tests/Forms/Prism.Forms.Tests/Navigation/PageNavigationServiceFixture.cs index 522ec09460..6e2e231695 100644 --- a/tests/Forms/Prism.Forms.Tests/Navigation/PageNavigationServiceFixture.cs +++ b/tests/Forms/Prism.Forms.Tests/Navigation/PageNavigationServiceFixture.cs @@ -1,4 +1,5 @@ -using Prism.Common; +using Moq; +using Prism.Common; using Prism.Forms.Tests.Mocks; using Prism.Forms.Tests.Mocks.ViewModels; using Prism.Forms.Tests.Mocks.Views; @@ -24,7 +25,9 @@ public PageNavigationServiceFixture() { Xamarin.Forms.Mocks.MockForms.Init(); + ContainerLocator.ResetContainer(); _container = new PageNavigationContainerMock(); + ContainerLocator.SetContainerExtension(() => _container); _container.Register("PageMock", typeof(PageMock)); diff --git a/tests/Forms/Prism.Forms.Tests/Prism.Forms.Tests.csproj b/tests/Forms/Prism.Forms.Tests/Prism.Forms.Tests.csproj index a4ceffc23b..b97c902ea5 100644 --- a/tests/Forms/Prism.Forms.Tests/Prism.Forms.Tests.csproj +++ b/tests/Forms/Prism.Forms.Tests/Prism.Forms.Tests.csproj @@ -7,11 +7,10 @@ - - - - - + + + + all diff --git a/tests/Forms/Prism.Forms.Tests/Services/Mocks/Ioc/DialogContainerExtension.cs b/tests/Forms/Prism.Forms.Tests/Services/Mocks/Ioc/DialogContainerExtension.cs index d1bbb4d515..d75e2943a5 100644 --- a/tests/Forms/Prism.Forms.Tests/Services/Mocks/Ioc/DialogContainerExtension.cs +++ b/tests/Forms/Prism.Forms.Tests/Services/Mocks/Ioc/DialogContainerExtension.cs @@ -10,6 +10,8 @@ public class DialogContainerExtension : IContainerExtension { private readonly MockContainer _container = new MockContainer(); + public IScopedProvider CurrentScope { get; } + public void CreateScope() { throw new NotImplementedException(); @@ -136,6 +138,11 @@ public object Resolve(Type type, string name, params (Type Type, object Instance throw new NotImplementedException(); } + IScopedProvider IContainerProvider.CreateScope() + { + throw new NotImplementedException(); + } + private class MockContainer { private readonly IList _registrations = new List(); diff --git a/tests/Forms/Prism.Unity.Forms.Tests/Prism.Unity.Forms.Tests.csproj b/tests/Forms/Prism.Unity.Forms.Tests/Prism.Unity.Forms.Tests.csproj index 3dcc490458..b3ac2fde45 100644 --- a/tests/Forms/Prism.Unity.Forms.Tests/Prism.Unity.Forms.Tests.csproj +++ b/tests/Forms/Prism.Unity.Forms.Tests/Prism.Unity.Forms.Tests.csproj @@ -8,10 +8,9 @@ - - - - + + + all diff --git a/tests/Prism.Core.Tests/Prism.Core.Tests.csproj b/tests/Prism.Core.Tests/Prism.Core.Tests.csproj index b580a7970c..a0184069f4 100644 --- a/tests/Prism.Core.Tests/Prism.Core.Tests.csproj +++ b/tests/Prism.Core.Tests/Prism.Core.Tests.csproj @@ -6,8 +6,8 @@ - - + + all diff --git a/tests/Wpf/Prism.DryIoc.Wpf.Tests/Prism.DryIoc.Wpf.Tests.csproj b/tests/Wpf/Prism.DryIoc.Wpf.Tests/Prism.DryIoc.Wpf.Tests.csproj index fe3852eac8..8aa6e0d580 100644 --- a/tests/Wpf/Prism.DryIoc.Wpf.Tests/Prism.DryIoc.Wpf.Tests.csproj +++ b/tests/Wpf/Prism.DryIoc.Wpf.Tests/Prism.DryIoc.Wpf.Tests.csproj @@ -8,8 +8,8 @@ - - + + all diff --git a/tests/Wpf/Prism.Unity.Wpf.Tests/Prism.Unity.Wpf.Tests.csproj b/tests/Wpf/Prism.Unity.Wpf.Tests/Prism.Unity.Wpf.Tests.csproj index 00d9e9c160..a7a578a060 100644 --- a/tests/Wpf/Prism.Unity.Wpf.Tests/Prism.Unity.Wpf.Tests.csproj +++ b/tests/Wpf/Prism.Unity.Wpf.Tests/Prism.Unity.Wpf.Tests.csproj @@ -7,8 +7,8 @@ - - + + all diff --git a/tests/Wpf/Prism.Wpf.Tests/Mocks/MockContainerAdapter.cs b/tests/Wpf/Prism.Wpf.Tests/Mocks/MockContainerAdapter.cs index 6ab160c284..4afe945087 100644 --- a/tests/Wpf/Prism.Wpf.Tests/Mocks/MockContainerAdapter.cs +++ b/tests/Wpf/Prism.Wpf.Tests/Mocks/MockContainerAdapter.cs @@ -8,6 +8,8 @@ internal class MockContainerAdapter : IContainerExtension { public Dictionary ResolvedInstances = new Dictionary(); + public IScopedProvider CurrentScope { get; } + public void CreateScope() { throw new NotImplementedException(); @@ -133,5 +135,10 @@ public object Resolve(Type type, string name, params (Type Type, object Instance { throw new NotImplementedException(); } + + IScopedProvider IContainerProvider.CreateScope() + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/tests/Wpf/Prism.Wpf.Tests/Prism.Wpf.Tests.csproj b/tests/Wpf/Prism.Wpf.Tests/Prism.Wpf.Tests.csproj index 4be5b5fe45..3a7069ab11 100644 --- a/tests/Wpf/Prism.Wpf.Tests/Prism.Wpf.Tests.csproj +++ b/tests/Wpf/Prism.Wpf.Tests/Prism.Wpf.Tests.csproj @@ -28,8 +28,8 @@ - - + + all