ContentPage

.NET Multi-platform App UI (.NET MAUI) ContentPage 显示单个视图,该视图通常是布局(如 Grid 或 StackLayout),并且是最常见的页面类型。

ContentPage 定义以下属性:

  • Content 属性,类型为 View,用于定义表示页面内容的视图。
  • HideSoftInputOnTapped,类型为 bool,指示是否点击页面上的任意位置会导致软输入键盘在可见时隐藏。

这些属性由 BindableProperty 对象提供支持,这意味着它们可以作为数据绑定的目标,并进行样式设置。

此外,ContentPage 从 Page 类继承 TitleIconImageSourceBackgroundImageSourceIsBusy 和 Padding 可绑定属性。

 备注

由于 Content 属性是 ContentPage 类的内容属性,因此不需要通过 XAML 显式设置。

.NET MAUI 应用通常包含派生自 ContentPage 的多个页面,并且可以在这些页面之间执行导航。 要详细了解页面导航,请参阅 NavigationPage

可以使用控件模板对 ContentPage 进行模板化。 有关详细信息,请参阅控件模板

创建 ContentPage

要将 ContentPage 添加到 .NET MAUI 应用,请执行以下操作:

  1. 在“解决方案资源管理器”中,右键单击项目或项目中的文件夹,然后选择“新项...”。

  2. 在“添加新项”对话框中,展开“已安装的 > C# 项”,选择“.NET MAUI”,然后选择“.NET MAUI ContentPage (XAML)”项模板,输入合适的页名称,然后单击“添加”按钮:

然后,Visual Studio 将新建 ContentPage 派生页,该页类似于以下示例:

XAML复制

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyMauiApp.MyPage"
             Title="MyPage"
             BackgroundColor="White">
    <StackLayout>
        <Label Text="Welcome to .NET MAUI!"
                VerticalOptions="Center"
                HorizontalOptions="Center" />
        <!-- Other views go here -->
    </StackLayout>
</ContentPage>

ContentPage 的子级通常是一个布局,例如 Grid 或 StackLayout,布局通常包含多个视图。 但是,ContentPage 的子级可以是显示集合的视图,例如 CollectionView

FlyoutPage

.NET Multi-platform App UI (.NET MAUI) FlyoutPage 是一个管理两个相关信息页面的页面 – 一个浮出控件页面和一个详细信息页面,前者显示项,而后者显示有关浮出控件页面上的项的详细信息。

FlyoutPage 有两种布局行为:

  • 在弹出框布局中,详细信息页面会覆盖或部分覆盖浮出控件页面。 选择浮出控件页面上的项时,将会导航到相应的详细信息页面。 在手机上运行的应用始终使用此布局行为。
  • 在拆分布局中,浮出控件页面显示在左侧,详细信息页面显示在右侧。 在平板电脑或桌面设备上运行的应用可以使用此布局行为,Windows 将默认使用它。

有关布局行为的详细信息,请参阅布局行为

FlyoutPage 定义以下属性:

  • Detail,类型为 Page,用于定义针对浮出控件页面中的选定项显示的详细信息页面。
  • Flyout,类型为 Page,用于定义浮出控件页面。
  • FlyoutLayoutBehavior,类型为 FlyoutLayoutBehavior,指示浮出控件面和详细信息页面的布局行为。
  • IsGestureEnabled,类型为 bool,确定轻扫手势是否在浮出控件页面和详细信息页面之间切换。 此属性的默认值为 true
  • IsPresented,类型为 bool,确定是否显示浮出控件页面或详细信息页面。 此属性的默认值是 false,它显示详细信息页面。 应将其设置为 true 以显示浮出控件页面。

IsGestureEnabledIsPresented 和 FlyoutLayoutBehavior 属性由 BindableProperty 对象提供支持,这意味着可以将它们用作数据绑定的目标,并能对它们进行样式设置。

FlyoutPage 还定义了 IsPresentedChanged 事件,在 IsPresented 属性更改值时会引发该事件。

 警告

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

创建 FlyoutPage

若要创建浮出控件页,需创建 FlyoutPage 对象并设置其 Flyout 和 Detail 属性。 应将 Flyout 属性设置为 ContentPage 对象,将 Detail 属性设置为 TabbedPageNavigationPage 或 ContentPage 对象。 这将有助于确保在所有平台上都有一致的用户体验。

 重要

FlyoutPage 被设计为应用的根页面,将其用作其他页面类型中的子页面可能会导致意外和不一致的行为。

下面的示例展示了设置 Flyout 和 Detail 属性的 FlyoutPage

XAML复制

<FlyoutPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            xmlns:local="clr-namespace:FlyoutPageNavigation"
            x:Class="FlyoutPageNavigation.MainPage">
    <FlyoutPage.Flyout>
        <local:FlyoutMenuPage x:Name="flyoutPage" />
    </FlyoutPage.Flyout>
    <FlyoutPage.Detail>
        <NavigationPage>
            <x:Arguments>
                <local:ContactsPage />
            </x:Arguments>
        </NavigationPage>
    </FlyoutPage.Detail>
</FlyoutPage>

在此示例中,Flyout 属性被设置为 ContentPage 对象,而 Detail 属性设置被为包含 ContentPage 对象的 NavigationPage

以下示例展示了 FlyoutMenuPage 对象的定义,该对象的类型为 ContentPage

XAML复制

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:FlyoutPageNavigation"
             x:Class="FlyoutPageNavigation.FlyoutMenuPage"
             Padding="0,40,0,0"
             IconImageSource="hamburger.png"
             Title="Personal Organiser">
    <CollectionView x:Name="collectionView"
                    x:FieldModifier="public"
                    SelectionMode="Single">
        <CollectionView.ItemsSource>
            <x:Array Type="{x:Type local:FlyoutPageItem}">
                <local:FlyoutPageItem Title="Contacts"
                                      IconSource="contacts.png"
                                      TargetType="{x:Type local:ContactsPage}" />
                <local:FlyoutPageItem Title="TodoList"
                                      IconSource="todo.png"
                                      TargetType="{x:Type local:TodoListPage}" />
                <local:FlyoutPageItem Title="Reminders"
                                      IconSource="reminders.png"
                                      TargetType="{x:Type local:ReminderPage}" />
            </x:Array>
        </CollectionView.ItemsSource>
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <Grid Padding="5,10">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="30"/>
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <Image Source="{Binding IconSource}" />
                    <Label Grid.Column="1"
                           Margin="20,0"
                           Text="{Binding Title}"
                           FontSize="20"
                           FontAttributes="Bold"
                           VerticalOptions="Center" />
                </Grid>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

在此示例中,浮出控件页面由 CollectionView 组成,通过将其 ItemsSource 属性设置为 FlyoutPageItem 对象的数组来填充数据。 以下示例展示了 FlyoutPageItem 类的定义:

C#复制

public class FlyoutPageItem
{
    public string Title { get; set; }
    public string IconSource { get; set; }
    public Type TargetType { get; set; }
}

DataTemplate 指定为 CollectionView.ItemTemplate 属性以显示每个 FlyoutPageItem。 DataTemplate 包含由 Image 和 Label 组成的 Grid。 Image 显示 IconSource 属性值,Label 显示每个 FlyoutPageItem 的 Title 属性值。 此外,还设置了浮出控件页面的 Title 和 IconImageSource 属性。 如果详细信息页有标题栏,则图标将显示在详细信息页上。

 备注

Flyout 页必须要有其 Title 属性集,否则会出现异常。

以下屏幕截图展示了生成的浮出控件:

创建并显示详细信息页

FlyoutMenuPage 对象包含从 MainPage 类引用的 CollectionView。 这允许 MainPage 类为 SelectionChanged 事件注册处理程序。 这使 MainPage 对象能够将 Detail 属性设置为表示所选 CollectionView 项的页面。 以下示例展示了事件处理程序:

C#复制

public partial class MainPage : FlyoutPage
{
    public MainPage()
    {
        ...
        flyoutPage.collectionView.SelectionChanged += OnSelectionChanged;
    }

    void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var item = e.CurrentSelection.FirstOrDefault() as FlyoutPageItem;
        if (item != null)
        {            
            Detail = new NavigationPage((Page)Activator.CreateInstance(item.TargetType));
            if (!((IFlyoutPageController)this).ShouldShowSplitMode)
                IsPresented = false;
        }
    }
}

在此示例中,OnSelectionChanged 事件处理程序从 CollectionView 对象中检索 CurrentSelection,并将详细信息页面设置为存储在 FlyoutPageItem 的 TargetType 属性中的页面类型的实例。 如果 FlyoutPage 未使用拆分布局,通过将 FlyoutPage.IsPresented 属性设置为 false 即可显示详细信息页面。 如果 FlyoutPage 使用拆分布局,则将同时显示浮出控件页面和详细信息页面,因此无需设置 FlyoutPage.IsPresented 属性。

布局行为

FlyoutPage 显示浮出控件页面和详细信息页面的方式取决于运行应用的设备的外形规格、设备的方向以及 FlyoutLayoutBehavior 属性的值。 此属性应设置为定义以下成员的 FlyoutLayoutBehavior 枚举的值:

  • Default – 使用平台默认值显示页面。
  • Popover – 详细信息页面覆盖或部分覆盖浮出控件页面。
  • Split – 浮出控件页面显示在左侧,详细信息页面显示在右侧。
  • SplitOnLandscape – 设备处于横向时使用分屏。
  • SplitOnPortrait – 设备处于纵向时使用分屏。

以下示例展示了如何在 FlyoutPage 上设置 FlyoutLayoutBehavior 属性:

<FlyoutPage ...
            FlyoutLayoutBehavior="Split">
  ...
</FlyoutPage>

   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 对象的堆栈中导航。 当一个页面导航到另一个页面时,新页面将推送到堆栈上,并成为活动页面:

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

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

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

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

 提示

建议通过 ContentPage 对象填充 NavigationPage

创建根页

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

C#复制

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

 备注

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

将页面推送到导航堆栈

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

C#复制

await Navigation.PushAsync(new DetailsPage());

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

 备注

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

从导航堆栈中弹出页面

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

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

C#复制

await Navigation.PopAsync();

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

 备注

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

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

操作导航堆栈

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

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

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

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

执行模式导航

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

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

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

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

 备注

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

将页面推送到模式堆栈

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

C#复制

await Navigation.PushModalAsync(new DetailsPage());

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

 备注

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

从模式堆栈中弹出页面

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

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

C#复制

await Navigation.PopModalAsync();

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

 备注

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

禁用“后退”按钮

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

在导航期间传递数据

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

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

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

C#复制

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 设置为该数据:

C#复制

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

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

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

XAML复制

<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 附加属性:

XAML复制

<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# 代码如下:

C#复制

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

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

 重要

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

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

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

 提示

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

更多MAUI知识请参考NavigationPage - .NET MAUI | Microsoft Learn

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐