summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Xamarin.Forms.Core.UnitTests/NavigationUnitTest.cs103
-rw-r--r--Xamarin.Forms.Core/NavigationPage.cs53
-rw-r--r--docs/Xamarin.Forms.Core/Xamarin.Forms/NavigationPage.xml36
3 files changed, 170 insertions, 22 deletions
diff --git a/Xamarin.Forms.Core.UnitTests/NavigationUnitTest.cs b/Xamarin.Forms.Core.UnitTests/NavigationUnitTest.cs
index d82a97a4..fb68d14e 100644
--- a/Xamarin.Forms.Core.UnitTests/NavigationUnitTest.cs
+++ b/Xamarin.Forms.Core.UnitTests/NavigationUnitTest.cs
@@ -14,6 +14,7 @@ namespace Xamarin.Forms.Core.UnitTests
{
NavigationPage nav = new NavigationPage ();
+ Assert.IsNull(nav.RootPage);
Assert.IsNull (nav.CurrentPage);
Label child = new Label {Text = "Label"};
@@ -21,7 +22,9 @@ namespace Xamarin.Forms.Core.UnitTests
await nav.Navigation.PushAsync (childRoot);
- Assert.AreSame (childRoot, nav.CurrentPage);
+ Assert.AreSame(childRoot, nav.RootPage);
+ Assert.AreSame(childRoot, nav.CurrentPage);
+ Assert.AreSame(nav.RootPage, nav.CurrentPage);
}
[Test]
@@ -40,16 +43,26 @@ namespace Xamarin.Forms.Core.UnitTests
bool fired = false;
nav.Popped += (sender, e) => fired = true;
+
+ Assert.AreSame(childRoot, nav.RootPage);
+ Assert.AreNotSame(childRoot2, nav.RootPage);
+ Assert.AreNotSame(nav.RootPage, nav.CurrentPage);
+
var popped = await nav.Navigation.PopAsync ();
Assert.True (fired);
- Assert.AreSame (childRoot, nav.CurrentPage);
+ Assert.AreSame(childRoot, nav.RootPage);
+ Assert.AreSame(childRoot, nav.CurrentPage);
+ Assert.AreSame(nav.RootPage, nav.CurrentPage);
Assert.AreEqual (childRoot2, popped);
await nav.PopAsync ();
var last = await nav.Navigation.PopAsync ();
Assert.IsNull (last);
+ Assert.IsNotNull(nav.RootPage);
+ Assert.IsNotNull(nav.CurrentPage);
+ Assert.AreSame(nav.RootPage, nav.CurrentPage);
}
[Test]
@@ -57,6 +70,7 @@ namespace Xamarin.Forms.Core.UnitTests
{
NavigationPage nav = new NavigationPage ();
+ Assert.IsNull(nav.RootPage);
Assert.IsNull (nav.CurrentPage);
Label child = new Label {Text = "Label"};
@@ -64,7 +78,9 @@ namespace Xamarin.Forms.Core.UnitTests
await nav.PushAsync (childRoot);
- Assert.AreSame (childRoot, nav.CurrentPage);
+ Assert.AreSame (childRoot, nav.RootPage);
+ Assert.AreSame(childRoot, nav.CurrentPage);
+ Assert.AreSame(nav.RootPage, nav.CurrentPage);
}
[Test]
@@ -96,10 +112,15 @@ namespace Xamarin.Forms.Core.UnitTests
bool fired = false;
nav.Pushed += (sender, e) => fired = true;
+ Assert.AreSame(childRoot, nav.RootPage);
+ Assert.AreSame(childRoot, nav.CurrentPage);
+
await nav.PushAsync (childRoot);
Assert.False (fired);
- Assert.AreEqual (childRoot, nav.CurrentPage);
+ Assert.AreSame(childRoot, nav.RootPage);
+ Assert.AreSame(childRoot, nav.CurrentPage);
+ Assert.AreSame(nav.RootPage, nav.CurrentPage);
}
[Test]
@@ -184,7 +205,9 @@ namespace Xamarin.Forms.Core.UnitTests
nav.PopToRootAsync ();
Assert.True (signaled);
- Assert.AreEqual (root, nav.CurrentPage);
+ Assert.AreSame (root, nav.RootPage);
+ Assert.AreSame(root, nav.CurrentPage);
+ Assert.AreSame(nav.RootPage, nav.CurrentPage);
}
[Test]
@@ -209,7 +232,9 @@ namespace Xamarin.Forms.Core.UnitTests
Assert.AreEqual (2, poppedChildren.Count);
Assert.Contains (child1, poppedChildren);
Assert.Contains (child2, poppedChildren);
- Assert.AreEqual (root, nav.CurrentPage);
+ Assert.AreSame(root, nav.RootPage);
+ Assert.AreSame(root, nav.CurrentPage);
+ Assert.AreSame(nav.RootPage, nav.CurrentPage);
}
[Test]
@@ -458,6 +483,72 @@ namespace Xamarin.Forms.Core.UnitTests
Assert.False (result);
}
+ [Test]
+ public void TestInsertPage()
+ {
+ var root = new ContentPage { Title = "Root" };
+ var newPage = new ContentPage();
+ var navPage = new NavigationPage(root);
+
+ navPage.Navigation.InsertPageBefore(newPage, navPage.RootPage);
+
+ Assert.AreSame(newPage, navPage.RootPage);
+ Assert.AreNotSame(newPage, navPage.CurrentPage);
+ Assert.AreNotSame(navPage.RootPage, navPage.CurrentPage);
+ Assert.AreSame(root, navPage.CurrentPage);
+
+ Assert.Throws<ArgumentException>(() =>
+ {
+ navPage.Navigation.InsertPageBefore(new ContentPage(), new ContentPage());
+ });
+
+ Assert.Throws<ArgumentException>(() =>
+ {
+ navPage.Navigation.InsertPageBefore(navPage.RootPage, navPage.CurrentPage);
+ });
+
+ Assert.Throws<ArgumentNullException>(() =>
+ {
+ navPage.Navigation.InsertPageBefore(null, navPage.CurrentPage);
+ });
+
+ Assert.Throws<ArgumentNullException>(() =>
+ {
+ navPage.Navigation.InsertPageBefore(new ContentPage(), null);
+ });
+ }
+
+ [Test]
+ public async void TestRemovePage()
+ {
+ var root = new ContentPage { Title = "Root" };
+ var newPage = new ContentPage();
+ var navPage = new NavigationPage(root);
+ await navPage.PushAsync(newPage);
+
+ navPage.Navigation.RemovePage(root);
+
+ Assert.AreSame(newPage, navPage.RootPage);
+ Assert.AreSame(newPage, navPage.CurrentPage);
+ Assert.AreSame(navPage.RootPage, navPage.CurrentPage);
+ Assert.AreNotSame(root, navPage.CurrentPage);
+
+ Assert.Throws<ArgumentException>(() =>
+ {
+ navPage.Navigation.RemovePage(root);
+ });
+
+ Assert.Throws<InvalidOperationException>(() =>
+ {
+ navPage.Navigation.RemovePage(newPage);
+ });
+
+ Assert.Throws<ArgumentNullException>(() =>
+ {
+ navPage.Navigation.RemovePage(null);
+ });
+ }
+
[Test (Description = "CurrentPage should not be set to null when you attempt to pop the last page")]
[Property ("Bugzilla", 28335)]
public async Task CurrentPageNotNullPoppingRoot()
diff --git a/Xamarin.Forms.Core/NavigationPage.cs b/Xamarin.Forms.Core/NavigationPage.cs
index fdafec06..7663d1bf 100644
--- a/Xamarin.Forms.Core/NavigationPage.cs
+++ b/Xamarin.Forms.Core/NavigationPage.cs
@@ -28,7 +28,10 @@ namespace Xamarin.Forms
static readonly BindablePropertyKey CurrentPagePropertyKey = BindableProperty.CreateReadOnly("CurrentPage", typeof(Page), typeof(NavigationPage), null);
public static readonly BindableProperty CurrentPageProperty = CurrentPagePropertyKey.BindableProperty;
-
+
+ static readonly BindablePropertyKey RootPagePropertyKey = BindableProperty.CreateReadOnly(nameof(RootPage), typeof(Page), typeof(NavigationPage), null);
+ public static readonly BindableProperty RootPageProperty = RootPagePropertyKey.BindableProperty;
+
public NavigationPage()
{
_platformConfigurationRegistry = new Lazy<PlatformConfigurationRegistry<NavigationPage>>(() => new PlatformConfigurationRegistry<NavigationPage>(this));
@@ -92,6 +95,12 @@ namespace Xamarin.Forms
private set { SetValue(CurrentPagePropertyKey, value); }
}
+ public Page RootPage
+ {
+ get { return (Page)GetValue(RootPageProperty); }
+ private set { SetValue(RootPagePropertyKey, value); }
+ }
+
public static string GetBackButtonTitle(BindableObject page)
{
return (string)page.GetValue(BackButtonTitleProperty);
@@ -304,8 +313,14 @@ namespace Xamarin.Forms
void InsertPageBefore(Page page, Page before)
{
+ if (page == null)
+ throw new ArgumentNullException($"{nameof(page)} cannot be null.");
+
+ if (before == null)
+ throw new ArgumentNullException($"{nameof(before)} cannot be null.");
+
if (!PageController.InternalChildren.Contains(before))
- throw new ArgumentException("before must be a child of the NavigationPage", "before");
+ throw new ArgumentException($"{nameof(before)} must be a child of the NavigationPage", nameof(before));
if (PageController.InternalChildren.Contains(page))
throw new ArgumentException("Cannot insert page which is already in the navigation stack");
@@ -316,6 +331,9 @@ namespace Xamarin.Forms
int index = PageController.InternalChildren.IndexOf(before);
PageController.InternalChildren.Insert(index, page);
+ if (index == 0)
+ RootPage = page;
+
// Shouldn't be required?
if (Width > 0 && Height > 0)
ForceLayout();
@@ -326,15 +344,13 @@ namespace Xamarin.Forms
if (((INavigationPageController)this).StackDepth == 1)
return;
- var root = (Page)PageController.InternalChildren.First();
-
- var childrenToRemove = PageController.InternalChildren.ToArray().Where(c => c != root);
- foreach (var child in childrenToRemove)
+ Element[] childrenToRemove = PageController.InternalChildren.Skip(1).ToArray();
+ foreach (Element child in childrenToRemove)
PageController.InternalChildren.Remove(child);
- CurrentPage = root;
+ CurrentPage = RootPage;
- var args = new NavigationRequestedEventArgs(root, animated);
+ var args = new NavigationRequestedEventArgs(RootPage, animated);
EventHandler<NavigationRequestedEventArgs> requestPopToRoot = PopToRootRequestedInternal;
if (requestPopToRoot != null)
@@ -345,8 +361,7 @@ namespace Xamarin.Forms
await args.Task;
}
- if (PoppedToRoot != null)
- PoppedToRoot(this, new PoppedToRootEventArgs(root, childrenToRemove.OfType<Page>().ToList()));
+ PoppedToRoot?.Invoke(this, new PoppedToRootEventArgs(RootPage, childrenToRemove.OfType<Page>().ToList()));
}
async Task PushAsyncInner(Page page, bool animated)
@@ -367,24 +382,29 @@ namespace Xamarin.Forms
await args.Task;
}
- if (Pushed != null)
- Pushed(this, args);
+ Pushed?.Invoke(this, args);
}
void PushPage(Page page)
{
PageController.InternalChildren.Add(page);
+ if (PageController.InternalChildren.Count == 1)
+ RootPage = page;
+
CurrentPage = page;
}
void RemovePage(Page page)
{
- if (page == CurrentPage && ((INavigationPageController)this).StackDepth <= 1)
+ if (page == null)
+ throw new ArgumentNullException($"{nameof(page)} cannot be null.");
+
+ if (page == CurrentPage && CurrentPage == RootPage)
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.");
+ Log.Warning("NavigationPage", "RemovePage called for CurrentPage object. This can result in undesired behavior, consider calling PopAsync instead.");
PopAsync();
return;
}
@@ -393,10 +413,11 @@ namespace Xamarin.Forms
throw new ArgumentException("Page to remove must be contained on this Navigation Page");
EventHandler<NavigationRequestedEventArgs> handler = RemovePageRequestedInternal;
- if (handler != null)
- handler(this, new NavigationRequestedEventArgs(page, true));
+ handler?.Invoke(this, new NavigationRequestedEventArgs(page, true));
PageController.InternalChildren.Remove(page);
+ if (RootPage == page)
+ RootPage = (Page)PageController.InternalChildren.First();
}
void SafePop()
diff --git a/docs/Xamarin.Forms.Core/Xamarin.Forms/NavigationPage.xml b/docs/Xamarin.Forms.Core/Xamarin.Forms/NavigationPage.xml
index 1094d154..c2afcedd 100644
--- a/docs/Xamarin.Forms.Core/Xamarin.Forms/NavigationPage.xml
+++ b/docs/Xamarin.Forms.Core/Xamarin.Forms/NavigationPage.xml
@@ -226,6 +226,42 @@
</remarks>
</Docs>
</Member>
+ <Member MemberName="RootPage">
+ <MemberSignature Language="C#" Value="public Xamarin.Forms.Page RootPage { get; }" />
+ <MemberSignature Language="ILAsm" Value=".property instance class Xamarin.Forms.Page RootPage" />
+ <MemberType>Property</MemberType>
+ <AssemblyInfo>
+ <AssemblyVersion>2.0.0.0</AssemblyVersion>
+ </AssemblyInfo>
+ <ReturnValue>
+ <ReturnType>Xamarin.Forms.Page</ReturnType>
+ </ReturnValue>
+ <Docs>
+ <summary>
+ The <see cref="T:Xamarin.Forms.Page" /> that is the root of the navigation stack.
+ </summary>
+ <value>To be added.</value>
+ <remarks>To be added.</remarks>
+ </Docs>
+ </Member>
+ <Member MemberName="RootPageProperty">
+ <MemberSignature Language="C#" Value="public static readonly Xamarin.Forms.BindableProperty RootPageProperty;" />
+ <MemberSignature Language="ILAsm" Value=".field public static initonly class Xamarin.Forms.BindableProperty RootPageProperty" />
+ <MemberType>Field</MemberType>
+ <AssemblyInfo>
+ <AssemblyVersion>2.0.0.0</AssemblyVersion>
+ </AssemblyInfo>
+ <ReturnValue>
+ <ReturnType>Xamarin.Forms.BindableProperty</ReturnType>
+ </ReturnValue>
+ <Docs>
+ <summary>
+ Identifies the <see cref="P:Xamarin.Forms.NavigationPage.RootPage" /> property.
+ </summary>
+ <remarks>
+ </remarks>
+ </Docs>
+ </Member>
<Member MemberName="GetBackButtonTitle">
<MemberSignature Language="C#" Value="public static string GetBackButtonTitle (Xamarin.Forms.BindableObject page);" />
<MemberSignature Language="ILAsm" Value=".method public static hidebysig string GetBackButtonTitle(class Xamarin.Forms.BindableObject page) cil managed" />