| File: NavigationPage\NavigationPage.Legacy.cs | Web Access |
| Project: src\src\Controls\src\Core\Controls.Core.csproj (Microsoft.Maui.Controls) |
#nullable disable using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Maui.Controls.Internals; namespace Microsoft.Maui.Controls { // This contains the messaging required to communicate with legacy renderers /// <include file="../../../docs/Microsoft.Maui.Controls/NavigationPage.xml" path="Type[@FullName='Microsoft.Maui.Controls.NavigationPage']/Docs/*" /> public partial class NavigationPage : INavigationPageController { async Task<Page> PopAsyncInner( bool animated, bool fast) { if (NavigationPageController.StackDepth == 1) { return null; } var page = (Page)InternalChildren.Last(); var previousPage = CurrentPage; SendNavigating(); var removedPage = await RemoveAsyncInner(page, animated, fast); SendNavigated(previousPage, NavigationType.Pop); return removedPage; } async Task<Page> RemoveAsyncInner( Page page, bool animated, bool fast) { if (NavigationPageController.StackDepth == 1) { return null; } FireDisappearing(page); if (InternalChildren.Last() == page) FireAppearing((Page)InternalChildren[NavigationPageController.StackDepth - 2]); var args = new NavigationRequestedEventArgs(page, animated); var removed = true; EventHandler<NavigationRequestedEventArgs> requestPop = _popRequested; if (requestPop != null) { requestPop(this, args); if (args.Task != null && !fast) removed = await args.Task; } if (!removed && !fast) return CurrentPage; RemoveFromInnerChildren(page); CurrentPage = (Page)InternalChildren.Last(); if (Popped != null) Popped(this, args); return page; } Task<Page> INavigationPageController.PopAsyncInner(bool animated, bool fast) { return PopAsyncInner(animated, fast); } Task<Page> INavigationPageController.RemoveAsyncInner(Page page, bool animated, bool fast) { return RemoveAsyncInner(page, animated, fast); } EventHandler<NavigationRequestedEventArgs> _popRequested; event EventHandler<NavigationRequestedEventArgs> INavigationPageController.PopRequested { add => _popRequested += value; remove => _popRequested -= value; } EventHandler<NavigationRequestedEventArgs> _popToRootRequested; event EventHandler<NavigationRequestedEventArgs> INavigationPageController.PopToRootRequested { add => _popToRootRequested += value; remove => _popToRootRequested -= value; } EventHandler<NavigationRequestedEventArgs> _pushRequested; event EventHandler<NavigationRequestedEventArgs> INavigationPageController.PushRequested { add => _pushRequested += value; remove => _pushRequested -= value; } EventHandler<NavigationRequestedEventArgs> _removePageRequested; event EventHandler<NavigationRequestedEventArgs> INavigationPageController.RemovePageRequested { add => _removePageRequested += value; remove => _removePageRequested -= value; } EventHandler<NavigationRequestedEventArgs> _insertPageBeforeRequested; event EventHandler<NavigationRequestedEventArgs> INavigationPageController.InsertPageBeforeRequested { add => _insertPageBeforeRequested += value; remove => _insertPageBeforeRequested -= value; } void InsertPageBefore(Page page, Page before) { if (page == null) throw new ArgumentNullException($"{nameof(page)} cannot be null."); if (before == null) throw new ArgumentNullException($"{nameof(before)} cannot be null."); if (!InternalChildren.Contains(before)) throw new ArgumentException($"{nameof(before)} must be a child of the NavigationPage", nameof(before)); if (InternalChildren.Contains(page)) throw new ArgumentException("Cannot insert page which is already in the navigation stack"); _insertPageBeforeRequested?.Invoke(this, new NavigationRequestedEventArgs(page, before, false)); int index = InternalChildren.IndexOf(before); InternalChildren.Insert(index, page); if (index == 0) RootPage = page; // Shouldn't be required? if (Width > 0 && Height > 0) ForceLayout(); } async Task PopToRootAsyncInner(bool animated) { if (NavigationPageController.StackDepth == 1) return; var previousPage = CurrentPage; SendNavigating(); FireDisappearing(CurrentPage); FireAppearing((Page)InternalChildren[0]); Element[] childrenToRemove = InternalChildren.Skip(1).ToArray(); foreach (Element child in childrenToRemove) { RemoveFromInnerChildren(child); } CurrentPage = RootPage; var args = new NavigationRequestedEventArgs(RootPage, animated); EventHandler<NavigationRequestedEventArgs> requestPopToRoot = _popToRootRequested; if (requestPopToRoot != null) { requestPopToRoot(this, args); if (args.Task != null) await args.Task; } PoppedToRoot?.Invoke(this, new PoppedToRootEventArgs(RootPage, childrenToRemove.OfType<Page>().ToList())); SendNavigated(previousPage, NavigationType.PopToRoot); } async Task PushAsyncInner(Page page, bool animated) { if (InternalChildren.Contains(page)) return; var previousPage = CurrentPage; SendNavigating(); FireDisappearing(CurrentPage); FireAppearing(page); PushPage(page); var args = new NavigationRequestedEventArgs(page, animated); EventHandler<NavigationRequestedEventArgs> requestPush = _pushRequested; if (requestPush != null) { requestPush(this, args); if (args.Task != null) await args.Task; } SendNavigated(previousPage, NavigationType.Push); Pushed?.Invoke(this, args); } #if IOS // Because iOS currently doesn't use our `IStackNavigationView` structures // there are scenarios where the legacy handler needs to alert the xplat // code of when a navigation has occurred. // For example, initial load and when the user taps the back button internal void SendNavigatedFromHandler(Page previousPage, NavigationType navigationType) { if (CurrentPage.HasNavigatedTo) return; SendNavigated(previousPage, navigationType); } #endif void PushPage(Page page) { InternalChildren.Add(page); if (InternalChildren.Count == 1) RootPage = page; CurrentPage = page; } void RemovePage(Page page) { if (page == null) throw new ArgumentNullException($"{nameof(page)} cannot be null."); if (page == CurrentPage && CurrentPage == RootPage) throw new InvalidOperationException("Cannot remove root page when it is also the currently displayed page."); if (page == CurrentPage) { Application.Current?.FindMauiContext()?.CreateLogger<NavigationPage>()?.LogWarning("RemovePage called for CurrentPage object. This can result in undesired behavior, consider calling PopAsync instead."); PopAsync(); return; } if (!InternalChildren.Contains(page)) throw new ArgumentException("Page to remove must be contained on this Navigation Page"); _removePageRequested?.Invoke(this, new NavigationRequestedEventArgs(page, true)); RemoveFromInnerChildren(page); if (RootPage == page) RootPage = (Page)InternalChildren.First(); } class NavigationImpl : NavigationProxy { readonly Lazy<ReadOnlyCastingList<Page, Element>> _castingList; public NavigationImpl(NavigationPage owner) { Owner = owner; _castingList = new Lazy<ReadOnlyCastingList<Page, Element>>(() => new ReadOnlyCastingList<Page, Element>(Owner.InternalChildren)); } NavigationPage Owner { get; } protected override IReadOnlyList<Page> GetNavigationStack() { return _castingList.Value; } protected override void OnInsertPageBefore(Page page, Page before) { Owner.InsertPageBefore(page, before); } protected override Task<Page> OnPopAsync(bool animated) { return Owner.PopAsync(animated); } protected override Task OnPopToRootAsync(bool animated) { return Owner.PopToRootAsync(animated); } protected override Task OnPushAsync(Page root, bool animated) { return Owner.PushAsync(root, animated); } protected override void OnRemovePage(Page page) { Owner.RemovePage(page); } } } }