protected virtual async Task UseReverseNavigation(Page currentPage, string nextSegment, Queue <string> segments, INavigationParameters parameters, bool?useModalNavigation, bool animated) { var navigationStack = new Stack <string>(); if (!String.IsNullOrWhiteSpace(nextSegment)) { navigationStack.Push(nextSegment); } var illegalSegments = new Queue <string>(); bool illegalPageFound = false; foreach (var item in segments) { //if we run into an illegal page, we need to create new navigation segments to properly handle the deep link if (illegalPageFound) { illegalSegments.Enqueue(item); continue; } //if any page decide to go modal, we need to consider it and all pages after it an illegal page var pageParameters = UriParsingHelper.GetSegmentParameters(item); if (pageParameters.ContainsKey(KnownNavigationParameters.UseModalNavigation)) { if (pageParameters.GetValue <bool>(KnownNavigationParameters.UseModalNavigation)) { illegalSegments.Enqueue(item); illegalPageFound = true; } else { navigationStack.Push(item); } } else { var pageType = PageNavigationRegistry.GetPageType(UriParsingHelper.GetSegmentName(item)); if (PageUtilities.IsSameOrSubclassOf <MasterDetailPage>(pageType)) { illegalSegments.Enqueue(item); illegalPageFound = true; } else { navigationStack.Push(item); } } } var pageOffset = currentPage.Navigation.NavigationStack.Count; if (currentPage.Navigation.NavigationStack.Count > 2) { pageOffset = currentPage.Navigation.NavigationStack.Count - 1; } var onNavigatedFromTarget = currentPage; if (currentPage is NavigationPage navPage && navPage.CurrentPage != null) { onNavigatedFromTarget = navPage.CurrentPage; } bool insertBefore = false; while (navigationStack.Count > 0) { var segment = navigationStack.Pop(); var nextPage = CreatePageFromSegment(segment); await DoNavigateAction(onNavigatedFromTarget, segment, nextPage, parameters, async() => { await DoPush(currentPage, nextPage, useModalNavigation, animated, insertBefore, pageOffset); }); insertBefore = true; } //if an illegal page is found, we force a Modal navigation if (illegalSegments.Count > 0) { await ProcessNavigation(currentPage.Navigation.NavigationStack.Last(), illegalSegments, parameters, true, animated); } }
protected virtual async Task ProcessNavigationForMasterDetailPage(MasterDetailPage currentPage, string nextSegment, Queue <string> segments, INavigationParameters parameters, bool?useModalNavigation, bool animated) { bool isPresented = GetMasterDetailPageIsPresented(currentPage); var detail = currentPage.Detail; if (detail == null) { var newDetail = CreatePageFromSegment(nextSegment); await ProcessNavigation(newDetail, segments, parameters, useModalNavigation, animated); await DoNavigateAction(null, nextSegment, newDetail, parameters, onNavigationActionCompleted : () => { currentPage.IsPresented = isPresented; currentPage.Detail = newDetail; }); return; } if (useModalNavigation.HasValue && useModalNavigation.Value) { var nextPage = CreatePageFromSegment(nextSegment); await ProcessNavigation(nextPage, segments, parameters, useModalNavigation, animated); await DoNavigateAction(currentPage, nextSegment, nextPage, parameters, async() => { currentPage.IsPresented = isPresented; await DoPush(currentPage, nextPage, true, animated); }); return; } var nextSegmentType = PageNavigationRegistry.GetPageType(UriParsingHelper.GetSegmentName(nextSegment)); //we must recreate the NavigationPage everytime or the transitions on iOS will not work properly, unless we meet the two scenarios below bool detailIsNavPage = false; bool reuseNavPage = false; if (detail is NavigationPage navPage) { detailIsNavPage = true; //we only care if we the next segment is also a NavigationPage. if (PageUtilities.IsSameOrSubclassOf <NavigationPage>(nextSegmentType)) { //first we check to see if we are being forced to reuse the NavPage by checking the interface reuseNavPage = !GetClearNavigationPageNavigationStack(navPage); if (!reuseNavPage) { //if we weren't forced to reuse the NavPage, then let's check the NavPage.CurrentPage against the next segment type as we don't want to recreate the entire nav stack //just in case the user is trying to navigate to the same page which may be nested in a NavPage var nextPageType = PageNavigationRegistry.GetPageType(UriParsingHelper.GetSegmentName(segments.Peek())); var currentPageType = navPage.CurrentPage.GetType(); if (nextPageType == currentPageType) { reuseNavPage = true; } } } } if ((detailIsNavPage && reuseNavPage) || (!detailIsNavPage && detail.GetType() == nextSegmentType)) { await ProcessNavigation(detail, segments, parameters, useModalNavigation, animated); await DoNavigateAction(null, nextSegment, detail, parameters, onNavigationActionCompleted : () => { if (detail is TabbedPage && nextSegment.Contains(KnownNavigationParameters.SelectedTab)) { var segmentParams = UriParsingHelper.GetSegmentParameters(nextSegment); SelectPageTab(detail, segmentParams); } currentPage.IsPresented = isPresented; }); return; } else { var newDetail = CreatePageFromSegment(nextSegment); await ProcessNavigation(newDetail, segments, parameters, newDetail is NavigationPage?false : true, animated); await DoNavigateAction(detail, nextSegment, newDetail, parameters, onNavigationActionCompleted : () => { if (detailIsNavPage) { OnNavigatedFrom(((NavigationPage)detail).CurrentPage, parameters); } currentPage.IsPresented = isPresented; currentPage.Detail = newDetail; PageUtilities.DestroyPage(detail); }); return; } }
void ConfigureTabbedPage(TabbedPage tabbedPage, string segment) { foreach (var child in tabbedPage.Children) { PageUtilities.SetAutowireViewModelOnPage(child); _pageBehaviorFactory.ApplyPageBehaviors(child); if (child is NavigationPage navPage) { PageUtilities.SetAutowireViewModelOnPage(navPage.CurrentPage); _pageBehaviorFactory.ApplyPageBehaviors(navPage.CurrentPage); } } var parameters = UriParsingHelper.GetSegmentParameters(segment); var tabsToCreate = parameters.GetValues <string>(KnownNavigationParameters.CreateTab); if (tabsToCreate.Count() > 0) { foreach (var tabToCreate in tabsToCreate) { //created tab can be a single view or a view nested in a NavigationPage with the syntax "NavigationPage|ViewToCreate" var tabSegements = tabToCreate.Split('|'); if (tabSegements.Length > 1) { var navigationPage = CreatePageFromSegment(tabSegements[0]) as NavigationPage; if (navigationPage != null) { var navigationPageChild = CreatePageFromSegment(tabSegements[1]); navigationPage.PushAsync(navigationPageChild); //when creating a NavigationPage w/ DI, a blank Page object is injected into the ctor. Let's remove it if (navigationPage.Navigation.NavigationStack.Count > 1) { navigationPage.Navigation.RemovePage(navigationPage.Navigation.NavigationStack[0]); } //set the title because Xamarin doesn't do this for us. navigationPage.Title = navigationPageChild.Title; navigationPage.Icon = navigationPageChild.Icon; tabbedPage.Children.Add(navigationPage); } } else { var tab = CreatePageFromSegment(tabToCreate); tabbedPage.Children.Add(tab); } } } var selectedTab = parameters.GetValue <string>(KnownNavigationParameters.SelectedTab); if (!string.IsNullOrWhiteSpace(selectedTab)) { var selectedTabType = PageNavigationRegistry.GetPageType(UriParsingHelper.GetSegmentName(selectedTab)); var childFound = false; foreach (var child in tabbedPage.Children) { if (!childFound && child.GetType() == selectedTabType) { tabbedPage.CurrentPage = child; childFound = true; } if (child is NavigationPage) { if (!childFound && ((NavigationPage)child).CurrentPage.GetType() == selectedTabType) { tabbedPage.CurrentPage = child; childFound = true; } } } } }
protected virtual async Task ProcessNavigationForNavigationPage(NavigationPage currentPage, string nextSegment, Queue <string> segments, INavigationParameters parameters, bool?useModalNavigation, bool animated) { if (currentPage.Navigation.NavigationStack.Count == 0) { await UseReverseNavigation(currentPage, nextSegment, segments, parameters, false, animated); return; } var clearNavigationStack = GetClearNavigationPageNavigationStack(currentPage); var isEmptyOfNavigationStack = currentPage.Navigation.NavigationStack.Count == 0; List <Page> destroyPages; if (clearNavigationStack && !isEmptyOfNavigationStack) { destroyPages = currentPage.Navigation.NavigationStack.ToList(); destroyPages.Reverse(); await currentPage.Navigation.PopToRootAsync(false); } else { destroyPages = new List <Page>(); } var topPage = currentPage.Navigation.NavigationStack.LastOrDefault(); var nextPageType = PageNavigationRegistry.GetPageType(UriParsingHelper.GetSegmentName(nextSegment)); if (topPage?.GetType() == nextPageType) { if (clearNavigationStack) { destroyPages.Remove(destroyPages.Last()); } if (segments.Count > 0) { await UseReverseNavigation(topPage, segments.Dequeue(), segments, parameters, false, animated); } await DoNavigateAction(topPage, nextSegment, topPage, parameters, onNavigationActionCompleted : () => { if (nextSegment.Contains(KnownNavigationParameters.SelectedTab)) { var segmentParams = UriParsingHelper.GetSegmentParameters(nextSegment); SelectPageTab(topPage, segmentParams); } }); } else { await UseReverseNavigation(currentPage, nextSegment, segments, parameters, false, animated); if (clearNavigationStack && !isEmptyOfNavigationStack) { currentPage.Navigation.RemovePage(topPage); } } foreach (var destroyPage in destroyPages) { PageUtilities.DestroyPage(destroyPage); } }
protected virtual async Task ProcessNavigationForNavigationPage(NavigationPage currentPage, string nextSegment, Queue <string> segments, NavigationParameters parameters, bool?useModalNavigation, bool animated) { if (currentPage.Navigation.NavigationStack.Count == 0) { await UseReverseNavigation(currentPage, nextSegment, segments, parameters, false, animated); return; } var clearNavigationStack = GetClearNavigationPageNavigationStack(currentPage); var isEmptyOfNavigationStack = currentPage.Navigation.NavigationStack.Count == 0; List <Page> destroyPages; if (clearNavigationStack && !isEmptyOfNavigationStack) { destroyPages = currentPage.Navigation.NavigationStack.ToList(); destroyPages.Reverse(); await currentPage.Navigation.PopToRootAsync(false); } else { destroyPages = new List <Page>(); } var topPage = currentPage.Navigation.NavigationStack.LastOrDefault(); var nextPageType = PageNavigationRegistry.GetPageType(UriParsingHelper.GetSegmentName(nextSegment)); if (topPage?.GetType() == nextPageType) { if (clearNavigationStack) { destroyPages.Remove(destroyPages.Last()); } await ProcessNavigation(topPage, segments, parameters, false, animated); await DoNavigateAction(topPage, nextSegment, topPage, parameters); } else { // Replace RootPage of NavigationStack var nextPage = CreatePageFromSegment(nextSegment); await ProcessNavigation(nextPage, segments, parameters, false, animated); await DoNavigateAction(topPage ?? currentPage, nextSegment, nextPage, parameters, async() => { var push = DoPush(currentPage, nextPage, false, animated); if (clearNavigationStack && !isEmptyOfNavigationStack) { currentPage.Navigation.RemovePage(topPage); } await push; }); } foreach (var destroyPage in destroyPages) { PageUtilities.DestroyPage(destroyPage); } }