自定义 .NET MAUI XAML 页面中的布局

内容纲要

介绍

.NET MAUI 布局面板可帮助您在各种设备上为应用程序创建一致的用户界面。

假设您正在构建一个提示计算器应用程序,您计划在许多计算机和设备上部署该应用程序。每个设备可以具有不同的屏幕尺寸和像素密度。您的目标是使应用程序在所有设备上看起来尽可能相似。您希望避免手动计算每个屏幕尺寸的视图大小和位置。.NET MAUI 包括一个布局管理系统,可为您执行这些计算。您可以将视图放在布局面板中,这些布局面板会自动管理其子视图的大小和位置。这些面板使在不同设备上创建一致的用户界面变得更加容易。

在本模块中,您将构建一个在不同设备上看起来相似的 .NET MAUI 应用程序。首先,您将设置视图的首选大小和位置。然后,您将使用 StackLayout 垂直排列视图。接下来,使用 将视图放入行和列中。在模块结束时,您将拥有一个 .NET MAUI 应用程序,该应用程序在每种设备类型和屏幕尺寸上看起来都一致。Grid

学习目标

在本模块中,您将:

  • 排列应用中的用户界面元素并调整其大小
  • 使用在垂直或水平列表中显示视图StackLayout
  • 使用网格在行和列中显示视图

先决条件

  • 安装了 .NET MAUI 工作负载的 Visual Studio 2022
  • 熟悉 C# 和 .NET

指定视图的大小

设计跨多个设备一致的用户界面非常困难,因为设备的大小可能不同,像素密度也不同。考虑可用的不同设备:移动设备,平板电脑,台式机等。我们如何创建一个在每个界面上看起来相似的用户界面?

.NET MAUI 提供布局面板来帮助您构建一致的用户界面。布局面板负责调整其子视图的大小和位置。在本单元中,您将了解布局系统在 .NET MAUI 中的工作原理。您将了解默认情况下视图的大小,以及如何在运行时请求视图的特定大小和位置。

什么是布局面板?

布局面板是一个 .NET MAUI 容器,用于保存子视图的集合并确定其大小和位置。布局面板会在应用程序大小更改时自动重新计算;例如,当用户旋转设备时。

备注

术语“视图”“子视图”是指放置在布局面板上的控件。视图可以是标签、按钮、输入字段或 .NET MAUI 支持的任何其他类型的可视元素。

.NET MAUI 具有多个布局面板供您选择。每个面板以不同的方式管理其子视图。下图显示了一些最常见选项的概念性概述。

  • StackLayout:将其子视图排列在单个行或列中。除了 之外,还有一个新的优化和当你不需要改变方向。StackLayoutVerticalStackLayoutHorizontalStackLayout
  • AbsoluteLayout:使用 x 和 y 坐标排列其子视图。
  • FlexLayout:像 a 一样排列其子视图,但如果它们不适合单个行或列,则可以将它们换行。StackLayout
  • Grid:将其子视图排列在由行和列创建的单元格中。

备注

还有第五种类型的布局面板称为 a ,它使您能够指定如何相对于彼此排列子视图。您应该使用该控件,而不是因为它的性能更好。 包含在 .NET MAUI 中,以便与较旧的 Xamarin 应用向后兼容。RelativeLayoutFlexLayoutRelativeLayoutRelativeLayout

生成 .NET MAUI 页面的典型过程是创建布局面板,然后向其添加子视图。向布局添加视图时,可以影响其大小和位置。但是,该面板根据其内部布局算法拥有最终决定权。

在了解如何为视图请求特定大小之前,请先了解布局系统在默认情况下如何调整视图大小。

视图的默认大小

如果未指定视图的大小,它将自动增长到足够大以适合其内容。例如,请考虑以下 XAML:

<Label
    Text="Hello"
    BackgroundColor="Silver"
    VerticalOptions="Center"
    HorizontalOptions="Center" 
    FontSize="40"/>

本示例定义一个标签,用于在银色背景上显示单词。由于您没有指定标签的大小,因此它将自动调整大小,使其适合单词 。下图显示了在安卓设备上呈现的标签:HelloHello

您可以设置标签的背景色,以帮助您确定标签在运行时的大小。这是一个很好的调试技术,在生成 UI 时要记住。

指定视图的大小

生成 UI 时,通常需要控制视图的大小。例如,假设你正在构建一个登录页,并且你希望登录按钮正好是屏幕宽度的一半。如果对视图使用了默认大小调整,则按钮将仅为文本“登录”的大小。该大小不够大,因此您需要自己指定大小。

基类定义了影响视图大小的两个属性:和 。 用于指定宽度,并允许您指定高度。这两个属性的类型都是 。ViewWidthRequestHeightRequestWidthRequestHeightRequestdouble

下面的示例演示如何在 XAML 中指定标签的宽度和高度:

<Label
    Text="Hello"
    BackgroundColor="Silver"
    VerticalOptions="Center"
    HorizontalOptions="Center"
    WidthRequest="100"
    HeightRequest="300"
    FontSize="40"/>

结果如下所示:

备注

标签仍居中,但标签的文本不在标签的中心。

值得注意的一件事是这些属性的名称。这两个属性都包含单词请求。这个词意味着布局面板在运行时可能不会遵循它们。布局面板在其大小调整计算期间读取这些值,并尝试在可能的情况下容纳这些请求。如果没有足够的空间,布局面板可以忽略这些值。

尺寸单位

设置 和 时,可以使用文本值,如 。在 .NET MAUI 级别,这些值没有单位。它们不是点或像素。它们只是 类型的值。.NET MAUI 在运行时将这些值传递给基础操作系统。操作系统提供了确定数字含义所需的上下文:WidthRequestHeightRequest100double

  • 在 iOS 上,这些值称为
  • 在Android上,它们是与密度无关的像素

视图的呈现大小

由于由布局面板确定视图的大小,因此您无法在运行时使用和告诉您实际大小。例如,假设您为标签设置了 ,但面板没有足够的空间来满足请求。相反,该面板会为标签指定宽度。此时,如果检查属性的值,即使呈现的值为 。,它也会说。WidthRequestHeightRequestWidthRequest10080WidthRequest10080

为了解决此问题,基类定义了另外两个名为 和 的属性。这些属性是类型,表示视图的呈现宽度和高度。每当检索视图的大小时,请使用 和 属性。ViewWidthHeightdoubleWidthHeight

指定视图的位置

您还需要设置视图的位置。例如,回想一下,在登录页示例中,您希望将登录按钮的大小调整为屏幕宽度的一半。由于登录按钮不是屏幕的整个宽度,因此有一些空间可用于移动它。您可以将其放置在左侧,右侧或屏幕中心。

基类有两个属性,可用于设置视图的位置:和 。这些设置会影响视图在布局面板为其分配的矩形中的定位方式。您可以指定希望视图与矩形的四条边之一对齐,或者希望视图占据整个矩形。ViewVerticalOptionsHorizontalOptions

为 或 指定值比设置大小更具挑战性,因为它们的类型为 。VerticalOptionsHorizontalOptionsLayoutOptions

什么是布局选项类型?

LayoutOptions是封装了两个布局首选项的 C# 类型,以及 。这两个属性都与定位相关,但它们彼此不相关。以下是该类型的定义:AlignmentExpands

public struct LayoutOptions
{
    public LayoutAlignment Alignment { get; set; }
    public bool Expands { get; set; }
    ...
}

你会看这里,因为它是最常见和最直观的。Alignment

什么是布局对齐枚举?

LayoutAlignment是包含四个值的枚举:、、、和 。可以使用这些值来控制子视图在其布局面板提供给它的矩形中的定位方式。例如,请考虑以下代码和安卓屏幕截图:StartCenterEndFill

<StackLayout>
    <Label Text="Start" HorizontalOptions="Start" BackgroundColor="Silver" FontSize="40" />
    <Label Text="Center" HorizontalOptions="Center" BackgroundColor="Silver"  FontSize="40" />
    <Label Text="End" HorizontalOptions="End" BackgroundColor="Silver"  FontSize="40"/>
    <Label Text="Fill" HorizontalOptions="Fill" BackgroundColor="Silver"  FontSize="40"/>
</StackLayout>

该示例使用垂直视图,因此为每个子视图指定一行。 确定视图在其行中的位置。StackLayoutHorizontalOptions

什么是扩展?

结构的第二个属性是 。该属性允许 中的视图请求额外的空间(如果有)。您将在布局上的单元中更详细地浏览该属性。LayoutOptionsExpandsExpandsboolStackLayoutExpandsStackLayout

练习 - 探索对齐选项

在本练习中,您将使用 .NET MAUI 应用程序来查看四个主要布局选项在应用于 中包含的视图时的效果。您不会在练习中编写代码。相反,您将使用提供给您的解决方案并选择按钮来更改标签的布局选项。Grid

打开入门级解决方案

  1. 从 GitHub 克隆或下载锻炼存储库。 备注如果您计划从 Windows 在 Android 上运行和调试 .NET MAUI 应用程序,最好将练习内容克隆或下载到较短的文件夹路径(如 C:\dev)中,以避免生成生成的文件超过最大路径长度。
  2. 使用 Visual Studio 从 exercise1/Alignment 文件夹中打开初学者解决方案。

测试应用程序行为

运行应用以测试并查看不同的布局选项如何更改标签的大小和位置。LayoutOptions

您将看到更改水平和垂直的按钮。选择它们并观察发生的情况。下图显示了如果选择“居中”的“水平”和“垂直对齐”选项会发生什么情况:LayoutOptions

请注意,对齐选项(、、 和 ) 可以更改视图的大小和对齐方式。StartCenterEndFill

使用堆栈布局排列视图

在垂直或水平列表中堆叠视图是用户界面的常见设计。想想应用程序中的一些常见页面。示例包括登录、注册和设置页面。所有这些页面通常都包含堆叠内容。在本单元中,您将学习如何使用和新的优化和 排列垂直或水平列表中的视图。StackLayoutVerticalStackLayoutHorizontalStackLayout

什么是 StackLayout、VerticalStackLayout 和 HorizontalStackLayout?

StackLayout是一个布局容器,用于从左到右或从上到下组织其子视图。方向基于其属性,默认值为自上而下。下图显示了垂直的概念视图。OrientationStackLayout

  • StackLayout具有它从其基类继承的列表。该列表存储视图,这很好,因为在 .NET MAUI 中使用的大多数 UI 元素都派生自 。布局面板也派生自 ,这意味着您可以根据需要嵌套面板。ChildrenLayout<T>ViewView
  • VerticalStackLayout并且是当您知道方向不会更改时的首选布局,因为它们已针对性能进行了优化。HorizontalStackLayout

如何将视图添加到堆栈布局

在 .NET MAUI 中,可以向 C# 代码或 XAML 中的 视图添加视图。下面是使用代码添加的三个视图的示例:StackLayout

<StackLayout x:Name="stack">
</StackLayout>
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();

        var a = new BoxView { BackgroundColor = Colors.Silver, HeightRequest = 40 };
        var b = new BoxView { BackgroundColor = Colors.Blue, HeightRequest = 40 };
        var c = new BoxView { BackgroundColor = Colors.Gray, HeightRequest = 40 };

        stack.Children.Add(a);
        stack.Children.Add(b);
        stack.Children.Add(c);
    }
}

您可以将视图添加到集合中,并自动将视图定位在垂直列表中。以下是它在Android设备上的外观:ChildrenStackLayout

若要在 XAML 中执行相同的操作,请将子项嵌套在标记内。XAML 分析器会自动将嵌套视图添加到集合中,因为 是所有布局面板的。下面是将相同的三个视图添加到 XAML 中的示例:StackLayoutChildrenChildrenContentPropertyStackLayout

<StackLayout>
    <BoxView Color="Silver" />
    <BoxView Color="Blue" />
    <BoxView Color="Gray" />
</StackLayout>

如何在堆栈布局中对视图进行排序

集合中视图的顺序决定了它们在呈现时的布局顺序。对于在 XAML 中添加的视图,将使用文本顺序。对于在代码中添加的子级,布局顺序由调用方法的顺序确定。ChildrenAdd

如何更改堆栈布局中视图之间的间距

通常需要在 . 允许您使用属性控制每个子级之间的空间。默认值为零个单位,但您可以将其设置为对您来说合适的单位。下面是在 XAML 中将属性设置为的示例:StackLayoutStackLayoutSpacingSpacing30

<StackLayout Spacing="30">
    <BoxView Color="Silver" />
    <BoxView Color="Blue" />
    <BoxView Color="Gray" />
</StackLayout>

以下屏幕截图显示了 UI 在 Android 上的呈现方式:

如何设置堆栈布局的方向

StackLayout允许您在列或行中排列子项。您可以通过设置其属性来控制此行为。到目前为止,我们只显示了垂直 。OrientationStackLayout

Vertical是默认值。是否显式设置 to 取决于您。一些程序员更喜欢显式设置,以使代码更具自我记录性。OrientationVertical

下面是在 XAML 中设置 to 的示例:OrientationHorizontal

<StackLayout x:Name="stack" Orientation="Horizontal">
    <BoxView Color="Silver" WidthRequest="40"/>
    <BoxView Color="Blue" WidthRequest="40"/>
    <BoxView Color="Gray" WidthRequest="40"/>
</StackLayout>

备注

如上一练习中所述,方向的更改会导致它忽略每个 的属性。相反,您可以设置 .以下屏幕截图显示了 UI 如何在 Android 设备上呈现:StackPanelHeightRequestBoxViewWidthRequest

在堆栈布局中设置视图的布局选项

每个景色都有一个和财产。可以使用这些属性在布局面板提供的矩形显示区域中设置视图的位置。VerticalOptionsHorizontalOptions

如上所述,使用 ,属性的行为取决于 . 在与 该属性相反的方向上使用该属性。默认情况下,不会在与堆栈布局相同的方向上为堆栈布局中的元素分配任何额外空间。在此默认情况下,为该方向分配位置不会更改元素的呈现。但是,当位置与扩展相结合时,渲染会发生变化。StackLayoutLayoutOptionsOrientationStackLayoutStackLayoutLayoutOptionsOrientationOrientation

什么是扩张?

从前面的单元中回想一下,该结构包含一个名为 的属性。此属性专为子视图设计,并允许子视图在有任何可用空间时请求额外的空间。下面是该属性的工作原理示例:LayoutOptionsboolExpandsStackLayoutExpands

请注意,中提供了额外的可用空间。额外的空间将在需要额外空间的所有视图之间平均分配。StackLayout

如何申请额外空间

每个视图都有两个名为 和 的属性。到目前为止,您已经看到了这些属性的四个值:、 、 和 。如果要请求额外的空间,请将该值替换为以下值之一:、 、 或 。LayoutOptionsVerticalOptionsHorizontalOptionsStartCenterEndFillLayoutOptionsStartAndExpandCenterAndExpandEndAndExpandFillAndExpand

以下是这些值中每个值的工作原理:

橙色框是视图,灰色矩形表示属性为其提供的额外空间。仅当使用值时,视图才会填充多余的空间。使用其他值时,多余的空间将保持为空,但不能由 中的其他视图使用。ExpandsFillAndExpandStackLayout

优化的堆栈布局

如前所述,和 是具有预定义方向的优化控件。建议尽可能使用这些控件,以获得最佳的布局性能。这些布局具有常规布局的功能。VerticalStackLayoutHorizontalStackLayoutStackLayoutLayoutOptionsSpacingStackLayout

<VerticalStackLayout Spacing="30">
    <BoxView Color="Silver" />
    <BoxView Color="Blue" />
    <BoxView Color="Gray" />
</VerticalStackLayout>

<HorizontalStackLayout Spacing="30">
    <BoxView Color="Silver" />
    <BoxView Color="Blue" />
    <BoxView Color="Gray" />
</HorizontalStackLayout>

练习 - 使用 StackLayout 构建用户界面

在本练习中,你将使用嵌套容器在用户界面 (UI) 中排列视图。第一个屏幕截图显示初学者项目实现的布局,第二个屏幕截图显示已完成项目的布局。您的工作是使用容器并将初学者项目转换为已完成的版本。StackLayoutStackLayoutLayoutOptions

探索入门级解决方案

入门解决方案包含一个功能齐全的提示计算器应用程序。你将首先浏览 UI 以了解应用的功能。

使用 Visual Studio,在上一练习开始时克隆的存储库中的 exercise2/TipCalculator 文件夹中打开初学者解决方案。

在你的首选操作系统上构建并运行应用。

在文本框中输入一个数字,然后使用应用查看其工作原理。

尝试使用吸头量按钮和滑块。

完成后,关闭应用程序。

打开 MainPage.xaml。请注意,所有视图都放在一个中,如以下 XAML 标记所示:VerticalStackLayout

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:local="clr-namespace:TipCalculator"
         x:Class="TipCalculator.MainPage">
    <VerticalStackLayout>

        <Label Text="Bill" />
        <Entry x:Name="billInput" Placeholder="Enter Amount" Keyboard="Numeric" />

        <Label Text="Tip"   />
        <Label x:Name="tipOutput" Text="0.00" />

        <Label Text="Total" />
        <Label x:Name="totalOutput" Text="0.00" />

        <Label Text="Tip Percentage" />
        <Label x:Name="tipPercent" Text="15%" />
        <Slider x:Name="tipPercentSlider" Minimum="0" Maximum="100" Value="15" />

        <Button Text="15%" Clicked="OnNormalTip" />
        <Button Text="20%" Clicked="OnGenerousTip" />

        <Button x:Name="roundDown" Text="Round Down" />
        <Button x:Name="roundUp"   Text="Round Up" />

    </VerticalStackLayout>
</ContentPage>

修复用户界面

现在,你已看到应用已运行,可以通过添加其他容器来使其外观更好。目标是使应用看起来像实验室开始时的屏幕截图。HorizontalStackLayout

打开 MainPage.xaml 文件。

将填充单位和间距单位添加到 :4010VerticalStackLayout

<VerticalStackLayout Padding="40" Spacing="10">

添加 一个以分组,该分组显示比尔,其下方的字段。将该属性设置为 。HorizontalStackLayoutLabelEntrySpacing10

将 帐单的 设置为 ,将属性设置为 。这将确保标签与字段垂直对齐。WidthRequestLabel100VerticalOptionsCenterEntry

<HorizontalStackLayout Spacing="10">
    <Label Text="Bill" WidthRequest="100" VerticalOptions="Center"/>
    <Entry ... />
</HorizontalStackLayout>

添加另一个以将显示 Tip 的与命名的 tipOutput 分组。将该属性设置为 ,将该属性设置为 。HorizontalStackLayoutLabelLabelSpacing10Margin0,20,0,0

提示设置为WidthRequestLabel100

<HorizontalStackLayout Margin="0,20,0,0" Spacing="10">
    <Label Text="Tip" WidthRequest="100" />
    <Label .../>
</HorizontalStackLayout>

使用 a 将“总计”与命名的 totalOutput 分组。将该属性设置为 。HorizontalStackLayoutLabelLabelSpacing10

将 “总计” 设置为WidthRequestLabel100

<HorizontalStackLayout Spacing="10">
    <Label Text="Total" WidthRequest="100"  />
    <Label .../>
</HorizontalStackLayout>

添加另一个以将“提示百分比”与指定的 tipPercent 分组HorizontalStackLayoutLabelLabel

将此属性设置为 ,并将该属性设置为 :VerticalOptionsHorizontalStackLayoutEndAndExpandSpacing10

将 提示百分比设置为WidthRequestLabel100

<HorizontalStackLayout VerticalOptions="EndAndExpand" Spacing="10">
    <Label Text="Tip Percentage" WidthRequest="100"/>
    <Label ... />
</HorizontalStackLayout>

使用 a 将 标题为 15% 和标题为 20% 分组。HorizontalStackLayoutButtonButton

将此属性设置为 ,并将属性设置为 :MarginStackLayout0,20,0,0Spacing10

<HorizontalStackLayout  Margin="0,20,0,0" Spacing="10">
    <Button Text="15%" ... />
    <Button Text="20%" ... />
</HorizontalStackLayout>

添加一个 final,将 带有标题的 Round Down 和带有标题的 Round Up 分组。将此属性设置为 ,并将属性设置为 :HorizontalStackLayoutButtonButtonMarginStackLayout0,20,0,0Spacing10

<HorizontalStackLayout Margin="0,20,0,0" Spacing="10">
        <Button ... Text="Round Down" />
        <Button ... Text="Round Up" />
</HorizontalStackLayout>

在所有四个按钮控件上,将属性设置为 ,将属性设置为 。例如:HorizontalOptionsCenterAndExpandWidthRequest150

<Button Text="15%" WidthRequest="150" HorizontalOptions="CenterAndExpand" ... />

内容页的完整 XAML 标记应如下所示:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:TipCalculator"
             x:Class="TipCalculator.MainPage">

    <VerticalStackLayout Padding="40" Spacing="10">

        <HorizontalStackLayout Spacing="10">
            <Label Text="Bill" WidthRequest="100" VerticalOptions="Center" />
            <Entry x:Name="billInput" Placeholder="Enter Amount" Keyboard="Numeric" />
        </HorizontalStackLayout>

        <HorizontalStackLayout Margin="0,20,0,0" Spacing="10">
            <Label Text="Tip"  WidthRequest="100" />
            <Label x:Name="tipOutput" Text="0.00" />
        </HorizontalStackLayout>

        <HorizontalStackLayout Spacing="10">
            <Label Text="Total" WidthRequest="100"/>
            <Label x:Name="totalOutput" Text="0.00" />
        </HorizontalStackLayout>

        <HorizontalStackLayout VerticalOptions="EndAndExpand" Spacing="10">
            <Label Text="Tip Percentage" WidthRequest="100"/>
            <Label x:Name="tipPercent" Text="15%" />
        </HorizontalStackLayout>
        
        <Slider x:Name="tipPercentSlider" Minimum="0" Maximum="100" Value="15" />

        <HorizontalStackLayout Margin="0,20,0,0" Spacing="10">
            <Button Text="15%" Clicked="OnNormalTip" WidthRequest="150" HorizontalOptions="CenterAndExpand"/>
            <Button Text="20%" Clicked="OnGenerousTip"  WidthRequest="150" HorizontalOptions="CenterAndExpand"/>
        </HorizontalStackLayout>
        
        <HorizontalStackLayout Margin="0,20,0,0" Spacing="10">
            <Button x:Name="roundDown" Text="Round Down" WidthRequest="150" HorizontalOptions="CenterAndExpand"/>
            <Button x:Name="roundUp"   Text="Round Up" WidthRequest="150" HorizontalOptions="CenterAndExpand"/>
        </HorizontalStackLayout>

    </VerticalStackLayout>

</ContentPage>

检查结果

再次运行应用并查看 UI 中的差异。验证控件是否正确对齐,以及大小和间距是否正确。

你使用和容器来改善现有 UI 的美观性。这些布局是最简单的布局面板,但功能强大到足以生成合理的 UI。VerticalStakLayoutHorizontalStackLayout

使用网格排列视图

假设您正在构建一个在 7x5 网格中显示图像的页面。可以使用多个水平和垂直容器创建此页面。但是,由于多个布局面板的内存和处理要求,编码起来会很繁琐,并且可能导致性能问题。布局面板是同时需要行和列的 UI 的更好选择。在本单元中,您将学习如何定义 a 和在其单元格内放置视图。StackLayoutGridGrid

什么是网格?

Grid是由行和列组成的布局面板。下图显示了网格的概念视图。

将视图放置在从行和列的交集创建的单元格中。例如,如果创建具有三列和两行的 a,则将有六个单元格可用于视图。行和列可以有不同的大小,也可以将它们设置为自动适应放置在其中的子项的大小。子视图可以占用单个单元格或跨多个单元格。这种灵活性是许多应用的根布局面板的不错选择。GridGrid

如何指定网格的行和列

创建 时,可以单独定义每一行和每一列。该系统使您可以完全控制每行的高度和每列的宽度。每个都有一个定义网格形状的集合 和 对象。使用 和 的实例填充这些集合,每个实例表示 UI 中的一行或一列。GridGridRowDefinitionColumnDefinitionRowDefinitionColumnDefinition

下面是两个代码片段,它们显示了 和 的类定义:RowDefinitionColumnDefinition

public sealed class RowDefinition : ...
{
    ...
    public GridLength Height { get; set; }
}
public sealed class ColumnDefinition : ...
{
    ...
    public GridLength Width { get; set; }
}

请注意,具有一个称为 的属性,并具有一个名为 的属性。您可以使用这些属性来设置行的高度和列的宽度,如以下各节所述。RowDefinitionHeightColumnDefinitionWidth

什么是 GridLength?

和 属性的数据类型为 。此类型包含两个属性:和 。下面是一段代码,其中显示了类型定义的一部分。WidthHeightGridLengthGridUnitTypeValue

public struct GridLength
{
    ...
    public GridUnitType GridUnitType { get; }
    public double Value { get; }
}

您可以将该属性设置为以下值之一:GridUnitType

  • Absolute
  • Auto
  • Star

让我们仔细看看这些值中的每一个。

绝对网格单位类型

Absolute指定行或列的大小是固定的。您可以使用该属性来指示大小。下面的示例演示如何将行的高度设置为 C# 中设备单位的固定大小。请注意如何使用构造函数,它采用数值。此构造函数将自动设置为 为您。Value100GridLengthGridUnitTypeAbsolute

var row = new RowDefinition() { Height = new GridLength(100) };

在 XAML 中,只需提供一个数值。XAML 分析器将调用类型转换器来创建实例。下面是一个示例,它在 XAML 中显示了相同的内容:GridLength

<RowDefinition Height="100" />

自动网格单位类型

Auto将自动调整行或列的大小以适合您的子视图。将扫描该行或列中的所有子视图,选择最大的视图,然后将行或列设置为足够大以适合该子视图。在代码中创建行定义时,将忽略该数值。您可以使用任何值。下面的示例演示如何在 C# 中将行的高度设置为自动调整大小。请注意,我们任意选择该值。Grid1

var row = new RowDefinition() { Height = new GridLength(1, GridUnitType.Auto) };

在 XAML 中,使用值 。下面是一个示例,它在 XAML 中显示了相同的内容。Auto

<RowDefinition Height="Auto" />

星形网格单元类型

Star为您提供按比例调整大小。大小由每行或每列要求的总可用空间和比率确定。在谈话中,人们经常称之为“星星尺寸”而不是“比例尺寸”。

让我们演练一下对网格中的行使用比例大小的过程。

  1. 确定可用空间:扫描所有不使用星形大小调整的行。它将所有这些行的高度相加,并从自身的高度减去该总和。此计算提供可用于所有星形大小的行的空间量。GridGrid
  2. 划分可用空间:然后,根据每行的设置,在所有星形大小的行之间划分可用空间。将该属性视为一个乘数,用于确定定义为星形大小的所有行之间的比率。例如,如果我们有两个星号大小的行,都以乘数为乘数,则可用空间将在它们之间平均分配。但是,如果其中一个具有值,它将获得两倍于另一个的空间。GridValueValue12

下面的示例演示如何将行的高度设置为 C# 中:2 Star

var row = new RowDefinition() { Height = new GridLength(2, GridUnitType.Star) };

在 XAML 中,可以使用该符号来表示星形大小调整。将值和 组合在一个字符串中,类型转换器将为您创建。下面是 XAML 中的相同示例。**GridLength

<RowDefinition Height="2*" />

网格集合

使用 和 定义行和列后,可以将它们添加到 .您可以使用 和 的集合属性。填充这些集合最常在 XAML 中完成。RowDefinitionColumnDefinitionGridRowDefinitionsColumnDefinitionsGrid

此示例演示如何定义四行,并如何使用该属性将它们添加到 中:GridRowDefinitions

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="100" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="1*" />
        <RowDefinition Height="2*" />
    </Grid.RowDefinitions>
    ...
</Grid>

这可以缩短为:

<Grid RowDefinitions="100, Auto, 1*, 2*">
    ...
</Grid>

用于定义列的 XAML 与此 XAML 类似,只是您将使用并设置宽度。ColumnDefinitions

在运行时,此 XAML 将生成具有四行的 。第一行将具有固定高度的设备单位。第二行将具有该行中最高视图的高度。第三行和第四行使用星形大小调整,这意味着它们将占用剩余的可用空间,并根据其乘数按比例进行划分。因为第三行是,第四行是,第四行的高度是第三行的两倍。Grid100Value1*2*

行和列默认大小

行和列的默认值是大小。例如,查看以下 XAML。1*

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    ...
</Grid>

这可以缩短为:

<Grid RowDefinitions="*, *, *" ColumnDefinitions="*, *">
    ...
</Grid>

由于没有指定任何行或列的大小,因此将应用于所有行或列。在运行时,此配置将创建一个统一的,这意味着所有行的高度相同,所有列的宽度相同。1*Grid

如何向网格添加视图

向 添加视图时,可将其添加到特定单元格。单元格是在行和列相交的位置创建的。若要在单元格中定位视图,需要知道单元格的位置。您可以使用行号和列号的组合来标识单元格。Grid

行和列编号

行和列的编号从零开始。原点是左上角。下图显示了具有四行和两列的 的 编号。Grid

例如,如果我们想向右下角的单元格添加一个视图,我们会说视图的位置是 。row 3 column 1

使用附加属性向网格添加视图

当我们将视图添加到网格时,您需要一种方法来指定视图的行号和列号。一种解决方案是在基类上定义 和 属性,以便您可以直接在视图上指定位置。这种技术可以工作,但它不是最有效的方法。视图并不总是在 中,因此有时不需要这些属性。更好的方法是使用附加属性。RowColumnViewGrid

附加属性是在一个类中定义但在其他类型的对象上设置的属性。

将附加属性视为作为视图一部分的键值对的集合。向 添加视图时,请指定行和列。通过使用附加属性,可以添加一个键值对和一个指定行号的键值对。当 准备好定位视图时,它将检查集合以查看是否存在名为 .如果有,将使用该值来定位视图。GridGrid.RowGridGrid.RowGrid

此示例演示如何使用附加属性创建和添加视图:Grid

<Grid RowDefinitions="*, *, *" ColumnDefinitions="*, *">

    <BoxView Grid.Row="1" Grid.Column="0" BackgroundColor="Navy" />
    
</Grid>

在此示例中,是添加到 的内部集合中的键值对。将使用这些值来确定视图的位置。下面是在设备上运行应用程序时的外观。Grid.Row=1Grid.Column=0BoxViewGridGrid

如何使视图跨越多行或多列

您还应该注意另外两个附加属性:和 。这些属性指定视图应占用的行数或列数。例如,查看以下 XAML。Grid.RowSpanGrid.ColumnSpan

<Grid RowDefinitions="*, *, *" ColumnDefinitions="*, *">

    <BoxView Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" BackgroundColor="Navy" />
    
</Grid>

请注意,此示例将 设置为 。此视图将占用从 开始的两列。下面是在设备上运行应用程序时的外观。ColumnSpan2Column 0Grid

练习 - 使用网格构建用户界面

在本练习中,您将使用 在用户界面 (UI) 中排列视图。你将从 TipCalculator 项目的另一个版本开始,并对其进行调整以使 UI 更加直观。您还将按钮移动到页面底部。这次您将使用布局,而不是使用 和 。下图显示了初始 UI,以及执行本练习中的步骤所生成的 UI:GridGridVerticalStackLayoutHorizontalStackLayout

打开入门级解决方案

入门解决方案包含一个功能齐全的提示计算器应用程序。

使用 Visual Studio,在上一练习开始时克隆的存储库中的 exercise3/TipCalculator 文件夹中打开初学者解决方案。

打开 MainPage.xaml。请注意,所有视图都使用一个垂直面板显示:StackLayout

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:TipCalculator"
             x:Class="TipCalculator.MainPage">

    <VerticalStackLayout>

        <Label Text="Bill" />
        <Entry x:Name="billInput" Placeholder="Enter Amount" Keyboard="Numeric" />

        <Label Text="Tip"   />
        <Label x:Name="tipOutput" Text="0.00" />

        <Label Text="Total" />
        <Label x:Name="totalOutput" Text="0.00" />

        <Label Text="Tip Percentage" />
        <Label x:Name="tipPercent" Text="15%" />
        <Slider x:Name="tipPercentSlider" Minimum="0" Maximum="100" Value="15" />

        <Button Text="15%" Clicked="OnNormalTip" />
        <Button Text="20%" Clicked="OnGenerousTip" />

        <Button x:Name="roundDown" Text="Round Down" />
        <Button x:Name="roundUp"   Text="Round Up" />

    </VerticalStackLayout>

</ContentPage>

创建网格布局

将布局面板从 更改为 带有单位间距。VerticalStackLayoutGrid40

为 定义七行和两列。使除第四行以外的所有行都达到大小。应使用第四行,以便它将获取网格中所有剩余的可用空间。对两列都使用大小调整。GridAutoStarStar

<Grid RowDefinitions="Auto, Auto, Auto, *, Auto, Auto, Auto"
      ColumnDefinitions="*, *"
      Padding="40">
    ...
</Grid>

在单元格中定位视图

为每个视图添加设置,并将其指定给 中的相应单元格。使用以下屏幕截图可帮助您确定每个视图的放置位置:Grid.RowGrid.ColumnGrid

下面的示例显示了如何设置帐单的位置和视图:LabelbillInputEntry

...
<Label Text="Bill" Grid.Row="0" Grid.Column="0"/>
<Entry x:Name="billInput" Placeholder="Enter Amount" Keyboard="Numeric" Grid.Row="0" Grid.Column="1"/>
...

对齐帐单,并将属性设置为标签上。LabelEntryVerticalOptionsCenter

将 的设置添加到 中,使其跨越两列:Grid.ColumnSpanSlider

<Slider ... Grid.ColumnSpan="2" ... />

找到带文本的提示百分比。设置它,使其占据其矩形中的左下角位置:Label

<Label Text="Tip Percentage" VerticalOptions="End" HorizontalOptions="Start" ... />

找到命名的 tipPercent。设置它,使其占据其矩形中的右下角位置:Label

<Label x:Name="tipPercent" VerticalOptions="End" HorizontalOptions="End" ... />

将所有四个按钮的属性设置为 。Margin5

页面的完整 XAML 标记应如下所示:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:TipCalculator"
             x:Class="TipCalculator.MainPage">
    <Grid RowDefinitions="Auto, Auto, Auto, *, Auto, Auto, Auto"
          ColumnDefinitions="*, *"
          Padding="40">

        <Label Text="Bill" VerticalOptions="Center" Grid.Row="0" Grid.Column="0"/>
        <Entry x:Name="billInput" Placeholder="Enter Amount" Keyboard="Numeric" Grid.Row="0" Grid.Column="1"/>

        <Label Text="Tip" Grid.Row="1" Grid.Column="0"/>
        <Label x:Name="tipOutput" Text="0.00" Grid.Row="1" Grid.Column="1"/>

        <Label Text="Total" Grid.Row="2" Grid.Column="0"/>
        <Label x:Name="totalOutput" Text="0.00" Grid.Row="2" Grid.Column="1"/>

        <Label Text="Tip Percentage" VerticalOptions="End" HorizontalOptions="Start" Grid.Row="3" Grid.Column="0"/>
        <Label x:Name="tipPercent" Text="15%" VerticalOptions="End" HorizontalOptions="End" Grid.Row="3" Grid.Column="1"/>
        <Slider x:Name="tipPercentSlider" Minimum="0" Maximum="100" Value="15" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2"/>

        <Button Text="15%" Clicked="OnNormalTip" Margin="5" Grid.Row="5" Grid.Column="0"/>
        <Button Text="20%" Clicked="OnGenerousTip" Margin="5" Grid.Row="5" Grid.Column="1"/>

        <Button x:Name="roundDown" Margin="5" Text="Round Down" Grid.Row="6" Grid.Column="0"/>
        <Button x:Name="roundUp"   Margin="5" Text="Round Up" Grid.Row="6" Grid.Column="1"/>

    </Grid>
</ContentPage>

检查结果

运行应用程序并查看 UI 中的差异。你使用了 来改善现有 UI 的美观性。 比 更强大。特别是,使跨行对齐视图变得更加容易。GridGridStackLayoutGrid

总结

很难设计出在各种设备上看起来相同的用户界面。每个设备可能具有不同的外形规格、大小和像素密度。你需要编写更多代码,以确保你的应用在所有设备上看起来都很棒。

.NET MAUI 提供了布局面板来帮助解决此问题。使用 、 、 和 , 可以广泛控制视图的排列。使用你学到的布局策略,你可以开发适应用户所有设备的应用。StackLayoutVerticalStackLayoutHorizontalStackLayoutGrid

给TA打赏
共{{data.count}}人
人已打赏
.NET MAUI

使用共享资源设计一致的 .NET MAUI XAML 页面

2022-6-29 18:17:32

.NET MAUI

使用 XAML 在 .NET MAUI 应用中创建 UI

2022-7-1 11:00:43

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新私信 私信列表
搜索