internal static Uri RemoveImplicit(Uri uri) { uri = ShellUriHandler.FormatUri(uri); string[] parts = uri.OriginalString.TrimEnd(_pathSeparator[0]).Split(_pathSeparator[0]); List <string> toKeep = new List <string>(); for (int i = 0; i < parts.Length; i++) { if (!IsImplicit(parts[i])) { toKeep.Add(parts[i]); } } return(new Uri(string.Join(_pathSeparator, toKeep), UriKind.Relative)); }
public string GetNextSegmentMatch(string matchMe) { var segmentsToMatch = ShellUriHandler.RetrievePaths(matchMe).ToList(); // if matchMe is an absolute route then we only match // if there are no routes already present if (matchMe.StartsWith("/", StringComparison.Ordinal) || matchMe.StartsWith("\\", StringComparison.Ordinal)) { for (var i = 0; i < _matchedSegments.Count; i++) { var seg = _matchedSegments[i]; if (segmentsToMatch.Count <= i || segmentsToMatch[i] != seg) { return(String.Empty); } segmentsToMatch.Remove(seg); } } List <string> matches = new List <string>(); List <string> currentSet = new List <string>(_matchedSegments); foreach (var split in segmentsToMatch) { string next = GetNextSegment(currentSet); if (next == split) { currentSet.Add(split); matches.Add(split); } else { return(String.Empty); } } return(String.Join(_uriSeparator, matches)); }
internal static Uri Remove(Uri uri, bool implicitRoutes, bool defaultRoutes) { uri = ShellUriHandler.FormatUri(uri, null); string[] parts = uri.OriginalString.TrimEnd(_pathSeparator[0]).Split(_pathSeparator[0]); bool userDefinedRouteAdded = false; List <string> toKeep = new List <string>(); for (int i = 0; i < parts.Length; i++) { // This means there are no routes defined on the shell but the user has navigated to a global route // so we need to attach the final route where the user left the shell if (s_routes.ContainsKey(parts[i]) && !userDefinedRouteAdded && i > 0) { toKeep.Add(parts[i - 1]); } if (!(IsDefault(parts[i]) && defaultRoutes) && !(IsImplicit(parts[i]) && implicitRoutes)) { if (!String.IsNullOrWhiteSpace(parts[i])) { userDefinedRouteAdded = true; } toKeep.Add(parts[i]); } } if (!userDefinedRouteAdded && parts.Length > 0) { toKeep.Add(parts[parts.Length - 1]); } return(new Uri(string.Join(_pathSeparator, toKeep), UriKind.Relative)); }
public static ShellNavigationSource CalculateNavigationSource(Shell shell, ShellNavigationState current, NavigationRequest request) { if (request.StackRequest == NavigationRequest.WhatToDoWithTheStack.PushToIt) { return(ShellNavigationSource.Push); } if (current == null) { return(ShellNavigationSource.ShellItemChanged); } var targetUri = ShellUriHandler.ConvertToStandardFormat(shell, request.Request.FullUri); var currentUri = ShellUriHandler.ConvertToStandardFormat(shell, current.FullLocation); var targetPaths = ShellUriHandler.RetrievePaths(targetUri.PathAndQuery); var currentPaths = ShellUriHandler.RetrievePaths(currentUri.PathAndQuery); var targetPathsLength = targetPaths.Length; var currentPathsLength = currentPaths.Length; if (targetPathsLength < 4 || currentPathsLength < 4) { return(ShellNavigationSource.Unknown); } if (targetPaths[1] != currentPaths[1]) { return(ShellNavigationSource.ShellItemChanged); } if (targetPaths[2] != currentPaths[2]) { return(ShellNavigationSource.ShellSectionChanged); } if (targetPaths[3] != currentPaths[3]) { return(ShellNavigationSource.ShellContentChanged); } if (targetPathsLength == currentPathsLength) { return(ShellNavigationSource.Unknown); } if (targetPathsLength < currentPathsLength) { for (var i = 0; i < targetPathsLength; i++) { var targetPath = targetPaths[i]; if (targetPath != currentPaths[i]) { break; } if (i == targetPathsLength - 1) { if (targetPathsLength == 4) { return(ShellNavigationSource.PopToRoot); } return(ShellNavigationSource.Pop); } } if (targetPaths[targetPathsLength - 1] == currentPaths[currentPathsLength - 1]) { return(ShellNavigationSource.Remove); } if (targetPathsLength == 4) { return(ShellNavigationSource.PopToRoot); } return(ShellNavigationSource.Pop); } else if (targetPathsLength > currentPathsLength) { for (var i = 0; i < currentPathsLength; i++) { if (targetPaths[i] != currentPaths[i]) { break; } if (i == targetPathsLength - 1) { return(ShellNavigationSource.Push); } } } if (targetPaths[targetPathsLength - 1] == currentPaths[currentPathsLength - 1]) { return(ShellNavigationSource.Insert); } return(ShellNavigationSource.Push); }
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); } }