Xamarin.Forms 中的布局行为更改

你可能会注意到,在运行升级后的 .NET Multi-platform App UI (.NET MAUI) 应用时,布局行为有所不同。 其中一些是更改布局间距值的结果。 有关详细信息,请参阅 Xamarin.Forms 中的默认值更改

下表显示了 Xamarin.Forms 和.NET MAUI 中的布局之间的其他行为更改:

布局 Xamarin.Forms .NET MAUI 建议
全部 在某些情况下,不遵循调整大小请求。 遵循尺寸调整请求。
Grid 列和行可以从 XAML 中推断出来。 必须显式声明列和行。 添加 ColumnDefinitionsRowDefinitions
HorizontalStackLayout *AndExpand 无效。
RelativeLayout 需要兼容性命名空间。 请改为使用 Grid,或为兼容性命名空间添加 xmlns
StackLayout 子级可在堆叠方向填充空间。 子级会堆叠在一起,超出可用空间。 如果需要子视图来填充空间,请更改为 Grid
VerticalStackLayout *AndExpand 无效。

.NET MAUI 控件通常遵循显式大小请求。 如果要求控件的宽度为 200 个与设备无关的单位,那么 .NET MAUI 将使该控件的宽度为 200 个单位,即使该控件的容器宽度只有 100 个单位也是如此。

Xamarin.Forms 中的默认布局值更改

Xamarin.Forms 对某些属性值使用任意默认值,如填充、边距和间距。 .NET MAUI 将这些任意属性值更改为零。

若要在未设置显式值的项目中保留 Xamarin.Forms 默认值,请将隐式样式添加到项目中。 有关隐式样式的详细信息,请参阅隐式样式

注意

.NET MAUI 项目模板包括为大多数控件提供默认样式的资源字典。 建议通过修改或继承这些资源字典,在应用中采用类似的方法。

下表列出了在 Xamarin.Forms 和 .NET MAUI 之间更改的布局属性值:

属性 Xamarin.Forms 值 .NET MAUI 值
Grid.ColumnSpacing 6 0
Grid.RowSpacing 6 0
StackLayout.Spacing 6 0

以下样式保留 Xamarin.Forms 默认值:

<!-- Forms defaults -->
<Style TargetType="Grid">
    <Setter Property="ColumnSpacing" Value="6"/>
    <Setter Property="RowSpacing" Value="6"/>
</Style>
<Style TargetType="StackLayout">
    <Setter Property="Spacing" Value="6"/>
</Style>
<Style TargetType="Frame">
    <Setter Property="Padding" Value="{OnPlatform 20,iOS=19}"/>
</Style>

Frame

Frame 已在 .NET MAUI 中替换成了 Border。 但是,将它包含在内,以便从 Xamarin.Forms 轻松迁移。 .NET MAUI 布局能够跨所有平台正确度量 Frame Padding,而 Xamarin.Forms 在不同平台上存在一些差异。 这可能会导致你的应用在 .NET MAUI 中看起来不一样。 如果使用的是默认值,则上面的示例说明了这种情况。

网格

Xamarin.Forms 和 .NET MAUI 之间 Grid 行为的最大变化是网格不会自动添加缺少的行和列。 例如,在 Xamarin.Forms 中,你可以将控件添加到 Grid,而无需指定其行行为:

<Grid>
    <Label Text="Hello"/>
    <Label Grid.Row="1" Text="World"/>
</Grid>

在 Xamarin.Forms 中,尽管没有声明 Grid 包含两行,但会自动添加第二行。 .NET MAUI 不执行此操作。 相反,你必须使用 RowDefinitions 属性显式指定 Grid 中有多少行。

重要说明

默认情况下,.NET MAUI 创建一个包含一列和一行的 Grid。 因此,如果你打算这样做,则无需设置 ColumnDefinitionsRowDefinitions 属性。

在 Xamarin.Forms 中,当 Label 位于 ColumnDefinition 的宽度设置为 Auto 的列中时,将隐式发生自动换行和尾部截断等换行符。 在 .NET MAUI 中,在此方案中换行符不会隐式发生,因为列将超过屏幕宽度以容纳子内容。 如果想要 LabelGrid 的边缘换行,则应将适当的 ColumnDefinition 设置为 * 或某个值。

StackLayout

.NET MAUI 中的堆栈布局(StackLayoutVerticalStackLayoutHorizontalStackLayout)与 Xamarin.Forms 中的 StackLayout 之间存在诸多差异。

主要区别在于 .NET MAUI 堆栈布局非常简单。 它们按一个方向堆叠子视图,直到所有子视图都堆叠完成。 它们会一直堆叠,直到最后一个子视图堆叠完成,即使超出堆叠方向的可用空间也不会停下。 因此,.NET MAUI 堆栈布局按特定方向排列控件。 它们不会细分空间。 这与 Xamarin.Forms StackLayout 完全不同,后者会根据情况和是否存在任何 *AndExpand 布局选项(如 FillAndExpandCenterAndExpand)来更改其布局行为。 Xamarin.Forms StackLayout 有时会细分空间,向其容器边缘扩展或者在此停止。 在其他情况下,它会扩展到其容器之外。

.NET MAUI 中的新堆栈布局(HorizontalStackLayoutVerticalStackLayout)无法识别 *AndExpand 布局选项。 如果遇到包含此类布局选项的子级,它们只会将 AndExpand 视为不存在。 例如,FillAndExpand 变为 Fill。 但是,为简化从 Xamarin.Forms 迁移的过程,.NET MAUI StackLayout 确实遵循 *AndExpand 布局选项,尽管它们已被标记为已过时。 为避免有关使用已过时成员的警告,应将使用 *AndExpand 布局选项的布局转换为适当的布局类型。 可按如下所示实现此目的:

  1. 如果布局不是 StackLayout ,请将 AndExpand 从其所有使用之处移除。 与在 Xamarin.Forms 中一样,在 .NET MAUI 中,AndExpand 布局选项对除 StackLayout 以外的任何布局都没有影响。

  2. 移除与堆叠方向正交的任何 AndExpand 属性。 例如,如果 StackLayoutOrientationVertical,并且有包含 HorizontalAligment="CenterAndExpand" 的子级 - 该布局选项不起作用,可以移除。

  3. 如果在 StackLayout 上有剩余 AndExpand 属性,则应该将该 StackLayout 转换为 GridGrid 旨在细分空间,并提供 AndExpand 在 Xamarin.Forms 中提供的布局。 以下示例显示使用 AndExpand 属性的 Xamarin.Forms StackLayout

    <StackLayout>
        <Label Text="Hello world!"/>
        <Image VerticalOptions="FillAndExpand" Source="dotnetbot.png"/>
    </StackLayout>
    

    这可以在 .NET MAUI 中转换为 Grid

    <Grid RowDefinitions="Auto, *">
        <Label Text="Hello world!"/>
        <Image Grid.Row="1" Source="dotnetbot.png"/>
    </Grid>
    

    执行此转换时,在 StackLayout 中标记为 AndExpand 的任何内容都应放在其自己的行或列中,其在 Grid 中的大小为 *

重要

StackLayout 沿其堆叠方向继续堆叠,直到内容耗尽。 它不会在该轴上细分其容器。 如果要将内容限制在某个方向上的受限空间内,则应使用其他布局,例如 Grid

RelativeLayout

不建议在 .NET MAUI 中使用 RelativeLayout。 请尽可能改用 Grid

如果确实需要 RelativeLayout,可以在 Microsoft.Maui.Controls.Compatibility 命名空间中找到它:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:compat="clr-namespace:Microsoft.Maui.Controls.Compatibility;assembly=Microsoft.Maui.Controls"
             x:Class="MyMauiApp.MyPage"
             Title="MyPage">
    <compat:RelativeLayout>
        <!-- Your code goes here -->
    </compat:RelativeLayout>
</ContentPage>

ScrollView

虽然通常不将 ScrollView 视为布局,但可以将其想象成布局,因为它用于滚动其子内容。 在 Xamarin.Forms 中,ScrollView 堆叠时的行为不一致。 它对最小大小有一些任意限制,这些限制部分取决于其内容,它有时会压缩,以使页面上的其他项能容纳在 StackLayout 内,但压缩方式不一致且有时出人意料。

在 .NET MAUI 中,除非另有限制,否则 ScrollView 将扩展到所需的任何大小。 这意味着在可以无限扩展的 VerticalStackLayout 内部,ScrollView 将扩展到其全部内容高度且不会滚动。 如果你是 Xamarin.Forms 用户,则此行为可能会令人困惑。