向 .NET MAUI 应用添加可视控件

发布时间 2023-09-25 10:13:07作者: 韩梦芫

现在你已使用 .NET MAUI 模板创建了应用程序,下一步是添加用户界面并实现初始 UI 逻辑。

在本单元中,你将详细了解 .NET MAUI 应用程序的构建基块和导航结构。

.NET MAUI 项目中有何内容?

总的来说,.NET MAUI 项目最初包含:

  • MauiProgram.cs 文件,其中包含用于创建和配置应用程序对象的代码。

  • App.xaml 和 App.xaml.cs 文件,用于提供 UI 资源并为应用程序创建初始窗口。

  • AppShell.xaml 和 AppShell.xaml.cs 文件,用于指定应用程序的初始页面并处理导航路由的页面注册。

  • MainPage.xaml 和 MainPage.xaml.cs 文件,用于定义初始窗口中默认显示的页面的布局和 UI 逻辑。

可根据需要向应用添加更多页面,并可创建更多类来实现应用所需的业务逻辑。

.NET MAUI 项目还包含一组默认的应用程序资源(例如图像、图标和字体)以及每个平台的默认启动代码。

应用程序类

App 类将 .NET MAUI 应用程序作为一个整体来表示。 它从 Microsoft.Maui.Controls.Application 继承一组默认行为。 回顾上一单元,App 类是启动代码为每个平台实例化和加载的。 反过来,App 类构造函数通常会创建 AppShell 类的实例并将其分配给 MainPage 属性。 正是此代码控制用户通过 AppShell 中定义的内容看到的第一个屏幕。

App 类还包含以下内容:

  • 处理生命周期事件的方法,包括将应用发送到后台时(即当应用不再是前台应用时)。

  • 为应用程序创建新 Windows 的方法。 .NET MAUI 应用程序默认只有一个窗口,但你可以创建并启动更多窗口,这对于桌面应用程序和平板电脑应用程序很有帮助。

Shell

.NET Multi-platform App UI (.NET MAUI) Shell 通过提供大多数应用需要的基本功能来降低应用开发的复杂性,包括:

  • 用于描述应用的视觉层次结构的单个位置。
  • 常见的导航用户体验。
  • 支持导航到应用中任何页面的基于 URI 的导航方案。
  • 集成的搜索处理程序。

在 .NET MAUI Shell 应用中,应用的视觉层次结构是在 Shell 类的子类中描述的。 此类可以包含三个主要的层次结构对象:

  • FlyoutItem 或 TabBar。 FlyoutItem 表示浮出控件中的一个或多个项,应在应用的导航模式需要浮出控件时使用。 TabBar 表示底部选项卡栏,应在应用的导航模式以底部选项卡开始且不需要浮出控件时使用。
  • Tab,表示分组内容,可通过底部选项卡导航。
  • ShellContent,表示每个选项卡的 ContentPage 对象。

这些对象不表示任何用户界面,而是表示应用的视觉层次结构的组织。 Shell 会接收这些对象并为内容生成导航用户界面。

页是 Shell 中 .NET MAUI 中的 UI 层次结构的根。 目前你所见到的解决方案包括一个名为 MainPage 的类。 此类派生自 ContentPage,这是最简单和最常见的页类型。 内容页仅显示其内容。 .NET MAUI 还具有几种其他内置页类型,包括以下类型:

  • TabbedPage:这是用于选项卡导航的根页。 选项卡式页包含子页对象;每个选项卡各一个。

  • FlyoutPage:通过此页可实现大纲/细节样式呈现。 浮出控件页包含一系列项。 选择某一项时,将会出现显示该项详细信息的视图。

其他页类型也可用,主要用于在多屏幕应用中启用不同的导航模式。

视图

内容页通常显示一个视图。 视图支持以特定方式检索和显示数据。 内容页的默认视图是 ContentView,它按原样显示各项。 如果缩小视图,项可能会从界面中消失,直到重设视图大小。 ScrollView 支持使用滚动窗口显示项;如果你缩小窗口,可以上下滚动以显示各项。 CarouselView 是一个可滚动视图,使用户能够浏览项集合。 CollectionView 可以从命名数据源检索数据,并以模板为格式呈现每个项。 还有许多其他类型的视图可用。

控件和布局

视图可以包含单个控件,例如按钮、标签或文本框。 (严格来说,视图本身是一个控件,因此一个视图可以包含另一个视图)。但是,限制为单个控件的用户界面不太有用,因此控件被放置在布局中。 布局可定义这些控件相对于彼此显示的规则。 布局也是一个控件,因此可将其添加到视图中。 如果查看默认 MainPage.xaml 文件,你会看到此页面/视图/布局/控件层次结构正在运行。 在这段 XAML 代码中,VerticalStackLayout 元素只是另一个让你能够微调其他控件的布局的控件。

XML
<ContentPage ...>
    <ScrollView ...>
        <VerticalStackLayout>
            <Image ... />
            <Label ... />
            <Label ... />
            <Button ... />
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>

用于定义布局的一些常见控件包括:

  • VerticalStackLayout 和 HorizontalStackLayout,它们是经过优化的堆积布局,可在从上到下或从左到右的堆栈中布置控件。 StackLayout 也可用,它有一个名为 StackOrientation 的属性,可将其设置为 Horizontal 或 Vertical。 在平板电脑或手机上,在应用程序代码中修改此属性可在用户旋转设备时调整显示内容:

    堆积布局的水平和垂直方向将如何布置控件的图。

  • AbsoluteLayout,支持设置控件的确切坐标。

  • FlexLayout,它类似于 StackLayout,不同之处在于它允许包装其包含的不适合单行或单列的子控件。 此布局还提供了用于对齐和适应不同屏幕大小的选项。 例如,垂直排列时,FlexLayout 控件可以使其子控件左对齐、右对齐或居中对齐。 水平对齐时,可以使控件两端对齐以确保均匀的间距。 可使用 ScrollView 中的水平 FlexLayout 显示一系列可水平滚动的框架(每个框架本身可以是垂直排列的 FlexLayout):

    应用运行时的屏幕截图,其中 FlexLayout 显示在屏幕上。首先显示映像,然后依次是标题、文本标签和按钮。以上所有都在一个框内垂直显示。

  • Grid,根据我们设置的列和行位置来布置其控件。 你可以定义列和行的大小以及范围,因此网格布局不一定具有“棋盘外观”。

下图汇总了这些常见布局类型的关键特性:

.NET MAUI UI 中最常用的布局图。

堆积布局显示了垂直排列的四个框。 绝对布局显示了在屏幕上排列的四个框,完全按照开发人员指定的位置排列。 Flex 布局显示了在屏幕上布局的多个框,充分利用了屏幕区域。 网格布局显示了屏幕上以网格模式布局的多个框。

 

所有控件都有属性。 可使用 XAML 为这些属性设置初始值。 在许多情况下,可在应用程序的 C# 代码中修改这些属性。 例如,处理默认 .NET MAUI 应用中的“单击我”按钮的 Clicked 事件的代码如下所示:

int count = 0;
private void OnCounterClicked(object sender, EventArgs e)
{
    count+=5;

    if (count == 1)
        CounterBtn.Text = $"Clicked {count} time";
    else
        CounterBtn.Text = $"Clicked {count} times";

    SemanticScreenReader.Announce(CounterBtn.Text);
}

 

此代码修改页中 CounterBtn 控件的 Text 属性。 你甚至可以在代码中创建整个页面、视图和布局;无需使用 XAML。 例如,考虑页的以下 XAML 定义:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Phoneword.MainPage">

    <ScrollView>
        <VerticalStackLayout>
            <Label Text="Current count: 0"
                Grid.Row="0"
                FontSize="18"
                FontAttributes="Bold"
                x:Name="CounterLabel"
                HorizontalOptions="Center" />

            <Button Text="Click me"
                Grid.Row="1"
                Clicked="OnCounterClicked"
                HorizontalOptions="Center" />
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>

  

等效的 C# 代码如下所示:

public partial class TestPage : ContentPage
{
    int count = 0;
    
    // Named Label - declared as a member of the class
    Label counterLabel;

    public TestPage()
    {       
        var myScrollView = new ScrollView();

        var myStackLayout = new VerticalStackLayout();
        myScrollView.Content = myStackLayout;

        counterLabel = new Label
        {
            Text = "Current count: 0",
            FontSize = 18,
            FontAttributes = FontAttributes.Bold,
            HorizontalOptions = LayoutOptions.Center
        };
        myStackLayout.Children.Add(counterLabel);
        
        var myButton = new Button
        {
            Text = "Click me",
            HorizontalOptions = LayoutOptions.Center
        };
        myStackLayout.Children.Add(myButton);

        myButton.Clicked += OnCounterClicked;

        this.Content = myScrollView;
    }

    private void OnCounterClicked(object sender, EventArgs e)
    {
        count++;
        counterLabel.Text = $"Current count: {count}";

        SemanticScreenReader.Announce(counterLabel.Text);
    }
}

 

C# 代码更详细,但是更加灵活,让你能够动态地调整 UI。

要在应用程序开始运行时显示此页,请将 AppShell 中的 TestPage 类设置为主 ShellContent

<ShellContent
        Title="Home"
        ContentTemplate="{DataTemplate local:TestPage}"
        Route="TestPage" />

 

优化布局

在控件周围添加一点喘息空间非常有用。 每个控件都有一个布局遵循的 Margin 属性。 可以将边距视为控件之间的一种间隔。

所有布局还有 Padding 属性,可以防止其子级靠近布局的边框。 理解此概念的一种方式是,所有控件都在一个盒子中,而这个盒子的墙壁上有垫子。

另一个有用的空白设置是 VerticalStackLayout 或 HorizontalStackLayout 的 Spacing 属性。 这是布局的所有子级之间的空间。 这可与控件自身的边距相加,因此实际的空白将是边距加间距。