diff --git a/e2e/Maui/MauiModule/ViewModels/ViewModelBase.cs b/e2e/Maui/MauiModule/ViewModels/ViewModelBase.cs index bd7b03805..8f1720b9a 100644 --- a/e2e/Maui/MauiModule/ViewModels/ViewModelBase.cs +++ b/e2e/Maui/MauiModule/ViewModels/ViewModelBase.cs @@ -3,7 +3,7 @@ namespace MauiModule.ViewModels; -public abstract class ViewModelBase : BindableBase, IInitialize, INavigatedAware, IPageLifecycleAware +public abstract class ViewModelBase : BindableBase, IInitialize, INavigatedAware, IPageLifecycleAware, IConfirmNavigation { protected INavigationService _navigationService { get; } protected IPageDialogService _pageDialogs { get; } @@ -109,4 +109,10 @@ public void OnDisappearing() { Messages.Add("View Disappearing"); } + + public virtual bool CanNavigate(INavigationParameters parameters) + { + Messages.Add("Can Navigate"); + return true; + } } diff --git a/e2e/Maui/PrismMauiDemo/MauiProgram.cs b/e2e/Maui/PrismMauiDemo/MauiProgram.cs index ca013122a..2c6d0d63c 100644 --- a/e2e/Maui/PrismMauiDemo/MauiProgram.cs +++ b/e2e/Maui/PrismMauiDemo/MauiProgram.cs @@ -38,21 +38,31 @@ public static MauiApp CreateMauiApp() if (status == "Failed" && !string.IsNullOrEmpty(x.Result?.Exception?.Message)) Console.Error.WriteLine(x.Result.Exception.Message); })) - //.CreateWindow(nav => nav.CreateBuilder() - // .AddTabbedSegment(page => - // page.CreateTab("ViewC") - // .CreateTab(t => - // t.AddNavigationPage() - // .AddSegment("ViewA", s => s.AddParameter("message", "Hello Tab - ViewA")) - // .AddSegment("ViewB", s => s.AddParameter("message", "Hello Tab - ViewB"))) - // //.CreateTab("ViewC", s => s.AddParameter("message", "Hello Tab - ViewC")) - // .SelectedTab("NavigationPage|ViewB")) - // .AddParameter("message_global", "This is a Global Message") - // .Navigate()) - //.CreateWindow("ViewA/ViewB/ViewC") - .CreateWindow(navigationService => navigationService.CreateBuilder() - .AddSegment() - .NavigateAsync(HandleNavigationError)) + //.CreateWindow(nav => nav.CreateBuilder() + // .AddTabbedSegment(page => + // page.CreateTab("ViewC") + // .CreateTab(t => + // t.AddNavigationPage() + // .AddSegment("ViewA", s => s.AddParameter("message", "Hello Tab - ViewA")) + // .AddSegment("ViewB", s => s.AddParameter("message", "Hello Tab - ViewB"))) + // //.CreateTab("ViewC", s => s.AddParameter("message", "Hello Tab - ViewC")) + // .SelectedTab("NavigationPage|ViewB")) + // .AddParameter("message_global", "This is a Global Message") + // .Navigate()) + //.CreateWindow("ViewA/ViewB") //broken + //.CreateWindow("NavigationPage/TabbedPage?createTab=ViewB/ViewC") //works + //.CreateWindow("ViewA/NavigationPage/TabbedPage?createTab=ViewB/ViewC") //works + //.CreateWindow(nav => nav + // .CreateBuilder() + // .AddTabbedSegment(page => + // page.CreateTab(t => + // t.AddNavigationPage() + // .AddSegment("ViewA") + // .AddSegment("ViewB"))) + // .NavigateAsync()) //works + .CreateWindow(navigationService => navigationService.CreateBuilder() + .AddSegment() + .NavigateAsync(HandleNavigationError)) ) .ConfigureFonts(fonts => { diff --git a/src/Maui/Prism.Maui/Controls/PrismNavigationPage.cs b/src/Maui/Prism.Maui/Controls/PrismNavigationPage.cs index 78964d7ac..a8b8d4ab0 100644 --- a/src/Maui/Prism.Maui/Controls/PrismNavigationPage.cs +++ b/src/Maui/Prism.Maui/Controls/PrismNavigationPage.cs @@ -1,4 +1,4 @@ -using Prism.Common; +using Prism.Common; using Prism.Navigation; using UIModalPresentationStyle = Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.UIModalPresentationStyle; @@ -12,10 +12,7 @@ public class PrismNavigationPage : NavigationPage /// /// Creates a new instance of the /// - public PrismNavigationPage() - { - BackButtonPressed += HandleBackButtonPressed; - } + public PrismNavigationPage() { } /// /// Creates a new instance of the with a specified at the Root @@ -23,23 +20,13 @@ public PrismNavigationPage() /// public PrismNavigationPage(Page page) : base(page) - { - BackButtonPressed += HandleBackButtonPressed; - } + { } /// - public event EventHandler BackButtonPressed; - - /// - protected override bool OnBackButtonPressed() - { - BackButtonPressed.Invoke(this, EventArgs.Empty); - return false; - } - - private async void HandleBackButtonPressed(object sender, EventArgs args) + protected sealed override bool OnBackButtonPressed() { - await MvvmHelpers.HandleNavigationPageGoBack(this); + MvvmHelpers.HandleNavigationPageGoBack(this).ConfigureAwait(false); + return true; //Prism will always handle the navigation } #if IOS diff --git a/src/Maui/Prism.Maui/Navigation/PageNavigationService.cs b/src/Maui/Prism.Maui/Navigation/PageNavigationService.cs index aecc7eb79..8a3cc401c 100644 --- a/src/Maui/Prism.Maui/Navigation/PageNavigationService.cs +++ b/src/Maui/Prism.Maui/Navigation/PageNavigationService.cs @@ -2,9 +2,7 @@ using System.Web; using Prism.Common; using Prism.Events; -using Prism.Extensions; using Prism.Mvvm; -using Prism.Navigation.Regions; using Application = Microsoft.Maui.Controls.Application; using XamlTab = Prism.Navigation.Xaml.TabbedPage; @@ -100,10 +98,6 @@ private async Task GoBackInternalAsync(INavigationParameters NavigationSource = PageNavigationSource.NavigationService; page = GetCurrentPage(); - if (IsRoot(GetPageFromWindow(), page)) - { - return SendAppToBackground(page); - } parameters.GetNavigationParametersInternal().Add(KnownInternalParameters.NavigationMode, NavigationMode.Back); @@ -113,6 +107,11 @@ private async Task GoBackInternalAsync(INavigationParameters throw new NavigationException(NavigationException.IConfirmNavigationReturnedFalse, page); } + if (IsRoot(GetPageFromWindow(), page)) + { + return SendAppToBackground(page); + } + bool useModalForDoPop = UseModalGoBack(page, parameters); Page previousPage = MvvmHelpers.GetOnNavigatedToTarget(page, Window?.Page, useModalForDoPop); @@ -1249,6 +1248,8 @@ private bool GoBackModal(NavigationPage navPage) return true; else if (navPage.Parent is TabbedPage tabbed && tabbed != rootPage) return true; + else if (rootPage != navPage || IsRoot(rootPage, navPage.CurrentPage)) + return true; return false; } diff --git a/src/Maui/Prism.Maui/Navigation/PrismWindow.cs b/src/Maui/Prism.Maui/Navigation/PrismWindow.cs index 83a81fed8..ee4210563 100644 --- a/src/Maui/Prism.Maui/Navigation/PrismWindow.cs +++ b/src/Maui/Prism.Maui/Navigation/PrismWindow.cs @@ -84,14 +84,14 @@ public void OnSystemBack() if (dialogContainer.Dismiss.CanExecute(null)) dialogContainer.Dismiss.Execute(null); } - else + else if (PageNavigationService.NavigationSource == PageNavigationSource.Device) { var navigation = container.Resolve(); navigation.GoBackAsync(); } } - private bool IsRoot(Page page) + internal bool IsRoot(Page page) { if (page == Page) return true; diff --git a/src/Maui/Prism.Maui/PrismAppBuilder.cs b/src/Maui/Prism.Maui/PrismAppBuilder.cs index f4a1b7452..ef06115ec 100644 --- a/src/Maui/Prism.Maui/PrismAppBuilder.cs +++ b/src/Maui/Prism.Maui/PrismAppBuilder.cs @@ -78,11 +78,6 @@ internal PrismAppBuilder(IContainerExtension containerExtension, MauiAppBuilder if (window is null) return false; - if (window.CurrentPage?.Parent is NavigationPage) - { - return true; - } - if(window.IsRootPage) { return false; diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Navigation/NavigationTests.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Navigation/NavigationTests.cs index 904282884..cee39c5c1 100644 --- a/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Navigation/NavigationTests.cs +++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Navigation/NavigationTests.cs @@ -5,8 +5,10 @@ using Prism.DryIoc.Maui.Tests.Mocks.ViewModels; using Prism.DryIoc.Maui.Tests.Mocks.Views; using Prism.Navigation.Xaml; +using Prism.Navigation; using Prism.Xaml; using TabbedPage = Microsoft.Maui.Controls.TabbedPage; +using Microsoft.Maui.LifecycleEvents; namespace Prism.DryIoc.Maui.Tests.Fixtures.Navigation; @@ -30,7 +32,7 @@ public void PagesInjectScopedInstanceOfIPageAccessor(string uri) var rootPage = window.Page; - if(rootPage is FlyoutPage flyoutPage) + if (rootPage is FlyoutPage flyoutPage) { TestPage(flyoutPage); rootPage = flyoutPage.Detail; @@ -659,6 +661,53 @@ public void Navigate_And_SelectTab(string selectTab, Type viewType) Assert.IsType(viewType, child); } + [Fact] + public async Task Issue3123_GoBack_SendsAppToBackground() + { + var errorInvoked = false; + var mauiApp = CreateBuilder(prism => prism + .AddGlobalNavigationObserver(context => context.Subscribe(x => + { + //this error message is used in this unit test to know that the SendAppToBackground method was called + //for Android we send the app to the background and do not throw the exception, otherwise we throw and quit the app + if (!x.Result.Success && x.Result?.Exception?.Message == NavigationException.CannotPopApplicationMainPage) + errorInvoked = true; + })) + .CreateWindow(nav => nav.CreateBuilder() + .AddTabbedSegment(page => + page.CreateTab(t => + t.AddNavigationPage() + .AddSegment("MockViewA") + .AddSegment("MockViewB"))) + .NavigateAsync())) + .Build(); + var window = GetWindow(mauiApp); + + var tabbedPage = window.Page as TabbedPage; + Assert.NotNull(tabbedPage); + + var currentPage = tabbedPage.CurrentPage; + Assert.IsType(currentPage); + Assert.Equal(2, currentPage.Navigation.NavigationStack.Count); + + var navPage = (PrismNavigationPage)currentPage; + Assert.IsType(navPage.CurrentPage); + + var container = navPage.CurrentPage.GetContainerProvider(); + var navService = container.Resolve(); + await navService.GoBackAsync(); + + Assert.False(errorInvoked); + Assert.IsType(navPage.CurrentPage); + Assert.Single(currentPage.Navigation.NavigationStack); + + container = navPage.CurrentPage.GetContainerProvider(); + navService = container.Resolve(); + await navService.GoBackAsync(); + + Assert.True(errorInvoked); + } + private static void TestPage(Page page, bool ignoreNavigationPage = false) { Assert.NotNull(page.BindingContext); @@ -671,7 +720,7 @@ private static void TestPage(Page page, bool ignoreNavigationPage = false) Assert.NotNull(accessor.Page); Assert.Same(page, accessor.Page); - if(page.Parent is not null) + if (page.Parent is not null) { Assert.False(page.BindingContext == page); Assert.False(page.BindingContext == page.Parent); @@ -690,7 +739,7 @@ private static void TestPage(Page page, bool ignoreNavigationPage = false) if (page is TabbedPage tabbedPage) { - foreach(var child in tabbedPage.Children) + foreach (var child in tabbedPage.Children) { TestPage(child, tabbedPage is MockExplicitTabbedPage); @@ -734,7 +783,7 @@ private static void TestPageBehaviors(Page page) Assert.Equal(expectedBehaviors, page.Behaviors.Count); - switch(page) + switch (page) { case TabbedPage: TestTabbedPageBehaviors(page); diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Prism.DryIoc.Maui.Tests.csproj b/tests/Maui/Prism.DryIoc.Maui.Tests/Prism.DryIoc.Maui.Tests.csproj index 15d1c2b6c..8e3eaacd0 100644 --- a/tests/Maui/Prism.DryIoc.Maui.Tests/Prism.DryIoc.Maui.Tests.csproj +++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Prism.DryIoc.Maui.Tests.csproj @@ -29,6 +29,7 @@ +