| File: NavigationModel.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; namespace Microsoft.Maui.Controls.Internals { internal class NavigationModel { readonly List<Page> _modalStack = new List<Page>(); readonly List<List<Page>> _navTree = new List<List<Page>>(); /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationModel.xml" path="//Member[@MemberName='CurrentPage']/Docs/*" /> public Page CurrentPage { get { if (_navTree.Count > 0) return _navTree.Last().Last(); return null; } } public Page LastRoot { get { if (_navTree.Count == 0) return null; return _navTree.Last()[0]; } } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationModel.xml" path="//Member[@MemberName='Modals']/Docs/*" /> public IReadOnlyList<Page> Modals { get { return _modalStack.AsReadOnly(); } } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationModel.xml" path="//Member[@MemberName='Roots']/Docs/*" /> public IEnumerable<Page> Roots { get { foreach (List<Page> list in _navTree) { yield return list[0]; } } } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationModel.xml" path="//Member[@MemberName='Tree']/Docs/*" /> public IReadOnlyList<IReadOnlyList<Page>> Tree { get { return _navTree; } } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationModel.xml" path="//Member[@MemberName='Clear']/Docs/*" /> public void Clear() { _navTree.Clear(); _modalStack.Clear(); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationModel.xml" path="//Member[@MemberName='InsertPageBefore']/Docs/*" /> public void InsertPageBefore(Page page, Page before) { List<Page> currentStack = _navTree.Last(); int index = currentStack.IndexOf(before); if (index == -1) throw new ArgumentException("before must be in the current navigation context"); currentStack.Insert(index, page); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationModel.xml" path="//Member[@MemberName='Pop']/Docs/*" /> public Page Pop(Page ancestralNav) { ancestralNav = ancestralNav.AncestorToRoot(); foreach (List<Page> stack in _navTree) { if (stack.Contains(ancestralNav)) { if (stack.Count <= 1) throw new InvalidNavigationException("Cannot pop final item in stack"); Page result = stack.Last(); stack.Remove(result); return result; } } throw new InvalidNavigationException("Popped from unpushed item?"); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationModel.xml" path="//Member[@MemberName='PopModal']/Docs/*" /> public Page PopModal() { if (_navTree.Count <= 1) throw new InvalidNavigationException("Can't pop modal without any modals pushed"); var previousPage = CurrentPage; Page modal = _navTree.Last()[0]; _modalStack.Remove(modal); _navTree.Remove(_navTree.Last()); // Shell handles its own page life cycle events // because you can pop multiple pages in a single // request if (_navTree.Count > 0 && _navTree[0].Count > 0 && _navTree[0][0] is not Shell) { previousPage.SendNavigatingFrom(new NavigatingFromEventArgs()); previousPage.SendDisappearing(); CurrentPage.SendAppearing(); } return modal; } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationModel.xml" path="//Member[@MemberName='PopTopPage']/Docs/*" /> public Page PopTopPage() { Page itemToRemove; if (_navTree.Count == 1) { if (_navTree[0].Count > 1) { itemToRemove = _navTree[0].Last(); _navTree[0].Remove(itemToRemove); return itemToRemove; } return null; } itemToRemove = _navTree.Last().Last(); _navTree.Last().Remove(itemToRemove); if (_navTree.Last().Count == 0) { _navTree.RemoveAt(_navTree.Count - 1); } return itemToRemove; } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationModel.xml" path="//Member[@MemberName='PopToRoot']/Docs/*" /> public void PopToRoot(Page ancestralNav) { ancestralNav = ancestralNav.AncestorToRoot(); foreach (List<Page> stack in _navTree) { if (stack.Contains(ancestralNav)) { if (stack.Count <= 1) throw new InvalidNavigationException("Cannot pop final item in stack"); stack.RemoveRange(1, stack.Count - 1); return; } } throw new InvalidNavigationException("Popped from unpushed item?"); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationModel.xml" path="//Member[@MemberName='Push']/Docs/*" /> public void Push(Page page, Page ancestralNav) { if (ancestralNav == null) { if (_navTree.Count > 0) throw new InvalidNavigationException("Ancestor must be provided for all pushes except first"); _navTree.Add(new List<Page> { page }); return; } ancestralNav = ancestralNav.AncestorToRoot(); foreach (List<Page> stack in _navTree) { if (stack.Contains(ancestralNav)) { stack.Add(page); return; } } throw new InvalidNavigationException("Invalid ancestor passed"); } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationModel.xml" path="//Member[@MemberName='PushModal']/Docs/*" /> public void PushModal(Page page) { var previousPage = CurrentPage; _navTree.Add(new List<Page> { page }); _modalStack.Add(page); // Shell handles its own page life cycle events // because you can push multiple pages in a single // request if (_navTree.Count > 0 && _navTree[0].Count > 0 && _navTree[0][0] is not Shell) { previousPage.SendNavigatingFrom(new NavigatingFromEventArgs()); previousPage.SendDisappearing(); page.SendAppearing(); } } /// <include file="../../docs/Microsoft.Maui.Controls.Internals/NavigationModel.xml" path="//Member[@MemberName='RemovePage']/Docs/*" /> public bool RemovePage(Page page) { bool found; List<Page> currentStack = _navTree.Last(); var i = 0; while (!(found = currentStack.Remove(page)) && i < _navTree.Count - 1) { currentStack = _navTree[i++]; } return found; } } }