2 using System.Collections.Generic;
3 using System.Collections.ObjectModel;
4 using System.ComponentModel;
6 using System.Threading.Tasks;
7 using Tizen.NUI.Binding.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 internal 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 Tizen.NUI.Xaml.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 /// Pops all but the root Page off the navigation stack.
247 /// <returns>A task that represents the asynchronous dismiss operation.</returns>
248 public Task PopToRootAsync()
250 return PopToRootAsync(true);
254 /// A task for asynchronously popping all pages off of the navigation stack.
256 /// <param name="animated">Whether to animate the pop.</param>
257 /// <returns>A task that represents the asynchronous dismiss operation.</returns>
258 public async Task PopToRootAsync(bool animated)
260 if (CurrentNavigationTask != null && !CurrentNavigationTask.IsCompleted)
262 var tcs = new TaskCompletionSource<bool>();
263 Task oldTask = CurrentNavigationTask;
264 CurrentNavigationTask = tcs.Task;
267 await PopToRootAsyncInner(animated);
272 Task result = PopToRootAsyncInner(animated);
273 CurrentNavigationTask = result;
278 /// Presents a Page modally.
280 /// <param name="page">The Page to present modally.</param>
281 /// <returns>An awaitable Task, indicating the PushModal completion.</returns>
282 public Task PushAsync(Page page)
284 return PushAsync(page, true);
288 /// A task for asynchronously pushing a page onto the navigation stack, with optional animation.
290 /// <param name="page">The Page to present modally.</param>
291 /// <param name="animated">Whether to animate the pop.</param>
292 /// <returns>An awaitable Task, indicating the PushModal completion.</returns>
293 public async Task PushAsync(Page page, bool animated)
295 if (CurrentNavigationTask != null && !CurrentNavigationTask.IsCompleted)
297 var tcs = new TaskCompletionSource<bool>();
298 Task oldTask = CurrentNavigationTask;
299 CurrentNavigationTask = tcs.Task;
302 await PushAsyncInner(page, animated);
307 CurrentNavigationTask = PushAsyncInner(page, animated);
308 await CurrentNavigationTask;
312 /// Event that is raised when a page is pushed onto this NavigationPage element.
314 public event EventHandler<NavigationEventArgs> Pushed;
317 /// Sets the title that appears on the back button for page.
319 /// <param name="page">The BindableObject object.</param>
320 /// <param name="value">The value to set.</param>
321 public static void SetBackButtonTitle(BindableObject page, string value)
323 page.SetValue(BackButtonTitleProperty, value);
327 /// Adds or removes a back button to page, with optional animation.
329 /// <param name="page">The page object.</param>
330 /// <param name="value">The value to set.</param>
331 public static void SetHasBackButton(Page page, bool value)
334 throw new ArgumentNullException("page");
335 page.SetValue(HasBackButtonProperty, value);
339 /// Sets a value that indicates whether or not this NavigationPage element has a navigation bar.
341 /// <param name="page">The BindableObject object</param>
342 /// <param name="value">The value to set</param>
343 public static void SetHasNavigationBar(BindableObject page, bool value)
345 page.SetValue(HasNavigationBarProperty, value);
348 internal static void SetTitleIcon(BindableObject bindable, FileImageSource value)
350 bindable.SetValue(TitleIconProperty, value);
354 /// Event that is raised when the hardware back button is pressed.
356 /// <returns>true if consumed</returns>
357 protected override bool OnBackButtonPressed()
359 if (CurrentPage.SendBackButtonPressed())
368 return base.OnBackButtonPressed();
374 [EditorBrowsable(EditorBrowsableState.Never)]
375 public event EventHandler<NavigationRequestedEventArgs> InsertPageBeforeRequested;
380 /// <param name="animated">Whether animate the pop.</param>
381 /// <param name="fast"></param>
382 /// <returns>A task that represents the asynchronous dismiss operation.</returns>
383 [EditorBrowsable(EditorBrowsableState.Never)]
384 public async Task<Page> PopAsyncInner(bool animated, bool fast)
391 var page = (Page)InternalChildren.Last();
393 return await (this as INavigationPageController).RemoveAsyncInner(page, animated, fast);
396 async Task<Page> INavigationPageController.RemoveAsyncInner(Page page, bool animated, bool fast)
403 var args = new NavigationRequestedEventArgs(page, animated);
407 EventHandler<NavigationRequestedEventArgs> requestPop = PopRequested;
408 if (requestPop != null)
410 requestPop(this, args);
412 if (args.Task != null && !fast)
413 removed = await args.Task;
416 if (!removed && !fast)
419 InternalChildren.Remove(page);
421 CurrentPage = (Page)InternalChildren.Last();
430 /// For internal use.
432 [EditorBrowsable(EditorBrowsableState.Never)]
433 public event EventHandler<NavigationRequestedEventArgs> PopRequested;
436 /// For internal use.
438 [EditorBrowsable(EditorBrowsableState.Never)]
439 public event EventHandler<NavigationRequestedEventArgs> PopToRootRequested;
442 /// For internal use.
444 [EditorBrowsable(EditorBrowsableState.Never)]
445 public event EventHandler<NavigationRequestedEventArgs> PushRequested;
448 /// For internal use.
450 [EditorBrowsable(EditorBrowsableState.Never)]
451 public event EventHandler<NavigationRequestedEventArgs> RemovePageRequested;
453 void InsertPageBefore(Page page, Page before)
456 throw new ArgumentNullException($"{nameof(page)} cannot be null.");
459 throw new ArgumentNullException($"{nameof(before)} cannot be null.");
461 if (!InternalChildren.Contains(before))
462 throw new ArgumentException($"{nameof(before)} must be a child of the NavigationPage", nameof(before));
464 if (InternalChildren.Contains(page))
465 throw new ArgumentException("Cannot insert page which is already in the navigation stack");
467 EventHandler<NavigationRequestedEventArgs> handler = InsertPageBeforeRequested;
468 handler?.Invoke(this, new NavigationRequestedEventArgs(page, before, false));
470 int index = InternalChildren.IndexOf(before);
471 InternalChildren.Insert(index, page);
476 // Shouldn't be required?
477 // if (Width > 0 && Height > 0)
481 async Task PopToRootAsyncInner(bool animated)
486 Element[] childrenToRemove = InternalChildren.Skip(1).ToArray();
487 foreach (Element child in childrenToRemove)
488 InternalChildren.Remove(child);
490 CurrentPage = RootPage;
492 var args = new NavigationRequestedEventArgs(RootPage, animated);
494 EventHandler<NavigationRequestedEventArgs> requestPopToRoot = PopToRootRequested;
495 if (requestPopToRoot != null)
497 requestPopToRoot(this, args);
499 if (args.Task != null)
503 // PoppedToRoot?.Invoke(this, new PoppedToRootEventArgs(RootPage, childrenToRemove.OfType<Page>().ToList()));
506 async Task PushAsyncInner(Page page, bool animated)
508 if (InternalChildren.Contains(page))
513 var args = new NavigationRequestedEventArgs(page, animated);
515 EventHandler<NavigationRequestedEventArgs> requestPush = PushRequested;
516 if (requestPush != null)
518 requestPush(this, args);
520 if (args.Task != null)
524 Pushed?.Invoke(this, args);
527 void PushPage(Page page)
529 InternalChildren.Add(page);
531 if (InternalChildren.Count == 1)
537 void RemovePage(Page page)
540 throw new ArgumentNullException($"{nameof(page)} cannot be null.");
542 if (page == CurrentPage && CurrentPage == RootPage)
543 throw new InvalidOperationException("Cannot remove root page when it is also the currently displayed page.");
544 if (page == CurrentPage)
546 // Log.Warning("NavigationPage", "RemovePage called for CurrentPage object. This can result in undesired behavior, consider calling PopAsync instead.");
551 if (!InternalChildren.Contains(page))
552 throw new ArgumentException("Page to remove must be contained on this Navigation Page");
554 EventHandler<NavigationRequestedEventArgs> handler = RemovePageRequested;
555 handler?.Invoke(this, new NavigationRequestedEventArgs(page, true));
557 InternalChildren.Remove(page);
558 if (RootPage == page)
559 RootPage = (Page)InternalChildren.First();
564 PopAsync(true).ContinueWith(t =>
571 class NavigationImpl : NavigationProxy
573 // readonly Lazy<ReadOnlyCastingList<Page, Element>> _castingList;
575 public NavigationImpl(NavigationPage owner)
578 // _castingList = new Lazy<ReadOnlyCastingList<Page, Element>>(() => new ReadOnlyCastingList<Page, Element>(Owner.InternalChildren));
581 NavigationPage Owner { get; }
583 protected override IReadOnlyList<Page> GetNavigationStack()
585 // return _castingList.Value;
589 protected override void OnInsertPageBefore(Page page, Page before)
591 Owner.InsertPageBefore(page, before);
594 protected override Task<Page> OnPopAsync(bool animated)
596 return Owner.PopAsync(animated);
599 protected override Task OnPopToRootAsync(bool animated)
601 return Owner.PopToRootAsync(animated);
604 protected override Task OnPushAsync(Page root, bool animated)
606 return Owner.PushAsync(root, animated);
609 protected override void OnRemovePage(Page page)
611 Owner.RemovePage(page);
615 readonly Lazy<PlatformConfigurationRegistry<NavigationPage>> _platformConfigurationRegistry;
618 /// Returns the platform-specific instance of this NavigationPage, on which a platform-specific method may be called.
620 /// <typeparam name="T">The platform for which to return an instance.</typeparam>
621 /// <returns>The platform-specific instance of this NavigationPage</returns>
622 public new IPlatformElementConfiguration<T, NavigationPage> On<T>() where T : IConfigPlatform
624 return _platformConfigurationRegistry.Value.On<T>();