public static ShellNavigationState GetNavigationState(ShellItem shellItem, ShellSection shellSection, ShellContent shellContent, IReadOnlyList <Page> sectionStack, IReadOnlyList <Page> modalStack) { List <string> routeStack = new List <string>(); bool stackAtRoot = sectionStack == null || sectionStack.Count <= 1; bool hasUserDefinedRoute = (Routing.IsUserDefined(shellItem)) || (Routing.IsUserDefined(shellSection)) || (Routing.IsUserDefined(shellContent)); if (shellItem != null) { var shellItemRoute = shellItem.Route; routeStack.Add(shellItemRoute); if (shellSection != null) { var shellSectionRoute = shellSection.Route; routeStack.Add(shellSectionRoute); if (shellContent != null) { var shellContentRoute = shellContent.Route; routeStack.Add(shellContentRoute); } if (!stackAtRoot) { for (int i = 1; i < sectionStack.Count; i++) { var page = sectionStack[i]; routeStack.AddRange(ShellUriHandler.CollapsePath(Routing.GetRoute(page), routeStack, hasUserDefinedRoute)); } } if (modalStack != null && modalStack.Count > 0) { for (int i = 0; i < modalStack.Count; i++) { var topPage = modalStack[i]; routeStack.AddRange(ShellUriHandler.CollapsePath(Routing.GetRoute(topPage), routeStack, hasUserDefinedRoute)); for (int j = 1; j < topPage.Navigation.NavigationStack.Count; j++) { routeStack.AddRange(ShellUriHandler.CollapsePath(Routing.GetRoute(topPage.Navigation.NavigationStack[j]), routeStack, hasUserDefinedRoute)); } } } } } if (routeStack.Count > 0) { routeStack.Insert(0, "/"); } return(new ShellNavigationState(String.Join("/", routeStack), true)); }
internal static Uri FormatUri(Uri path, Shell shell) { if (path.OriginalString.StartsWith("..") && shell?.CurrentState != null) { var pages = ShellNavigationManager.BuildFlattenedNavigationStack(shell); var currentState = shell.CurrentState.FullLocation.OriginalString; List <string> restOfPath = new List <string>(); bool dotsAllParsed = false; foreach (var p in path.OriginalString.Split(_pathSeparators)) { if (p != ".." || dotsAllParsed) { dotsAllParsed = true; restOfPath.Add(p); continue; } var lastPage = pages[pages.Count - 1]; if (lastPage == null) { break; } pages.Remove(lastPage); List <string> buildUpPages = new List <string>(); foreach (var page in pages) { if (page == null) { continue; } var route = Routing.GetRoute(page); buildUpPages.AddRange(CollapsePath(route, buildUpPages, false)); } restOfPath = buildUpPages; } restOfPath.Insert(0, shell.CurrentItem.CurrentItem.CurrentItem.Route); restOfPath.Insert(0, shell.CurrentItem.CurrentItem.Route); restOfPath.Insert(0, shell.CurrentItem.Route); var result = String.Join(_pathSeparator, restOfPath); var returnValue = ConvertToStandardFormat("scheme", "host", null, new Uri(result, UriKind.Relative)); return(new Uri(FormatUri(returnValue.PathAndQuery), UriKind.Relative)); } if (path.IsAbsoluteUri) { return(new Uri(FormatUri(path.OriginalString), UriKind.Absolute)); } return(new Uri(FormatUri(path.OriginalString), UriKind.Relative)); }
public async Task GoToAsync(ShellNavigationParameters shellNavigationParameters) { if (shellNavigationParameters.PagePushing != null) { Routing.RegisterImplicitPageRoute(shellNavigationParameters.PagePushing); } var state = shellNavigationParameters.TargetState ?? new ShellNavigationState(Routing.GetRoute(shellNavigationParameters.PagePushing), false); bool?animate = shellNavigationParameters.Animated; bool enableRelativeShellRoutes = shellNavigationParameters.EnableRelativeShellRoutes; ShellNavigatingEventArgs deferredArgs = shellNavigationParameters.DeferredArgs; var navigationRequest = ShellUriHandler.GetNavigationRequest(_shell, state.FullLocation, enableRelativeShellRoutes, shellNavigationParameters: shellNavigationParameters); bool isRelativePopping = ShellUriHandler.IsTargetRelativePop(shellNavigationParameters); ShellNavigationSource source = CalculateNavigationSource(_shell, _shell.CurrentState, navigationRequest); // If the deferredArgs are non null that means we are processing a delayed navigation // so the user has indicated they want to go forward with navigation // This scenario only comes up from UI iniated navigation (i.e. switching tabs) if (deferredArgs == null) { var navigatingArgs = ProposeNavigation(source, state, _shell.CurrentState != null, animate ?? true); if (navigatingArgs != null) { bool accept = !navigatingArgs.NavigationDelayedOrCancelled; if (navigatingArgs.DeferredTask != null) { accept = await navigatingArgs.DeferredTask; } if (!accept) { return; } } } Routing.RegisterImplicitPageRoutes(_shell); _accumulateNavigatedEvents = true; var uri = navigationRequest.Request.FullUri; var queryString = navigationRequest.Query; var queryData = ParseQueryString(queryString); ApplyQueryAttributes(_shell, queryData, false, false); var shellItem = navigationRequest.Request.Item; var shellSection = navigationRequest.Request.Section; var currentShellSection = _shell.CurrentItem?.CurrentItem; var nextActiveSection = shellSection ?? shellItem?.CurrentItem; ShellContent shellContent = navigationRequest.Request.Content; bool modalStackPreBuilt = false; // If we're replacing the whole stack and there are global routes then build the navigation stack before setting the shell section visible if (navigationRequest.Request.GlobalRoutes.Count > 0 && nextActiveSection != null && navigationRequest.StackRequest == NavigationRequest.WhatToDoWithTheStack.ReplaceIt) { modalStackPreBuilt = true; bool?isAnimated = (nextActiveSection != currentShellSection) ? false : animate; await nextActiveSection.GoToAsync(navigationRequest, queryData, isAnimated, isRelativePopping); } if (shellItem != null) { ApplyQueryAttributes(shellItem, queryData, navigationRequest.Request.Section == null, false); bool navigatedToNewShellElement = false; if (shellSection != null && shellContent != null) { ApplyQueryAttributes(shellContent, queryData, navigationRequest.Request.GlobalRoutes.Count == 0, isRelativePopping); if (shellSection.CurrentItem != shellContent) { shellSection.SetValueFromRenderer(ShellSection.CurrentItemProperty, shellContent); navigatedToNewShellElement = true; } } if (shellSection != null) { ApplyQueryAttributes(shellSection, queryData, navigationRequest.Request.Content == null, false); if (shellItem.CurrentItem != shellSection) { shellItem.SetValueFromRenderer(ShellItem.CurrentItemProperty, shellSection); navigatedToNewShellElement = true; } } if (_shell.CurrentItem != shellItem) { _shell.SetValueFromRenderer(Shell.CurrentItemProperty, shellItem); navigatedToNewShellElement = true; } if (!modalStackPreBuilt && currentShellSection?.Navigation.ModalStack.Count > 0) { // - navigating to new shell element so just pop everything // - or route contains no global route requests if (navigatedToNewShellElement || navigationRequest.Request.GlobalRoutes.Count == 0) { // remove all non visible pages first so the transition just smoothly goes from // currently visible modal to base page if (navigationRequest.Request.GlobalRoutes.Count == 0) { for (int i = currentShellSection.Stack.Count - 1; i >= 1; i--) { currentShellSection.Navigation.RemovePage(currentShellSection.Stack[i]); } } await currentShellSection.PopModalStackToPage(null, animate); } } if (navigationRequest.Request.GlobalRoutes.Count > 0 && navigationRequest.StackRequest != NavigationRequest.WhatToDoWithTheStack.ReplaceIt) { // TODO get rid of this hack and fix so if there's a stack the current page doesn't display await Device.InvokeOnMainThreadAsync(() => { return(_shell.CurrentItem.CurrentItem.GoToAsync(navigationRequest, queryData, animate, isRelativePopping)); }); } else if (navigationRequest.Request.GlobalRoutes.Count == 0 && navigationRequest.StackRequest == NavigationRequest.WhatToDoWithTheStack.ReplaceIt && currentShellSection?.Navigation?.NavigationStack?.Count > 1) { // TODO get rid of this hack and fix so if there's a stack the current page doesn't display await Device.InvokeOnMainThreadAsync(() => { return(_shell.CurrentItem.CurrentItem.GoToAsync(navigationRequest, queryData, animate, isRelativePopping)); }); } } else { await _shell.CurrentItem.CurrentItem.GoToAsync(navigationRequest, queryData, animate, isRelativePopping); } _accumulateNavigatedEvents = false; // this can be null in the event that no navigation actually took place! if (_accumulatedEvent != null) { HandleNavigated(_accumulatedEvent); } }
public static void ApplyQueryAttributes(Element element, IDictionary <string, string> query, bool isLastItem, bool isPopping) { string prefix = ""; if (!isLastItem) { var route = Routing.GetRoute(element); if (string.IsNullOrEmpty(route) || Routing.IsImplicit(route)) { return; } prefix = route + "."; } //if the lastItem is implicitly wrapped, get the actual ShellContent if (isLastItem) { if (element is IShellItemController shellitem && shellitem.GetItems().FirstOrDefault() is ShellSection section) { element = section; } if (element is IShellSectionController shellsection && shellsection.GetItems().FirstOrDefault() is ShellContent content) { element = content; } if (element is ShellContent shellcontent && shellcontent.Content is Element e) { element = e; } } if (!(element is BaseShellItem baseShellItem)) { baseShellItem = element?.Parent as BaseShellItem; } //filter the query to only apply the keys with matching prefix var filteredQuery = new Dictionary <string, string>(query.Count); foreach (var q in query) { if (!q.Key.StartsWith(prefix, StringComparison.Ordinal)) { continue; } var key = q.Key.Substring(prefix.Length); if (key.Contains(".")) { continue; } filteredQuery.Add(key, q.Value); } if (baseShellItem is ShellContent) { baseShellItem.ApplyQueryAttributes(MergeData(element, filteredQuery, isPopping)); } else if (isLastItem) { element.SetValue(ShellContent.QueryAttributesProperty, MergeData(element, query, isPopping)); } IDictionary <string, string> MergeData(Element shellElement, IDictionary <string, string> data, bool isPopping) { if (!isPopping) { return(data); } var returnValue = new Dictionary <string, string>(data); var existing = (IDictionary <string, string>)shellElement.GetValue(ShellContent.QueryAttributesProperty); if (existing == null) { return(data); } foreach (var datum in existing) { if (!returnValue.ContainsKey(datum.Key)) { returnValue[datum.Key] = datum.Value; } } return(returnValue); } }
public static ShellNavigationState GetNavigationState(ShellItem shellItem, ShellSection shellSection, ShellContent shellContent, IReadOnlyList <Page> sectionStack, IReadOnlyList <Page> modalStack) { List <string> routeStack = new List <string>(); bool stackAtRoot = sectionStack == null || sectionStack.Count <= 1; bool hasUserDefinedRoute = (Routing.IsUserDefined(shellItem)) || (Routing.IsUserDefined(shellSection)) || (Routing.IsUserDefined(shellContent)); if (shellItem != null) { var shellItemRoute = shellItem.Route; routeStack.Add(shellItemRoute); if (shellSection != null) { var shellSectionRoute = shellSection.Route; routeStack.Add(shellSectionRoute); if (shellContent != null) { var shellContentRoute = shellContent.Route; routeStack.Add(shellContentRoute); } if (!stackAtRoot) { for (int i = 1; i < sectionStack.Count; i++) { var page = sectionStack[i]; routeStack.AddRange(CollapsePath(Routing.GetRoute(page), routeStack, hasUserDefinedRoute)); } } if (modalStack != null && modalStack.Count > 0) { for (int i = 0; i < modalStack.Count; i++) { var topPage = modalStack[i]; routeStack.AddRange(CollapsePath(Routing.GetRoute(topPage), routeStack, hasUserDefinedRoute)); for (int j = 1; j < topPage.Navigation.NavigationStack.Count; j++) { routeStack.AddRange(CollapsePath(Routing.GetRoute(topPage.Navigation.NavigationStack[j]), routeStack, hasUserDefinedRoute)); } } } } } if (routeStack.Count > 0) { routeStack.Insert(0, "/"); } return(new ShellNavigationState(String.Join("/", routeStack), true)); List <string> CollapsePath( string myRoute, IEnumerable <string> currentRouteStack, bool userDefinedRoute) { var localRouteStack = currentRouteStack.ToList(); for (var i = localRouteStack.Count - 1; i >= 0; i--) { var route = localRouteStack[i]; if (Routing.IsImplicit(route) || (Routing.IsDefault(route) && userDefinedRoute)) { localRouteStack.RemoveAt(i); } } var paths = myRoute.Split('/').ToList(); // collapse similar leaves int walkBackCurrentStackIndex = localRouteStack.Count - (paths.Count - 1); while (paths.Count > 1 && walkBackCurrentStackIndex >= 0) { if (paths[0] == localRouteStack[walkBackCurrentStackIndex]) { paths.RemoveAt(0); } else { break; } walkBackCurrentStackIndex++; } return(paths); } }