[Core] Added RootPage to NavigationPage (#464)
authoradrianknight89 <adrianknight89@outlook.com>
Thu, 2 Feb 2017 15:35:23 +0000 (09:35 -0600)
committerKangho Hur <kangho.hur@samsung.com>
Fri, 24 Mar 2017 04:18:57 +0000 (13:18 +0900)
* d

* removed whitespace

* Using ArgumentNullException

* changes

Xamarin.Forms.Core.UnitTests/NavigationUnitTest.cs
Xamarin.Forms.Core/NavigationPage.cs
docs/Xamarin.Forms.Core/Xamarin.Forms/NavigationPage.xml

index d82a97a..fb68d14 100644 (file)
@@ -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()
index fdafec0..7663d1b 100644 (file)
@@ -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()
index 1094d15..c2afced 100644 (file)
         </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" />