summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Core/NavigationPage.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Core/NavigationPage.cs')
-rw-r--r--Xamarin.Forms.Core/NavigationPage.cs411
1 files changed, 411 insertions, 0 deletions
diff --git a/Xamarin.Forms.Core/NavigationPage.cs b/Xamarin.Forms.Core/NavigationPage.cs
new file mode 100644
index 00000000..61545b11
--- /dev/null
+++ b/Xamarin.Forms.Core/NavigationPage.cs
@@ -0,0 +1,411 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_NavigationPageRenderer))]
+ public class NavigationPage : Page, IPageContainer<Page>
+ {
+ public static readonly BindableProperty BackButtonTitleProperty = BindableProperty.CreateAttached("BackButtonTitle", typeof(string), typeof(Page), null);
+
+ public static readonly BindableProperty HasNavigationBarProperty = BindableProperty.CreateAttached("HasNavigationBar", typeof(bool), typeof(Page), true);
+
+ public static readonly BindableProperty HasBackButtonProperty = BindableProperty.CreateAttached("HasBackButton", typeof(bool), typeof(NavigationPage), true);
+
+ [Obsolete("Use BarBackgroundColorProperty and BarTextColorProperty to change NavigationPage bar color properties")] public static readonly BindableProperty TintProperty =
+ BindableProperty.Create("Tint", typeof(Color), typeof(NavigationPage), Color.Default);
+
+ public static readonly BindableProperty BarBackgroundColorProperty = BindableProperty.Create("BarBackgroundColor", typeof(Color), typeof(NavigationPage), Color.Default);
+
+ public static readonly BindableProperty BarTextColorProperty = BindableProperty.Create("BarTextColor", typeof(Color), typeof(NavigationPage), Color.Default);
+
+ public static readonly BindableProperty TitleIconProperty = BindableProperty.CreateAttached("TitleIcon", typeof(FileImageSource), typeof(NavigationPage), default(FileImageSource));
+
+ static readonly BindablePropertyKey CurrentPagePropertyKey = BindableProperty.CreateReadOnly("CurrentPage", typeof(Page), typeof(NavigationPage), null);
+ public static readonly BindableProperty CurrentPageProperty = CurrentPagePropertyKey.BindableProperty;
+
+ public NavigationPage()
+ {
+ Navigation = new NavigationImpl(this);
+ }
+
+ public NavigationPage(Page root) : this()
+ {
+ PushPage(root);
+ }
+
+ public Color BarBackgroundColor
+ {
+ get { return (Color)GetValue(BarBackgroundColorProperty); }
+ set { SetValue(BarBackgroundColorProperty, value); }
+ }
+
+ public Color BarTextColor
+ {
+ get { return (Color)GetValue(BarTextColorProperty); }
+ set { SetValue(BarTextColorProperty, value); }
+ }
+
+ [Obsolete("Use BarBackgroundColor and BarTextColor to change NavigationPage bar color properties")]
+ public Color Tint
+ {
+ get { return (Color)GetValue(TintProperty); }
+ set { SetValue(TintProperty, value); }
+ }
+
+ internal Task CurrentNavigationTask { get; set; }
+
+ internal Stack<Page> StackCopy
+ {
+ get
+ {
+ var result = new Stack<Page>(InternalChildren.Count);
+ foreach (Page page in InternalChildren)
+ result.Push(page);
+ return result;
+ }
+ }
+
+ internal int StackDepth
+ {
+ get { return InternalChildren.Count; }
+ }
+
+ public Page CurrentPage
+ {
+ get { return (Page)GetValue(CurrentPageProperty); }
+ private set { SetValue(CurrentPagePropertyKey, value); }
+ }
+
+ public static string GetBackButtonTitle(BindableObject page)
+ {
+ return (string)page.GetValue(BackButtonTitleProperty);
+ }
+
+ public static bool GetHasBackButton(Page page)
+ {
+ if (page == null)
+ throw new ArgumentNullException("page");
+ return (bool)page.GetValue(HasBackButtonProperty);
+ }
+
+ public static bool GetHasNavigationBar(BindableObject page)
+ {
+ return (bool)page.GetValue(HasNavigationBarProperty);
+ }
+
+ public static FileImageSource GetTitleIcon(BindableObject bindable)
+ {
+ return (FileImageSource)bindable.GetValue(TitleIconProperty);
+ }
+
+ public Task<Page> PopAsync()
+ {
+ return PopAsync(true);
+ }
+
+ public async Task<Page> PopAsync(bool animated)
+ {
+ if (CurrentNavigationTask != null && !CurrentNavigationTask.IsCompleted)
+ {
+ var tcs = new TaskCompletionSource<bool>();
+ Task oldTask = CurrentNavigationTask;
+ CurrentNavigationTask = tcs.Task;
+ await oldTask;
+
+ Page page = await PopAsyncInner(animated);
+ tcs.SetResult(true);
+ return page;
+ }
+
+ Task<Page> result = PopAsyncInner(animated);
+ CurrentNavigationTask = result;
+ return await result;
+ }
+
+ public event EventHandler<NavigationEventArgs> Popped;
+
+ public event EventHandler<NavigationEventArgs> PoppedToRoot;
+
+ public Task PopToRootAsync()
+ {
+ return PopToRootAsync(true);
+ }
+
+ public async Task PopToRootAsync(bool animated)
+ {
+ if (CurrentNavigationTask != null && !CurrentNavigationTask.IsCompleted)
+ {
+ var tcs = new TaskCompletionSource<bool>();
+ Task oldTask = CurrentNavigationTask;
+ CurrentNavigationTask = tcs.Task;
+ await oldTask;
+
+ await PopToRootAsyncInner(animated);
+ tcs.SetResult(true);
+ return;
+ }
+
+ Task result = PopToRootAsyncInner(animated);
+ CurrentNavigationTask = result;
+ await result;
+ }
+
+ public Task PushAsync(Page page)
+ {
+ return PushAsync(page, true);
+ }
+
+ public async Task PushAsync(Page page, bool animated)
+ {
+ if (CurrentNavigationTask != null && !CurrentNavigationTask.IsCompleted)
+ {
+ var tcs = new TaskCompletionSource<bool>();
+ Task oldTask = CurrentNavigationTask;
+ CurrentNavigationTask = tcs.Task;
+ await oldTask;
+
+ await PushAsyncInner(page, animated);
+ tcs.SetResult(true);
+ return;
+ }
+
+ CurrentNavigationTask = PushAsyncInner(page, animated);
+ await CurrentNavigationTask;
+ }
+
+ public event EventHandler<NavigationEventArgs> Pushed;
+
+ public static void SetBackButtonTitle(BindableObject page, string value)
+ {
+ page.SetValue(BackButtonTitleProperty, value);
+ }
+
+ public static void SetHasBackButton(Page page, bool value)
+ {
+ if (page == null)
+ throw new ArgumentNullException("page");
+ page.SetValue(HasBackButtonProperty, value);
+ }
+
+ public static void SetHasNavigationBar(BindableObject page, bool value)
+ {
+ page.SetValue(HasNavigationBarProperty, value);
+ }
+
+ public static void SetTitleIcon(BindableObject bindable, FileImageSource value)
+ {
+ bindable.SetValue(TitleIconProperty, value);
+ }
+
+ protected override bool OnBackButtonPressed()
+ {
+ if (CurrentPage.SendBackButtonPressed())
+ return true;
+
+ if (StackDepth > 1)
+ {
+ SafePop();
+ return true;
+ }
+
+ return base.OnBackButtonPressed();
+ }
+
+ internal event EventHandler<NavigationRequestedEventArgs> InsertPageBeforeRequested;
+
+ internal async Task<Page> PopAsyncInner(bool animated, bool fast = false)
+ {
+ if (StackDepth == 1)
+ {
+ return null;
+ }
+
+ var page = (Page)InternalChildren.Last();
+
+ 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;
+
+ InternalChildren.Remove(page);
+
+ CurrentPage = (Page)InternalChildren.Last();
+
+ if (Popped != null)
+ Popped(this, args);
+
+ return page;
+ }
+
+ internal event EventHandler<NavigationRequestedEventArgs> PopRequested;
+
+ internal event EventHandler<NavigationRequestedEventArgs> PopToRootRequested;
+
+ internal event EventHandler<NavigationRequestedEventArgs> PushRequested;
+
+ internal event EventHandler<NavigationRequestedEventArgs> RemovePageRequested;
+
+ void InsertPageBefore(Page page, Page before)
+ {
+ if (!InternalChildren.Contains(before))
+ throw new ArgumentException("before must be a child of the NavigationPage", "before");
+
+ if (InternalChildren.Contains(page))
+ throw new ArgumentException("Cannot insert page which is already in the navigation stack");
+
+ EventHandler<NavigationRequestedEventArgs> handler = InsertPageBeforeRequested;
+ if (handler != null)
+ handler(this, new NavigationRequestedEventArgs(page, before, false));
+
+ int index = InternalChildren.IndexOf(before);
+ InternalChildren.Insert(index, page);
+
+ // Shouldn't be required?
+ if (Width > 0 && Height > 0)
+ ForceLayout();
+ }
+
+ async Task PopToRootAsyncInner(bool animated)
+ {
+ if (StackDepth == 1)
+ return;
+
+ var root = (Page)InternalChildren.First();
+
+ InternalChildren.ToArray().Where(c => c != root).ForEach(c => InternalChildren.Remove(c));
+
+ CurrentPage = root;
+
+ var args = new NavigationRequestedEventArgs(root, animated);
+
+ EventHandler<NavigationRequestedEventArgs> requestPopToRoot = PopToRootRequested;
+ if (requestPopToRoot != null)
+ {
+ requestPopToRoot(this, args);
+
+ if (args.Task != null)
+ await args.Task;
+ }
+
+ if (PoppedToRoot != null)
+ PoppedToRoot(this, new NavigationEventArgs(root));
+ }
+
+ async Task PushAsyncInner(Page page, bool animated)
+ {
+ if (InternalChildren.Contains(page))
+ return;
+
+ 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;
+ }
+
+ if (Pushed != null)
+ Pushed(this, args);
+ }
+
+ void PushPage(Page page)
+ {
+ InternalChildren.Add(page);
+
+ CurrentPage = page;
+ }
+
+ void RemovePage(Page page)
+ {
+ if (page == CurrentPage && StackDepth <= 1)
+ throw new InvalidOperationException("Cannot remove root page when it is also the currently displayed page.");
+ if (page == CurrentPage)
+ {
+ Log.Warning("NavigationPage", "RemovePage called for CurrentPage object. This can result in undesired behavior, consider called PopAsync instead.");
+ PopAsync();
+ return;
+ }
+
+ if (!InternalChildren.Contains(page))
+ throw new ArgumentException("Page to remove must be contained on this Navigation Page");
+
+ EventHandler<NavigationRequestedEventArgs> handler = RemovePageRequested;
+ if (handler != null)
+ handler(this, new NavigationRequestedEventArgs(page, true));
+
+ InternalChildren.Remove(page);
+ }
+
+ void SafePop()
+ {
+ PopAsync(true).ContinueWith(t =>
+ {
+ if (t.IsFaulted)
+ throw t.Exception;
+ });
+ }
+
+ 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);
+ }
+ }
+ }
+} \ No newline at end of file