操纵导航堆栈
有时需要改变正常的面向堆栈的导航流程。例如,假设页面需要来自用户的一些信息,但首先它导航到提供一些指令或免责声明的页面,然后从那里导航到实际获取信息的页面。当用户完成并返回时,您将希望跳过包含说明或免责声明的页面。该页面应从导航堆栈中删除。
这是一个类似的例子:假设用户正在与获取某些信息的页面进行交互,然后想要返回上一页。但是,程序检测到此信息出现问题,需要在单独的页面上进行扩展讨论。该程序可以将新页面插入导航堆栈以提供该讨论。
或者某个页面序列可能以标记为“转到主页”的按钮结束,并且在导航回主页时可以简单地跳过其间的所有页面。
INavigation接口定义了所有这三种情况的方法。它们被命名为RemovePage,InsertPageBefore和PopToRootAsync。
StackManipulation程序以非常抽象的方式演示了这三种方法。该程序由五个仅代码页面组成,分别名为PageA,PageB,PageBAlternative,PageC和PageD。每个页面都设置其Title属性以标识自身。
PageA有一个导航到PageB的按钮:
public class PageA : ContentPage
{
public PageA()
{
Button button = new Button
{
Text = "Go to Page B",
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Button)),
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center
};
button.Clicked += async (sender, args) =>
{
await Navigation.PushAsync(new PageB());
};
Title = "Page A";
Content = new button;
}
}
PageB类似,只是导航到PageC。 PageBA替代与PageB相同,只是它将自己标识为“Page B Alt”。 PageC有一个按钮可以导航到PageD,而PageD有两个按钮:
public class PageD : ContentPage
{
public PageD()
{
// Create Button to go directly to PageA.
Button homeButton = new Button
{
Text = "Go Directly to Home",
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Button)),
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
};
homeButton.Clicked += async (sender, args) =>
{
await Navigation.PopToRootAsync();
};
// Create Button to swap pages.
Button swapButton = new Button
{
Text = "Swap B and Alt B",
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Button)),
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
};
swapButton.Clicked += (sender, args) =>
{
IReadOnlyList<Page> navStack = Navigation.NavigationStack;
Page pageC = navStack[navStack.Count - 2];
Page existingPageB = navStack[navStack.Count - 3];
bool isOriginal = existingPageB is PageB;
Page newPageB = isOriginal ? (Page)new PageBAlternative() : new PageB();
// Swap the pages.
Navigation.RemovePage(existingPageB);
Navigation.InsertPageBefore(newPageB, pageC);
// Finished: Disable the Button.
swapButton.IsEnabled = false;
};
Title = "Page D";
Content = new StackLayout
{
Children =
{
homeButton,
swapButton
}
};
}
}
标记为Go Directly to Home的按钮具有一个调用PopToRootAsync的Clicked处理程序。 这会导致程序跳回到PageA并有效地清除所有中间页面的导航堆栈。
标记为Swap B和Alt B的按钮稍微复杂一些。 此按钮的Clicked处理程序将PageB替换为导航堆栈中的PageBAlternative(反之亦然),因此当您返回页面时,您将遇到不同的页面B.以下是Clicked处理程序的执行方式:
在单击Button时,NavigationStack有四个索引为0,1,2和3的项。这四个索引对应于PageA(PageB(或PageBA替代),PageC和PageD类型的堆栈中的对象。 处理程序访问NavigationStack以获取这些实际实例:
IReadOnlyList<Page> navStack = Navigation.NavigationStack;
Page pageC = navStack[navStack.Count - 2];
Page existingPageB = navStack[navStack.Count - 3];
现有的Page对象可能是Page或Page Alternative类型,因此创建另一个类型的newPageB对象:
bool isOriginal = existingPageB is PageB;
Page newPageB = isOriginal ? (Page)new PageBAlternative() : new PageB();
接下来的两个语句从导航堆栈中删除existingPageB对象,然后在pageC之前插入newPageB对象,有效地交换页面:
// Swap the pages.
Navigation.RemovePage(existingPageB);
Navigation.InsertPageBefore(newPageB, pageC);
显然,第一次单击此按钮时,existingPageB将是一个PageB对象,newPageB将是一个PageBA替代对象,但您可以返回到PageC或PageBA替代,然后再向前导航到PageD。 再次单击该按钮将使用PageB对象替换PageBAlternative对象。