| File: NavigationProxy.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.Threading.Tasks; namespace Microsoft.Maui.Controls.Internals { [EditorBrowsable(EditorBrowsableState.Never)] public interface INavigationProxy { NavigationProxy NavigationProxy { get; } } /// <summary>Represents an object capable of handling stack-based navigation via proxying.</summary> /// <remarks> /// <para>Elements may use navigation proxies to delegate navigation capabilities to their parents if they themselves can't handle it.</para> /// <para>For internal use for .NET MAUI.</para> /// </remarks> [EditorBrowsable(EditorBrowsableState.Never)] public class NavigationProxy : INavigation { INavigation _inner; Lazy<NavigatingStepRequestList> _modalStack = new Lazy<NavigatingStepRequestList>(() => new NavigatingStepRequestList()); Lazy<List<Page>> _pushStack = new Lazy<List<Page>>(() => new List<Page>()); /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationProxy.xml" path="//Member[@MemberName='Inner']/Docs/*" /> public INavigation Inner { get { return _inner; } set { if (_inner == value) return; _inner = value; // reverse so that things go into the new stack in the same order // null out to release memory that will likely never be needed again if (_inner is null) { _pushStack = new Lazy<List<Page>>(() => new List<Page>()); _modalStack = new Lazy<NavigatingStepRequestList>(() => new NavigatingStepRequestList()); } else { if (_pushStack is not null && _pushStack.IsValueCreated) { foreach (Page page in _pushStack.Value) { _inner.PushAsync(page); } } if (_modalStack is not null && _modalStack.IsValueCreated) { foreach (var request in _modalStack.Value) { _inner.PushModalAsync(request.Page, request.IsAnimated); } } _pushStack = null; _modalStack = null; } } } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationProxy.xml" path="//Member[@MemberName='InsertPageBefore']/Docs/*" /> public void InsertPageBefore(Page page, Page before) { OnInsertPageBefore(page, before); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationProxy.xml" path="//Member[@MemberName='ModalStack']/Docs/*" /> public IReadOnlyList<Page> ModalStack { get { return GetModalStack(); } } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationProxy.xml" path="//Member[@MemberName='NavigationStack']/Docs/*" /> public IReadOnlyList<Page> NavigationStack { get { return GetNavigationStack(); } } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationProxy.xml" path="//Member[@MemberName='PopAsync'][1]/Docs/*" /> public Task<Page> PopAsync() { return OnPopAsync(true); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationProxy.xml" path="//Member[@MemberName='PopAsync'][2]/Docs/*" /> public Task<Page> PopAsync(bool animated) { return OnPopAsync(animated); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationProxy.xml" path="//Member[@MemberName='PopModalAsync'][1]/Docs/*" /> public Task<Page> PopModalAsync() { return OnPopModal(true); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationProxy.xml" path="//Member[@MemberName='PopModalAsync'][2]/Docs/*" /> public Task<Page> PopModalAsync(bool animated) { return OnPopModal(animated); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationProxy.xml" path="//Member[@MemberName='PopToRootAsync'][1]/Docs/*" /> public Task PopToRootAsync() { return OnPopToRootAsync(true); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationProxy.xml" path="//Member[@MemberName='PopToRootAsync'][2]/Docs/*" /> public Task PopToRootAsync(bool animated) { return OnPopToRootAsync(animated); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationProxy.xml" path="//Member[@MemberName='PushAsync'][1]/Docs/*" /> public Task PushAsync(Page root) { return PushAsync(root, true); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationProxy.xml" path="//Member[@MemberName='PushAsync'][2]/Docs/*" /> public Task PushAsync(Page root, bool animated) { if (root.RealParent is not null) throw new InvalidOperationException("Page must not already have a parent."); return OnPushAsync(root, animated); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationProxy.xml" path="//Member[@MemberName='PushModalAsync'][1]/Docs/*" /> public Task PushModalAsync(Page modal) { return PushModalAsync(modal, true); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationProxy.xml" path="//Member[@MemberName='PushModalAsync'][2]/Docs/*" /> public Task PushModalAsync(Page modal, bool animated) { if (modal.RealParent is not null && modal.RealParent is not IWindow) throw new InvalidOperationException("Page must not already have a parent."); return OnPushModal(modal, animated); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationProxy.xml" path="//Member[@MemberName='RemovePage']/Docs/*" /> public void RemovePage(Page page) { OnRemovePage(page); } protected virtual IReadOnlyList<Page> GetModalStack() { INavigation currentInner = Inner; return currentInner is null ? _modalStack.Value.Pages : currentInner.ModalStack; } protected virtual IReadOnlyList<Page> GetNavigationStack() { INavigation currentInner = Inner; return currentInner is null ? _pushStack.Value : currentInner.NavigationStack; } protected virtual void OnInsertPageBefore(Page page, Page before) { INavigation currentInner = Inner; if (currentInner is null) { int index = _pushStack.Value.IndexOf(before); if (index == -1) throw new ArgumentException("before must be in the pushed stack of the current context"); _pushStack.Value.Insert(index, page); } else { currentInner.InsertPageBefore(page, before); } } protected virtual Task<Page> OnPopAsync(bool animated) { INavigation inner = Inner; return inner is null ? Task.FromResult(Pop()) : inner.PopAsync(animated); } protected virtual Task<Page> OnPopModal(bool animated) { INavigation innerNav = Inner; return innerNav is null ? Task.FromResult(PopModal()) : innerNav.PopModalAsync(animated); } protected virtual Task OnPopToRootAsync(bool animated) { INavigation currentInner = Inner; if (currentInner is null) { if (_pushStack.Value.Count == 0) return Task.FromResult<Page>(null); Page root = _pushStack.Value.Last(); _pushStack.Value.Clear(); _pushStack.Value.Add(root); return Task.FromResult(root); } return currentInner.PopToRootAsync(animated); } protected virtual Task OnPushAsync(Page page, bool animated) { INavigation currentInner = Inner; if (currentInner is null) { _pushStack.Value.Add(page); return Task.FromResult(page); } return currentInner.PushAsync(page, animated); } protected virtual Task OnPushModal(Page modal, bool animated) { INavigation currentInner = Inner; if (currentInner is null) { _modalStack.Value.Add(new NavigationStepRequest(modal, true, animated)); return Task.FromResult<object>(null); } return currentInner.PushModalAsync(modal, animated); } protected virtual void OnRemovePage(Page page) { INavigation currentInner = Inner; if (currentInner is null) { _pushStack.Value.Remove(page); } else { currentInner.RemovePage(page); } } Page Pop() { List<Page> list = _pushStack.Value; if (list.Count == 0) return null; Page result = list[list.Count - 1]; list.RemoveAt(list.Count - 1); return result; } Page PopModal() { var list = _modalStack.Value; if (list.Count == 0) return null; var result = list[list.Count - 1]; list.RemoveAt(list.Count - 1); return result.Page; } } }