2 using System.Collections.Generic;
3 using System.Collections.ObjectModel;
4 using System.ComponentModel;
6 using System.Threading.Tasks;
7 using Tizen.NUI.Internals;
8 using Tizen.NUI.Binding;
13 /// A Page that manages the navigation and user-experience of a stack of other pages.
15 // [RenderWith(typeof(_NavigationPageRenderer))]
16 [EditorBrowsable(EditorBrowsableState.Never)]
17 public class NavigationPage : Page, IPageContainer<Page>, INavigationPageController, IElementConfiguration<NavigationPage>
20 /// Identifies the property associated with the title of the back button.
22 public static readonly BindableProperty BackButtonTitleProperty = BindableProperty.CreateAttached("BackButtonTitle", typeof(string), typeof(Page), null);
25 /// Backing store for the HasNavigationBar property.
27 public static readonly BindableProperty HasNavigationBarProperty = BindableProperty.CreateAttached("HasNavigationBar", typeof(bool), typeof(Page), true);
30 /// Backing store for the HasBackButton property.
32 public static readonly BindableProperty HasBackButtonProperty = BindableProperty.CreateAttached("HasBackButton", typeof(bool), typeof(NavigationPage), true);
35 /// Identifies the Tint bindable property.
37 [Obsolete("TintProperty is obsolete as of version 1.2.0. Please use BarBackgroundColorProperty and BarTextColorProperty to change NavigationPage bar color properties.")]
38 public static readonly BindableProperty TintProperty = BindableProperty.Create("Tint", typeof(Color), typeof(NavigationPage), /*Color.Default*/Color.Black);
41 /// Identifies the property associated with the color of the NavigationPage's bar background color.
43 public static readonly BindableProperty BarBackgroundColorProperty = BindableProperty.Create("BarBackgroundColor", typeof(Color), typeof(NavigationPage), /*Color.Default*/Color.Black);
46 /// Identifies the property associated with the color of the NavigationPage's bar text color.
48 public static readonly BindableProperty BarTextColorProperty = BindableProperty.Create("BarTextColor", typeof(Color), typeof(NavigationPage), /*Color.Default*/Color.Black);
51 /// Indicates the NavigationPage.SetTitleIcon/NavigationPage.GetTitleIcon property.
53 public static readonly BindableProperty TitleIconProperty = BindableProperty.CreateAttached("TitleIcon", typeof(FileImageSource), typeof(NavigationPage), default(FileImageSource));
55 static readonly BindablePropertyKey CurrentPagePropertyKey = BindableProperty.CreateReadOnly("CurrentPage", typeof(Page), typeof(NavigationPage), null);
58 /// Identifies the property associated with NavigationPage.CurrentPage
60 public static readonly BindableProperty CurrentPageProperty = CurrentPagePropertyKey.BindableProperty;
62 static readonly BindablePropertyKey RootPagePropertyKey = BindableProperty.CreateReadOnly(nameof(RootPage), typeof(Page), typeof(NavigationPage), null);
64 /// Identifies the property associated with NavigationPage.RootPage
66 public static readonly BindableProperty RootPageProperty = RootPagePropertyKey.BindableProperty;
69 /// Initializes a new NavigationPage object.
71 public NavigationPage()
73 _platformConfigurationRegistry = new Lazy<PlatformConfigurationRegistry<NavigationPage>>(() => new PlatformConfigurationRegistry<NavigationPage>(this));
75 Navigation = new NavigationImpl(this);
79 /// Creates a new NavigationPage element with root as its root element.
81 /// <param name="root">The root page.</param>
82 public NavigationPage(Page root) : this()
88 /// Gets or sets the background color for the bar at the top of the NavigationPage.
90 public Color BarBackgroundColor
92 get { return (Color)GetValue(BarBackgroundColorProperty); }
93 set { SetValue(BarBackgroundColorProperty, value); }
97 /// Gets or sets the text that appears on the bar at the top of the NavigationPage.
99 public Color BarTextColor
101 get { return (Color)GetValue(BarTextColorProperty); }
102 set { SetValue(BarTextColorProperty, value); }
106 /// The color to be used as the Tint of the NavigationPage.
108 [Obsolete("Tint is obsolete as of version 1.2.0. Please use BarBackgroundColor and BarTextColor to change NavigationPage bar color properties.")]
111 get { return (Color)GetValue(TintProperty); }
112 set { SetValue(TintProperty, value); }
115 internal Task CurrentNavigationTask { get; set; }
120 /// <param name="depth">The depth</param>
121 /// <returns>The page instance</returns>
122 [EditorBrowsable(EditorBrowsableState.Never)]
123 public Page Peek(int depth)
130 if (InternalChildren.Count <= depth)
135 return (Page)InternalChildren[InternalChildren.Count - depth - 1];
139 /// For internal use.
141 [EditorBrowsable(EditorBrowsableState.Never)]
142 public IEnumerable<Page> Pages => InternalChildren.Cast<Page>();
147 [EditorBrowsable(EditorBrowsableState.Never)]
148 public int StackDepth
150 get { return InternalChildren.Count; }
154 /// The Page that is currently top-most on the navigation stack.
156 public Page CurrentPage
158 get { return (Page)GetValue(CurrentPageProperty); }
159 private set { SetValue(CurrentPagePropertyKey, value); }
163 /// The Page that is the root of the navigation stack.
167 get { return (Page)GetValue(RootPageProperty); }
168 private set { SetValue(RootPagePropertyKey, value); }
172 /// The title of the back button for the specified page.
174 /// <param name="page">The Page whose back-button's title is being requested.</param>
175 /// <returns>The title of the back button that would be shown if the specified page were the Xamarin.Forms.CurrentPage.</returns>
176 public static string GetBackButtonTitle(BindableObject page)
178 return (string)page.GetValue(BackButtonTitleProperty);
182 /// Returns a value that indicates whether page has a back button.
184 /// <param name="page">The page to be checked</param>
185 /// <returns>true if the page has a back button.</returns>
186 public static bool GetHasBackButton(Page page)
189 throw new ArgumentNullException("page");
190 return (bool)page.GetValue(HasBackButtonProperty);
194 /// Returns a value that indicates whether the page has a navigation bar.
196 /// <param name="page">The Page being queried.</param>
197 /// <returns>true if page would display a navigation bar were it the CurrentPage.</returns>
198 public static bool GetHasNavigationBar(BindableObject page)
200 return (bool)page.GetValue(HasNavigationBarProperty);
203 internal static FileImageSource GetTitleIcon(BindableObject bindable)
205 return (FileImageSource)bindable.GetValue(TitleIconProperty);
209 /// Asynchronously removes the top Page from the navigation stack.
211 /// <returns>The Page that had been at the top of the navigation stack.</returns>
212 public Task<Page> PopAsync()
214 return PopAsync(true);
218 /// Asynchronously removes the top Page from the navigation stack, with optional animation.
220 /// <param name="animated">Whether to animate the pop.</param>
221 /// <returns>The Page that had been at the top of the navigation stack.</returns>
222 public async Task<Page> PopAsync(bool animated)
224 var tcs = new TaskCompletionSource<bool>();
225 if (CurrentNavigationTask != null && !CurrentNavigationTask.IsCompleted)
227 var oldTask = CurrentNavigationTask;
228 CurrentNavigationTask = tcs.Task;
232 CurrentNavigationTask = tcs.Task;
234 var result = await PopAsyncInner(animated, false);
240 /// Event that is raised after a page is popped from this NavigationPage element.
242 public event EventHandler<NavigationEventArgs> Popped;
245 /// Event that is raised when the last nonroot element is popped from this NavigationPage element.
247 public event EventHandler<NavigationEventArgs> PoppedToRoot;
250 /// Pops all but the root Page off the navigation stack.
252 /// <returns>A task that represents the asynchronous dismiss operation.</returns>
253 public Task PopToRootAsync()
255 return PopToRootAsync(true);
259 /// A task for asynchronously popping all pages off of the navigation stack.
261 /// <param name="animated">Whether to animate the pop.</param>
262 /// <returns>A task that represents the asynchronous dismiss operation.</returns>
263 public async Task PopToRootAsync(bool animated)
265 if (CurrentNavigationTask != null && !CurrentNavigationTask.IsCompleted)
267 var tcs = new TaskCompletionSource<bool>();
268 Task oldTask = CurrentNavigationTask;
269 CurrentNavigationTask = tcs.Task;
272 await PopToRootAsyncInner(animated);
277 Task result = PopToRootAsyncInner(animated);
278 CurrentNavigationTask = result;
283 /// Presents a Page modally.
285 /// <param name="page">The Page to present modally.</param>
286 /// <returns>An awaitable Task, indicating the PushModal completion.</returns>
287 public Task PushAsync(Page page)
289 return PushAsync(page, true);
293 /// A task for asynchronously pushing a page onto the navigation stack, with optional animation.
295 /// <param name="page">The Page to present modally.</param>
296 /// <param name="animated">Whether to animate the pop.</param>
297 /// <returns>An awaitable Task, indicating the PushModal completion.</returns>
298 public async Task PushAsync(Page page, bool animated)
300 if (CurrentNavigationTask != null && !CurrentNavigationTask.IsCompleted)
302 var tcs = new TaskCompletionSource<bool>();
303 Task oldTask = CurrentNavigationTask;
304 CurrentNavigationTask = tcs.Task;
307 await PushAsyncInner(page, animated);
312 CurrentNavigationTask = PushAsyncInner(page, animated);
313 await CurrentNavigationTask;
317 /// Event that is raised when a page is pushed onto this NavigationPage element.
319 public event EventHandler<NavigationEventArgs> Pushed;
322 /// Sets the title that appears on the back button for page.
324 /// <param name="page">The BindableObject object.</param>
325 /// <param name="value">The value to set.</param>
326 public static void SetBackButtonTitle(BindableObject page, string value)
328 page.SetValue(BackButtonTitleProperty, value);
332 /// Adds or removes a back button to page, with optional animation.
334 /// <param name="page">The page object.</param>
335 /// <param name="value">The value to set.</param>
336 public static void SetHasBackButton(Page page, bool value)
339 throw new ArgumentNullException("page");
340 page.SetValue(HasBackButtonProperty, value);
344 /// Sets a value that indicates whether or not this NavigationPage element has a navigation bar.
346 /// <param name="page">The BindableObject object</param>
347 /// <param name="value">The value to set</param>
348 public static void SetHasNavigationBar(BindableObject page, bool value)
350 page.SetValue(HasNavigationBarProperty, value);
353 internal static void SetTitleIcon(BindableObject bindable, FileImageSource value)
355 bindable.SetValue(TitleIconProperty, value);
359 /// Event that is raised when the hardware back button is pressed.
361 /// <returns>true if consumed</returns>
362 protected override bool OnBackButtonPressed()
364 if (CurrentPage.SendBackButtonPressed())
373 return base.OnBackButtonPressed();
379 [EditorBrowsable(EditorBrowsableState.Never)]
380 public event EventHandler<NavigationRequestedEventArgs> InsertPageBeforeRequested;
385 /// <param name="animated">Whether animate the pop.</param>
386 /// <param name="fast"></param>
387 /// <returns>A task that represents the asynchronous dismiss operation.</returns>
388 [EditorBrowsable(EditorBrowsableState.Never)]
389 public async Task<Page> PopAsyncInner(bool animated, bool fast)
396 var page = (Page)InternalChildren.Last();
398 return await (this as INavigationPageController).RemoveAsyncInner(page, animated, fast);
401 async Task<Page> INavigationPageController.RemoveAsyncInner(Page page, bool animated, bool fast)
408 var args = new NavigationRequestedEventArgs(page, animated);
412 EventHandler<NavigationRequestedEventArgs> requestPop = PopRequested;
413 if (requestPop != null)
415 requestPop(this, args);
417 if (args.Task != null && !fast)
418 removed = await args.Task;
421 if (!removed && !fast)
424 InternalChildren.Remove(page);
426 CurrentPage = (Page)InternalChildren.Last();
435 /// For internal use.
437 [EditorBrowsable(EditorBrowsableState.Never)]
438 public event EventHandler<NavigationRequestedEventArgs> PopRequested;
441 /// For internal use.
443 [EditorBrowsable(EditorBrowsableState.Never)]
444 public event EventHandler<NavigationRequestedEventArgs> PopToRootRequested;
447 /// For internal use.
449 [EditorBrowsable(EditorBrowsableState.Never)]
450 public event EventHandler<NavigationRequestedEventArgs> PushRequested;
453 /// For internal use.
455 [EditorBrowsable(EditorBrowsableState.Never)]
456 public event EventHandler<NavigationRequestedEventArgs> RemovePageRequested;
458 void InsertPageBefore(Page page, Page before)
461 throw new ArgumentNullException($"{nameof(page)} cannot be null.");
464 throw new ArgumentNullException($"{nameof(before)} cannot be null.");
466 if (!InternalChildren.Contains(before))
467 throw new ArgumentException($"{nameof(before)} must be a child of the NavigationPage", nameof(before));
469 if (InternalChildren.Contains(page))
470 throw new ArgumentException("Cannot insert page which is already in the navigation stack");
472 EventHandler<NavigationRequestedEventArgs> handler = InsertPageBeforeRequested;
473 handler?.Invoke(this, new NavigationRequestedEventArgs(page, before, false));
475 int index = InternalChildren.IndexOf(before);
476 InternalChildren.Insert(index, page);
481 // Shouldn't be required?
482 // if (Width > 0 && Height > 0)
486 async Task PopToRootAsyncInner(bool animated)
491 Element[] childrenToRemove = InternalChildren.Skip(1).ToArray();
492 foreach (Element child in childrenToRemove)
493 InternalChildren.Remove(child);
495 CurrentPage = RootPage;
497 var args = new NavigationRequestedEventArgs(RootPage, animated);
499 EventHandler<NavigationRequestedEventArgs> requestPopToRoot = PopToRootRequested;
500 if (requestPopToRoot != null)
502 requestPopToRoot(this, args);
504 if (args.Task != null)
508 // PoppedToRoot?.Invoke(this, new PoppedToRootEventArgs(RootPage, childrenToRemove.OfType<Page>().ToList()));
511 async Task PushAsyncInner(Page page, bool animated)
513 if (InternalChildren.Contains(page))
518 var args = new NavigationRequestedEventArgs(page, animated);
520 EventHandler<NavigationRequestedEventArgs> requestPush = PushRequested;
521 if (requestPush != null)
523 requestPush(this, args);
525 if (args.Task != null)
529 Pushed?.Invoke(this, args);
532 void PushPage(Page page)
534 InternalChildren.Add(page);
536 if (InternalChildren.Count == 1)
542 void RemovePage(Page page)
545 throw new ArgumentNullException($"{nameof(page)} cannot be null.");
547 if (page == CurrentPage && CurrentPage == RootPage)
548 throw new InvalidOperationException("Cannot remove root page when it is also the currently displayed page.");
549 if (page == CurrentPage)
551 // Log.Warning("NavigationPage", "RemovePage called for CurrentPage object. This can result in undesired behavior, consider calling PopAsync instead.");
556 if (!InternalChildren.Contains(page))
557 throw new ArgumentException("Page to remove must be contained on this Navigation Page");
559 EventHandler<NavigationRequestedEventArgs> handler = RemovePageRequested;
560 handler?.Invoke(this, new NavigationRequestedEventArgs(page, true));
562 InternalChildren.Remove(page);
563 if (RootPage == page)
564 RootPage = (Page)InternalChildren.First();
569 PopAsync(true).ContinueWith(t =>
576 class NavigationImpl : NavigationProxy
578 // readonly Lazy<ReadOnlyCastingList<Page, Element>> _castingList;
580 public NavigationImpl(NavigationPage owner)
583 // _castingList = new Lazy<ReadOnlyCastingList<Page, Element>>(() => new ReadOnlyCastingList<Page, Element>(Owner.InternalChildren));
586 NavigationPage Owner { get; }
588 protected override IReadOnlyList<Page> GetNavigationStack()
590 // return _castingList.Value;
594 protected override void OnInsertPageBefore(Page page, Page before)
596 Owner.InsertPageBefore(page, before);
599 protected override Task<Page> OnPopAsync(bool animated)
601 return Owner.PopAsync(animated);
604 protected override Task OnPopToRootAsync(bool animated)
606 return Owner.PopToRootAsync(animated);
609 protected override Task OnPushAsync(Page root, bool animated)
611 return Owner.PushAsync(root, animated);
614 protected override void OnRemovePage(Page page)
616 Owner.RemovePage(page);
620 readonly Lazy<PlatformConfigurationRegistry<NavigationPage>> _platformConfigurationRegistry;
623 /// Returns the platform-specific instance of this NavigationPage, on which a platform-specific method may be called.
625 /// <typeparam name="T">The platform for which to return an instance.</typeparam>
626 /// <returns>The platform-specific instance of this NavigationPage</returns>
627 public new IPlatformElementConfiguration<T, NavigationPage> On<T>() where T : IConfigPlatform
629 return _platformConfigurationRegistry.Value.On<T>();