/// <summary> /// Selects a Tab of the TabbedPage parent. /// </summary> /// <param name="name">The name of the tab to select</param> /// <param name="parameters">The navigation parameters</param> public static async Task <INavigationResult> SelectTabAsync(this INavigationService navigationService, string name, INavigationParameters parameters = null) { try { var currentPage = ((IPageAware)navigationService).Page; var canNavigate = await PageUtilities.CanNavigateAsync(currentPage, parameters); if (!canNavigate) { throw new Exception($"IConfirmNavigation for {currentPage} returned false"); } TabbedPage tabbedPage = null; if (currentPage.Parent is TabbedPage parent) { tabbedPage = parent; } else if (currentPage.Parent is NavigationPage navPage) { if (navPage.Parent != null && navPage.Parent is TabbedPage parent2) { tabbedPage = parent2; } } if (tabbedPage == null) { throw new Exception("No parent TabbedPage could be found"); } var tabToSelectedType = PageNavigationRegistry.GetPageType(UriParsingHelper.GetSegmentName(name)); if (tabToSelectedType is null) { throw new Exception($"No View Type has been registered for '{name}'"); } Page target = null; foreach (var child in tabbedPage.Children) { if (child.GetType() == tabToSelectedType) { target = child; break; } if (child is NavigationPage childNavPage) { if (childNavPage.CurrentPage.GetType() == tabToSelectedType || childNavPage.RootPage.GetType() == tabToSelectedType) { target = child; break; } } } if (target is null) { throw new Exception($"Could not find a Child Tab for '{name}'"); } var tabParameters = UriParsingHelper.GetSegmentParameters(name, parameters); tabbedPage.CurrentPage = target; PageUtilities.OnNavigatedFrom(currentPage, tabParameters); PageUtilities.OnNavigatedTo(target, tabParameters); } catch (Exception ex) { return(new NavigationResult { Exception = ex }); } return(new NavigationResult { Success = 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 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; //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 : () => { 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 : () => { currentPage.IsPresented = isPresented; currentPage.Detail = newDetail; PageUtilities.DestroyPage(detail); }); return; } }
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 (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); } }