internal void NavigateToShellSection(ShellNavigationSource source, ShellSection section, bool animate = true) { _ = section ?? throw new ArgumentNullException(nameof(section)); if (ShellSection != null) { ShellSection.PropertyChanged -= OnShellSectionPropertyChanged; ((System.Collections.Specialized.INotifyCollectionChanged)section.Items).CollectionChanged -= OnShellSectionRendererCollectionChanged; ShellSection = null; MenuItemsSource = null; } ShellSection = section; ShellSection.PropertyChanged += OnShellSectionPropertyChanged; SelectedItem = null; IsPaneVisible = section.Items.Count > 1; MenuItemsSource = section.Items; ((System.Collections.Specialized.INotifyCollectionChanged)section.Items).CollectionChanged += OnShellSectionRendererCollectionChanged; SelectedItem = section.CurrentItem; NavigateToContent(source, section.CurrentItem, animate); }
// This is used for cases where the user is navigating via native UI navigation i.e. clicking on Tabs // If the user defers this type of navigation we generate the equivalent GotoAsync call // so when the deferral is completed the same navigation can complete public bool ProposeNavigationOutsideGotoAsync( ShellNavigationSource source, ShellItem shellItem, ShellSection shellSection, ShellContent shellContent, IReadOnlyList <Page> stack, bool canCancel, bool isAnimated) { if (AccumulateNavigatedEvents) { return(true); } var proposedState = GetNavigationState(shellItem, shellSection, shellContent, stack, shellSection.Navigation.ModalStack); var navArgs = ProposeNavigation(source, proposedState, canCancel, isAnimated); if (navArgs.DeferralCount > 0) { navArgs.RegisterDeferralCompletedCallBack(async() => { if (navArgs.Cancelled) { return; } Func <Task> navigationTask = () => GoToAsync(navArgs.Target, navArgs.Animate, false, navArgs); await _shell .FindDispatcher() .DispatchIfRequiredAsync(navigationTask); }); } return(!navArgs.NavigationDelayedOrCancelled); }
/// <include file="../../../docs/Microsoft.Maui.Controls/ShellNavigatingEventArgs.xml" path="//Member[@MemberName='.ctor']/Docs" /> public ShellNavigatingEventArgs(ShellNavigationState current, ShellNavigationState target, ShellNavigationSource source, bool canCancel) { #if !NETSTANDARD1_0 _deferralFinishedTask = () => Task.CompletedTask; #else _deferralFinishedTask = () => Task.Delay(0); #endif Current = current; Target = target; Source = source; CanCancel = canCancel; Animate = true; }
protected override void SetupAnimation(ShellNavigationSource navSource, AndroidX.Fragment.App.FragmentTransaction t, Page page) { // Don't setup any animations }
public void TestNavigationArgs(ShellNavigationSource source, string from, string to) { TestNavigatingArgs(source, from, to); TestNavigatedArgs(source, from, to); }
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); } }
public ShellNavigatedEventArgs(ShellNavigationState previous, ShellNavigationState current, ShellNavigationSource source) { Previous = previous; Current = current; Source = source; }
internal void NavigateToContent(ShellNavigationSource source, ShellContent shellContent, Page page, bool animate = true) { Page nextPage = null; if (source == ShellNavigationSource.PopToRoot) { nextPage = (shellContent as IShellContentController).GetOrCreateContent(); } else { nextPage = (ShellSection as IShellSectionController) .PresentedPage ?? ((IShellContentController)shellContent)?.GetOrCreateContent(); } if (CurrentContent != null && Page != null) { Page.PropertyChanged -= OnPagePropertyChanged; ((IShellContentController)CurrentContent).RecyclePage(Page); } CurrentContent = shellContent; if (nextPage != null) { Page = nextPage; Page.PropertyChanged += OnPagePropertyChanged; switch (source) { case ShellNavigationSource.Insert: { var pageIndex = ShellSection.Stack.ToList().IndexOf(page); if (pageIndex == Frame.BackStack.Count - 1) { Frame.Navigate(typeof(ShellPageWrapper), GetTransitionInfo(source)); } else { Frame.BackStack.Insert(pageIndex, new PageStackEntry(typeof(ShellPageWrapper), null, GetTransitionInfo(source))); } break; } case ShellNavigationSource.Pop: Frame.GoBack(GetTransitionInfo(source)); break; case ShellNavigationSource.Unknown: break; case ShellNavigationSource.Push: Frame.Navigate(typeof(ShellPageWrapper), GetTransitionInfo(source)); break; case ShellNavigationSource.PopToRoot: while (Frame.BackStackDepth > 2) { Frame.BackStack.RemoveAt(1); } Frame.GoBack(GetTransitionInfo(source)); break; case ShellNavigationSource.Remove: { var pageIndex = FormsNavigationStack.IndexOf(page); if (pageIndex == Frame.BackStack.Count - 1) { Frame.GoBack(GetTransitionInfo(source)); } else { Frame.BackStack.RemoveAt(pageIndex); } break; } case ShellNavigationSource.ShellItemChanged: break; case ShellNavigationSource.ShellSectionChanged: Frame.Navigate(typeof(ShellPageWrapper), GetTransitionInfo(source)); break; case ShellNavigationSource.ShellContentChanged: break; } UpdateSearchHandler(Shell.GetSearchHandler(Page)); var wrapper = (ShellPageWrapper)(Frame.Content); if (wrapper.Page == null) { wrapper.Page = Page; } wrapper.LoadPage(); FormsNavigationStack = ShellSection.Stack.ToList(); } }
public ShellNavigatingEventArgs(ShellNavigationState current, ShellNavigationState target, ShellNavigationSource source, bool canCancel) { Current = current; Target = target; Source = source; CanCancel = canCancel; }
/// <include file="../../../docs/Microsoft.Maui.Controls/ShellNavigatingEventArgs.xml" path="//Member[@MemberName='.ctor']/Docs" /> public ShellNavigatingEventArgs(ShellNavigationState current, ShellNavigationState target, ShellNavigationSource source, bool canCancel) { _deferralFinishedTask = () => Task.CompletedTask; Current = current; Target = target; Source = source; CanCancel = canCancel; Animate = true; }
internal void NavigateToContent(ShellNavigationSource source, ShellContent shellContent, Page page, bool animate = true) { Page nextPage = (ShellSection as IShellSectionController) .PresentedPage ?? ((IShellContentController)shellContent)?.GetOrCreateContent(); if (CurrentContent != null && Page != null) { Page.PropertyChanged -= OnPagePropertyChanged; ((IShellContentController)CurrentContent).RecyclePage(Page); } CurrentContent = shellContent; if (nextPage != null) { Page = nextPage; Page.PropertyChanged += OnPagePropertyChanged; switch (source) { case ShellNavigationSource.Insert: break; case ShellNavigationSource.Pop: Frame.GoBack(GetTransitionInfo(source)); break; case ShellNavigationSource.Unknown: break; case ShellNavigationSource.Push: Frame.Navigate(typeof(ShellPageWrapper), GetTransitionInfo(source)); break; case ShellNavigationSource.PopToRoot: while (Frame.BackStackDepth > 1) { Frame.GoBack(GetTransitionInfo(source)); } break; case ShellNavigationSource.Remove: break; case ShellNavigationSource.ShellItemChanged: break; case ShellNavigationSource.ShellSectionChanged: Frame.Navigate(typeof(ShellPageWrapper), GetTransitionInfo(source)); break; case ShellNavigationSource.ShellContentChanged: break; } UpdateSearchHandler(Shell.GetSearchHandler(Page)); var wrapper = (ShellPageWrapper)(Frame.Content); if (wrapper.Page == null) { wrapper.Page = Page; } wrapper.LoadPage(); } }