示例#1
0
文件: Routing.cs 项目: sung-su/maui
        internal static HashSet <string> GetRouteKeys()
        {
            var keys = s_routeKeys;

            if (keys != null)
            {
                return(keys);
            }

            keys = new HashSet <string>(StringComparer.Ordinal);
            foreach (var key in s_routes.Keys)
            {
                keys.Add(ShellUriHandler.FormatUri(key));
            }
            foreach (var key in s_implicitPageRoutes.Keys)
            {
                keys.Add(ShellUriHandler.FormatUri(key));
            }
            return(s_routeKeys = keys);
        }
示例#2
0
        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));
        }
示例#3
0
        public static ShellNavigationSource CalculateNavigationSource(Shell shell, ShellNavigationState current, ShellNavigationRequest request)
        {
            if (request.StackRequest == ShellNavigationRequest.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);
        }
示例#4
0
        internal async Task GoToAsync(
            ShellNavigationParameters shellNavigationParameters,
            ShellNavigationRequest navigationRequest)
        {
            // check for any pending navigations that need to complete
            if (_shell?.CurrentItem?.CurrentItem?.PendingNavigationTask != null)
            {
                await(_shell?.CurrentItem?.CurrentItem?.PendingNavigationTask ?? Task.CompletedTask);
            }

            if (shellNavigationParameters.PagePushing != null && navigationRequest == 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;

            navigationRequest ??= ShellUriHandler.GetNavigationRequest(_shell, state.FullLocation, enableRelativeShellRoutes, shellNavigationParameters: shellNavigationParameters);

            bool isRelativePopping = ShellUriHandler.IsTargetRelativePop(shellNavigationParameters);
            var  parameters        = shellNavigationParameters.Parameters ?? new ShellRouteParameters();

            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);

            parameters.Merge(queryData);
            ApplyQueryAttributes(_shell, parameters, 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;

            // check for any pending navigations that need to complete
            if (currentShellSection?.PendingNavigationTask != null)
            {
                await(currentShellSection?.PendingNavigationTask ?? Task.CompletedTask);
            }

            // 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 == ShellNavigationRequest.WhatToDoWithTheStack.ReplaceIt)
            {
                modalStackPreBuilt = true;

                bool?isAnimated = (nextActiveSection != currentShellSection) ? false : animate;
                await nextActiveSection.GoToAsync(navigationRequest, parameters, _shell.FindMauiContext()?.Services, isAnimated, isRelativePopping);
            }

            if (shellItem != null)
            {
                ApplyQueryAttributes(shellItem, parameters, navigationRequest.Request.Section == null, false);
                bool navigatedToNewShellElement = false;

                if (shellSection != null && shellContent != null)
                {
                    ApplyQueryAttributes(shellContent, parameters, navigationRequest.Request.GlobalRoutes.Count == 0, isRelativePopping);
                    if (shellSection.CurrentItem != shellContent)
                    {
                        shellSection.SetValueFromRenderer(ShellSection.CurrentItemProperty, shellContent);
                        navigatedToNewShellElement = true;
                    }
                }

                if (shellSection != null)
                {
                    ApplyQueryAttributes(shellSection, parameters, 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;
                }

                // Setting the current item isn't an async operation but it triggers an async
                // navigation path. So this waits until that's finished before returning from GotoAsync
                if (_shell?.CurrentItem?.CurrentItem?.PendingNavigationTask != null)
                {
                    await(_shell?.CurrentItem?.CurrentItem?.PendingNavigationTask ?? Task.CompletedTask);
                }

                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 != ShellNavigationRequest.WhatToDoWithTheStack.ReplaceIt)
                {
                    // TODO get rid of this hack and fix so if there's a stack the current page doesn't display
                    await _shell.Dispatcher.DispatchAsync(() =>
                    {
                        return(_shell.CurrentItem.CurrentItem.GoToAsync(navigationRequest, parameters, _shell.FindMauiContext()?.Services, animate, isRelativePopping));
                    });
                }
                else if (navigationRequest.Request.GlobalRoutes.Count == 0 &&
                         navigationRequest.StackRequest == ShellNavigationRequest.WhatToDoWithTheStack.ReplaceIt &&
                         nextActiveSection?.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 _shell.Dispatcher.DispatchAsync(() =>
                    {
                        return(_shell.CurrentItem.CurrentItem.GoToAsync(navigationRequest, parameters, _shell.FindMauiContext()?.Services, animate, isRelativePopping));
                    });
                }
            }
            else
            {
                await _shell.CurrentItem.CurrentItem.GoToAsync(navigationRequest, parameters, _shell.FindMauiContext()?.Services, animate, isRelativePopping);
            }

            // Setting the current item isn't an async operation but it triggers an async
            // navigation path. So this waits until that's finished before returning from GotoAsync
            if (_shell?.CurrentItem?.CurrentItem?.PendingNavigationTask != null)
            {
                await(_shell?.CurrentItem?.CurrentItem?.PendingNavigationTask ?? Task.CompletedTask);
            }

            (_shell as IShellController).UpdateCurrentState(source);
            _accumulateNavigatedEvents = false;

            // this can be null in the event that no navigation actually took place!
            if (_accumulatedEvent != null)
            {
                HandleNavigated(_accumulatedEvent);
            }
        }