diff --git a/Source/Xamarin/Prism.Forms.Tests/Navigation/INavigationServiceExtensionsFixture.cs b/Source/Xamarin/Prism.Forms.Tests/Navigation/INavigationServiceExtensionsFixture.cs new file mode 100644 index 000000000..52094d772 --- /dev/null +++ b/Source/Xamarin/Prism.Forms.Tests/Navigation/INavigationServiceExtensionsFixture.cs @@ -0,0 +1,93 @@ +using Prism.Common; +using Prism.Forms.Tests.Mocks; +using Prism.Forms.Tests.Mocks.Views; +using Prism.Navigation; +using System.Threading.Tasks; +using Xunit; + +namespace Prism.Forms.Tests.Navigation +{ + public class INavigationServiceExtensionsFixture + { + [Fact] + public async Task PopToRootAsync_PopsToRoot() + { + var navigationService = new PageNavigationServiceMock(null, null, null); + var rootPage = new Xamarin.Forms.NavigationPage(); + ((IPageAware)navigationService).Page = rootPage; + + var page1 = new ContentPageMock() { Title = "Page 1" }; + await rootPage.Navigation.PushAsync(page1); + + await rootPage.Navigation.PushAsync(new ContentPageMock() { Title = "Page 2" }); + await rootPage.Navigation.PushAsync(new ContentPageMock() { Title = "Page 3" }); + await rootPage.Navigation.PushAsync(new ContentPageMock() { Title = "Page 4" }); + + Assert.True(rootPage.Navigation.NavigationStack.Count == 4); + + await navigationService.PopToRootAsync(); + + Assert.Equal(1, rootPage.Navigation.NavigationStack.Count); + Assert.Equal(page1, rootPage.Navigation.NavigationStack[0]); + } + + [Fact] + public async Task PopToRootAsync_PopsToRoot_Destroy() + { + var recorder = new PageNavigationEventRecorder(); + var navigationService = new PageNavigationServiceMock(null, null, null); + var rootPage = new Xamarin.Forms.NavigationPage(); + ((IPageAware)navigationService).Page = rootPage; + + var page1 = new ContentPageMock(recorder) { Title = "Page 1" }; + await rootPage.Navigation.PushAsync(page1); + + var page2 = new ContentPageMock(recorder) { Title = "Page 2" }; + var page2ViewModel = page2.BindingContext; + await rootPage.Navigation.PushAsync(page2); + + var page3 = new ContentPageMock(recorder) { Title = "Page 3" }; + var page3ViewModel = page3.BindingContext; + await rootPage.Navigation.PushAsync(page3); + + var page4 = new ContentPageMock(recorder) { Title = "Page 4" }; + var page4ViewModel = page4.BindingContext; + await rootPage.Navigation.PushAsync(page4); + + Assert.True(rootPage.Navigation.NavigationStack.Count == 4); + + await navigationService.PopToRootAsync(); + + Assert.Equal(1, rootPage.Navigation.NavigationStack.Count); + Assert.Equal(page1, rootPage.Navigation.NavigationStack[0]); + Assert.Equal(6, recorder.Records.Count); + + //page 4 + var record = recorder.TakeFirst(); + Assert.Equal(page4, record.Sender); + Assert.Equal(PageNavigationEvent.Destroy, record.Event); + + record = recorder.TakeFirst(); + Assert.Equal(page4ViewModel, record.Sender); + Assert.Equal(PageNavigationEvent.Destroy, record.Event); + + //page 3 + record = recorder.TakeFirst(); + Assert.Equal(page3, record.Sender); + Assert.Equal(PageNavigationEvent.Destroy, record.Event); + + record = recorder.TakeFirst(); + Assert.Equal(page3ViewModel, record.Sender); + Assert.Equal(PageNavigationEvent.Destroy, record.Event); + + //page 2 + record = recorder.TakeFirst(); + Assert.Equal(page2, record.Sender); + Assert.Equal(PageNavigationEvent.Destroy, record.Event); + + record = recorder.TakeFirst(); + Assert.Equal(page2ViewModel, record.Sender); + Assert.Equal(PageNavigationEvent.Destroy, record.Event); + } + } +} diff --git a/Source/Xamarin/Prism.Forms/Navigation/INavigationServiceExtensions.cs b/Source/Xamarin/Prism.Forms/Navigation/INavigationServiceExtensions.cs new file mode 100644 index 000000000..a2f19c552 --- /dev/null +++ b/Source/Xamarin/Prism.Forms/Navigation/INavigationServiceExtensions.cs @@ -0,0 +1,33 @@ +using Prism.Common; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Xamarin.Forms; + +namespace Prism.Navigation +{ + public static class INavigationServiceExtensions + { + /// + /// When navigating inside a NavigationPage: Pops all but the root Page off the navigation stack + /// + /// The INavigatinService instance + /// Only works when called from a View within a NavigationPage + public static async Task PopToRootAsync(this INavigationService navigationService) + { + IPageAware pageAware = (IPageAware)navigationService; + + List pagesToDestroy = pageAware.Page.Navigation.NavigationStack.ToList(); // get all pages to destroy + pagesToDestroy.Reverse(); // destroy them in reverse order + pagesToDestroy.Remove(pagesToDestroy.Last()); //don't destroy the root page + + await pageAware.Page.Navigation.PopToRootAsync(); + + //BOOM! + foreach (var destroyPage in pagesToDestroy) + { + PageUtilities.DestroyPage(destroyPage); + } + } + } +}