NavigationPage

.NET MAUI NavigationPage.

.NET Multi-platform App UI (.NET MAUI) NavigationPage 提供分层导航体验,用户可以随心所欲地向前或向后导航页面。 NavigationPage 提供导航作为 Page 对象的后进先出 (LIFO) 堆栈。

NavigationPage 定义以下属性:

  • BarBackground,类型为 Brush,将导航栏的背景指定为 Brush
  • BarBackgroundColor,类型为 Color,指定导航栏的背景色。
  • BackButtonTitle,类型为 string,表示用于后退按钮的文本。 这是附加属性。
  • BarTextColor,类型为 Color,指定导航栏上文本的颜色。
  • CurrentPage,类型为 Page,表示导航堆栈顶部的页面。 这是只读属性。
  • HasNavigationBar,类型为 bool,表示导航栏是否位于 NavigationPage。 此属性的默认值为 true。 这是附加属性。
  • HasBackButton,类型为 bool,表示导航栏是否包含后退按钮。 此属性的默认值为 true。 这是附加属性。
  • IconColor,类型为 Color,定义导航栏中图标的背景色。 这是附加属性。
  • RootPage,类型为 Page,表示导航堆栈的根页。 这是只读属性。
  • TitleIconImageSource,类型为 ImageSource,定义表示导航栏上标题的图标。 这是附加属性。
  • TitleView,类型为 View,定义可在导航栏中显示的视图。 这是附加属性。

这些属性由 BindableProperty 对象提供支持;也就是说,它们可以作为数据绑定的目标,并能进行样式设置。

NavigationPage 类还定义三个事件:

  • 在将页面推送到导航堆栈时,将引发 Pushed
  • 当从导航堆栈弹出页面时,将引发 Popped
  • 从导航堆栈弹出最后一个非根页面时,将引发 PoppedToRoot

所有三个事件都接收 NavigationEventArgs 对象,这些对象定义只读的 Page 属性,该属性检索从导航堆栈中弹出的页面或堆栈上新显示的页面。

警告

NavigationPage 与 .NET MAUI Shell 应用不兼容,如果尝试在 Shell 应用中使用 NavigationPage,将引发异常。 有关 Shell 应用的详细信息,请参阅 Shell

执行无模式导航

.NET MAUI 支持无模式页面导航。 无模式页面会停留在屏幕上,并且在导航到另一个页面之前一直可用。

NavigationPage 通常用于在 ContentPage 对象的堆栈中导航。 当一个页面导航到另一个页面时,新页面将推送到堆栈上,并成为活动页面:

Pushing a page to the navigation stack.

当第二页返回到第一页时,会从堆栈弹出一个页面,然后最顶层的新页面变为活动状态:

Popping a page from the navigation stack.

NavigationPage 包含一个导航栏,活动页面显示在导航栏下方。 下图显示了导航栏的主要组件:

NavigationPage components.

后退按钮和标题之间会显示一个可选图标。

可以由任何 Page 派生类型上的 Navigation 属性公开导航方法。 通过这些方法,可以将页面推送到导航堆栈、从堆栈弹出页面以及操作堆栈。

提示

建议通过 ContentPage 对象填充 NavigationPage

创建根页

围绕多个页面构建的应用始终有根页,这是添加到导航堆栈的第一个页面。 可通过创建 NavigationPage 对象来达成此目的,该对象的构造函数参数是应用的根页,并将生成的对象设置为 App.MainPage 属性的值:

public partial class App : Application
{
    public App()
    {
        InitializeComponent();
        MainPage = new NavigationPage(new MainPage());
    }
}

注意

NavigationPageRootPage 属性提供对导航堆栈中第一页的访问权限。

将页面推送到导航堆栈

可以通过对当前页面的 Navigation 属性调用 PushAsync 方法导航到页面:

await Navigation.PushAsync(new DetailsPage());

在此示例中,DetailsPage 对象被推送到导航堆栈,在此它将成为活动页。

注意

PushAsync 方法有包括 bool 参数的重写函数,该参数指定是否在导航期间显示页面切换。 缺少 bool 参数的 PushAsync 方法默认启用页面切换。

从导航堆栈中弹出页面

通过按下设备上的返回按钮(无论是设备上的物理按钮还是屏幕按钮),即可从导航堆栈中弹出活动页。

要以编程方式返回到上一页,应对当前页的 Navigation 属性调用 PopAsync 方法:

await Navigation.PopAsync();

在此示例中,当前页将从导航堆栈中删除,最顶部的新页面将成为活动页。

注意

PopAsync 方法有包含 bool 参数的重写函数,该参数指定是否在导航期间显示页面切换。 缺少 bool 参数的 PopAsync 方法默认启用页面切换。

此外,每个页面的 Navigation 属性还公开 PopToRootAsync 方法,该方法从导航堆栈中弹出除根页之外的所有页面,从而使应用的根页成为活动页。

操作导航堆栈

PageNavigation 属性公开 NavigationStack 属性,从中可以获取导航堆栈中的页面。 虽然 .NET MAUI 保持对导航堆栈的访问,但 Navigation 属性提供 InsertPageBeforeRemovePage 方法,用于通过插入或删除页面来操作堆栈。

InsertPageBefore 方法将导航堆栈中的指定页插入到现有指定页之前,如下图所示:

Inserting a page in the navigation stack.

RemovePage 方法可从导航堆栈中删除指定页面,如下图所示:

Removing a page from the navigation stack.

这些方法共同支持自定义导航体验,例如在成功登录后将登录页替换为新页面。

执行模式导航

.NET MAUI 支持模式页面导航。 模式页面鼓励用户完成独立任务,在完成或取消该任务之前,不允许导航离开该任务。

模式页面可以是 .NET MAUI 支持的任何页面类型。 要以模式方式显示页面,应用应将其推送到模式堆栈,在此它将成为活动页:

Pushing a page to the modal stack.

要返回上一页,应用应从模式堆栈中弹出当前页,从而使最顶部的新页面成为活动页:

Popping a page from the modal stack.

可以由任何 Page 派生类型上的 Navigation 属性公开模式导航方法。 这些方法提供将页面推送到模式堆栈以及从模式堆栈弹出页面的功能。 Navigation 属性还公开 ModalStack 属性,从中可以获取模式堆栈中的页面。 但是,在模式导航中没有执行模式堆栈操作或弹出到根页的概念。 这是因为基础平台普遍都不支持这些操作。

注意

无需 NavigationPage 对象即可执行模式页面导航。

将页面推送到模式堆栈

可以通过对当前页的 Navigation 属性调用 PushModalAsync 方法以模式方式导航到页面:

await Navigation.PushModalAsync(new DetailsPage());

在此示例中,DetailsPage 对象被推送到模式堆栈,在此它将成为活动页。

注意

PushModalAsync 方法有包括 bool 参数的重写函数,该参数指定是否在导航过程中显示页面切换。 缺少 bool 参数的 PushModalAsync 方法默认启用页面切换。

从模式堆栈中弹出页面

通过按下设备上的返回按钮(无论是设备上的物理按钮还是屏幕按钮),即可从模式堆栈中弹出活动页。

要以编程方式返回到原始页,应对当前页的 Navigation 属性调用 PopModalAsync 方法:

await Navigation.PopModalAsync();

在此示例中,当前页将从模式堆栈中删除,最顶部的页面将成为活动页。

注意

PopModalAsync 方法有包括 bool 参数的重写函数,该参数指定是否在导航期间显示页面切换。 缺少 bool 参数的 PopModalAsync 方法默认启用页面切换。

禁用“后退”按钮

在 Android 上,只要按设备上的标准“后退”按钮,用户就可返回到上一页。 如果模式页面要求用户在离开页面之前完成独立任务,则应用必须禁用“后退”按钮。 可通过替代模式页面上的 Page.OnBackButtonPressed 方法来完成此操作。

在导航期间传递数据

有时,页面必须在导航期间将数据传递到另一个页面。 实现此操作的两种标准方法是:通过页面构造函数传递数据,或者将新页面的 BindingContext 设置为该数据。

通过页面构造函数传递数据

在导航期间,通过页面构造函数参数将数据传递到另一个页面是最简单的方法:

Contact contact = new Contact
{
    Name = "Jane Doe",
    Age = 30,
    Occupation = "Developer",
    Country = "USA"
};
...
await Navigation.PushModalAsync(new DetailsPage(contact));

在此示例中,将 Contact 对象作为构造函数参数传递给 DetailPage。 然后,可以由 DetailsPage 显示 Contact 对象。

通过 BindingContext 传递数据

在导航期间将数据传递到另一个页面的另一种方法是将新页面的 BindingContext 设置为该数据:

Contact contact = new Contact
{
    Name = "Jane Doe",
    Age = 30,
    Occupation = "Developer",
    Country = "USA"
};

await Navigation.PushAsync(new DetailsPage
{
    BindingContext = contact  
});

通过页面的 BindingContext 传递导航数据的优点是,新页面可以使用数据绑定来显示数据:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyMauiApp.DetailsPage"
             Title="Details">
    <StackLayout>
        <Label Text="{Binding Name}" />
        <Label Text="{Binding Occupation}" />
    </StackLayout>
</ContentPage>

有关数据绑定的详细信息,请参阅数据绑定

在导航栏中显示视图

任何 .NET MAUI View 都可以显示在 NavigationPage 的导航栏中。 这是通过将 NavigationPage.TitleView 附加属性设置为 View 来实现的。 此附加属性可以在任何 Page 上设置,当 Page 被推送到 NavigationPage 上后,NavigationPage 会遵守属性的值。

下面的示例展示了如何设置 NavigationPage.TitleView 附加属性:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="NavigationPageTitleView.TitleViewPage">
    <NavigationPage.TitleView>
        <Slider HeightRequest="44"
                WidthRequest="300" />
    </NavigationPage.TitleView>
    ...
</ContentPage>

等效 C# 代码如下:

Slider titleView = new Slider { HeightRequest = 44, WidthRequest = 300 };
NavigationPage.SetTitleView(this, titleView);

在此示例中,NavigationPage 的导航栏中会显示一个 Slider 以控制缩放。

重要

很多视图不会出现在导航栏中,除非使用 WidthRequestHeightRequest 属性指定视图的大小。

因为 Layout 类派生自 View 类,所以可以设置 TitleView 附加属性来显示包含多个视图的布局类。 但是,如果导航栏中显示的视图大于导航栏的默认大小,则可能会出现截断问题。 但是,在 Android 上,可以通过将 NavigationPage.BarHeight 可绑定属性设置为表示新高度的 double 来更改导航条的高度。

或者,可以通过在导航栏中放置一些内容,以及在与导航栏颜色匹配的页面内容顶部的视图中放置一些内容,来建议扩展导航条。 此外,在 iOS 中,导航栏底部的分隔线和阴影可以通过将 NavigationPage.HideNavigationBarSeparator 可绑定属性设置为 true 来移除。

提示

BackButtonTitleTitleTitleIconImageSourceTitleView 属性都可以定义占用导航栏空间的值。 虽然导航栏大小因平台和屏幕大小而异,但由于可用空间有限,设置所有这些属性将导致冲突。 你可能会发现,与其尝试使用这些属性的组合,不如仅通过设置 TitleView 属性来更好地实现所需的导航栏设计。