.NET MAUI XAML 中的类型和属性

已完成

XAML 是一种声明性标记语言。 其设计理念在于简化创建 UI 的过程。 XAML 中的元素直接表示在代码隐藏文件中访问的对象的实例化。

在本单元中,你将了解如何使用 XAML 中可用的类型,以及如何设置和读取这些类型的属性。

类型在哪里定义?

.NET MAUI 实现一个 XAML 分析程序,该分析程序用于分析声明的 XAML 元素并将每个元素实例化为 .NET 类型。 .NET MAUI 分析程序理解的 XAML 的方言特定于 .NET MAUI,但它类似于其他框架(例如 Windows Presentation Foundation)使用的 XAML。

实现由 XAML 代码标识的项目的 .NET 类型由多个 .NET 程序集中的代码实现。 其中许多程序集包含在 .NET MAUI 模板中。 还可以通过在项目中加载相应的程序集来利用其他自定义类型。 许多程序集都作为 NuGet 包提供。 MAUI 应用使用的大多数常见类型都位于 Microsoft.Maui.Dependencies 和 Microsoft.Maui.Extensions 包中。

每个类型都在命名空间中定义。 在 XAML 代码中,为引用的类型指定命名空间。 许多 MAUI 控件位于 Microsoft.Maui.Controls 命名空间中,而 Microsoft.Maui 命名空间定义实用工具类型(例如 Thickness),Microsoft.Maui.Graphics 命名空间包括通用类型(例如 Color)。 选择以这种方式引入类型突出了 XAML 的可扩展性。 利用 XAML,可以创建应用的 UI,并可自由包含 .NET MAUI XAML 元素、.NET 类型和自定义类型。 在大多数情况下,你无需担心这些命名空间,因为它们是使用 C# 的隐式 usings 功能引入的,该功能会自动在应用范围添加它们。

如何使用 XAML 实例化类型

使用 XAML 生成 UI 的第一步是实例化 UI 控件类型。 在 XAML 中,可以使用对象元素语法创建指定类型的对象。 对象元素语法是一种标准格式的 XML 语法,用于声明元素。 例如,如果要创建具有特定颜色的标签,XAML 元素将类似于以下代码:

<Label TextColor="AntiqueWhite"/>

.NET MAUI XAML 分析程序将分析此 XAML 元素,以在内存中实例化该对象。 实际上,分析后的 XAML 标签与以下 C# 代码相同:

var myLabel = new Label
{
  TextColor = Color.FromRgb(255, 255, 100)
};

什么是 XAML 命名空间?

请记住,若要使 XAML 分析程序成功分析页面中控件的 XAML 定义,它必须有权访问实现控件并定义其属性的代码。 .NET MAUI 页面可用的控件在 Microsoft.Maui NuGet 包中安装的程序集的集合中实现。 这些控件位于这些程序集中的 .NET 命名空间中。 在 C# 代码中,使用 using 指令将命名空间引入范围。 在 XAML 页中,引用具有页面的 xmlns 属性的命名空间。 以下代码显示了在上一单元中创建的 XAML 页面使用的命名空间:

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

    ...
</ContentPage>

第一个命名空间 http://schemas.microsoft.com/dotnet/2021/maui 是页面的默认命名空间。 这种 URI 形式的命名空间是典型的 XML,看起来与你可能熟悉的 C# 中的命名空间稍有不同。 但是,此 URI 只是 Microsoft.Maui NuGet 包中程序集定义的一个或多个命名空间的别名,因此在页面开头指定此命名空间会将所有 .NET MAUI 类型和控件引入范围。 如果省略此命名空间,则无法使用控件,例如 ButtonLabelEntryStackLayout

第二个命名空间 http://schemas.microsoft.com/winfx/2009/xaml 引用包含各种 .NET 内部类型的程序集,例如字符串、数值和属性。 在上面的 XAML 代码中,此命名空间分配有别名 x。 在此页的 XAML 代码中,通过使用 x: 对这些类型添加前缀来引用此命名空间中的类型。 例如,将每个 XAML 页面编译为类,并指定使用页面的 x:Class 属性生成的类的名称:

<ContentPage ...
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiXaml.Page1"
            ...>

    ...
</ContentPage>

可以通过 XAML 命名空间在 XAML 代码中引用自己的程序集中的类型。 例如,如果希望将类型和方法用于项目中名为 Utils 的命名空间中定义的 XAML 代码,可以将 Utils 命名空间添加到页面,如以下代码所示。 在此示例中,通过使用别名 mycode 为此命名空间中的类型添加前缀来访问它们。

<ContentPage ...
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:mycode="clr-namespace:Utils"
            ...>

    ...
</ContentPage>

注意

你稍后将在本模块中看到此技术的更多示例。

如何在 XAML 中指定属性值

在 XML 中,你使用特性来描述或提供有关元素的信息。 在 XAML 中,你使用特性在基础类型上设置属性。 例如,考虑使用以下 C# 代码:

var label = new Label { Text = "Username", TextColor = Color.Black };

此语句创建一个新 Label 对象并设置 TextTextColor 属性。 若要在 XAML 中设置属性,请使用特性。 相应的 XAML 代码与下面类似:

<Label Text="Username" TextColor="Black" />

你可能会注意到,XAML 代码与 C# 代码的不同之处在于属性值。 例如,在 C# 代码中,你将 Color 类型用于 TextColor 属性。 但是,在 XAML 定义中,使用字符串值设置 TextColor。 这是因为,字符串是你可用于 XML 特性值的唯一有效元素。 因此,需要有一种方法来将每个字符串值转换为其正确的类型。 在 XAML 中,可以通过使用类型转换器执行此转换。

类型转换器是什么?

类型转换器将指定为字符串值的 XML 特性转换为其正确类型。 若要更好地了解此概念,请考虑以下示例:

<Label Text="Username" TextColor="Black" FontSize="42" FontAttributes="Bold,Italic" />

此代码创建一个 Label 并设置其 TextTextColorFontSizeFontAttributes 属性。

我们先从第一个属性 Text 开始。 文本已经是一个字符串,这意味着 XAML 页面不需要类型转换器。 接下来,TextColor 使用 Color 类型,因此 XAML 需要类型转换器才能将字符串转换为相应的 Color 值。 FontSize 属性是一个整数,因此 XAML 需使用类型转换器将字符串分析为整数。 最后,FontAttributes 是复杂对象的一个示例。 可以将值合并为一个以逗号分隔的字符串:"Bold,Italic"。 逗号分隔的字符串会被视为基于 [Flags] 的枚举,相应的类型转换器会将值的按位 OR 应用于属性。

.NET MAUI 具有适用于大多数内置类的类型转换器,它将自动使用这些类型转换器。 但如果没有特定转换器,可以编写自己的转换器并将其与类型相关联,使其可在 XAML 中使用。

复杂类型分配

类型转换器非常适合简单的属性设置;但是,在某些情况下,还需要使用对象的属性值创建完整对象。 此问题的解决方案是更改属性分配以使用基于元素的语法。 此语法称为“属性元素”窗体。 此语法涉及将属性 setter 分解为父子形式,其中属性表示为 Type.PropertyName 形式的元素标记。 假设你要为标签分配手势识别器,以便应用用户可以点击标签。 手势识别器是具有自身属性的复杂对象。 通常,需要分配这些属性来确保功能正常:

<TapGestureRecognizer NumberOfTapsRequired="2" />

如果需要将此值分配给 Label,可以编写如下所示的 XAML:

<Label Text="Username" TextColor="Black" FontSize="42" FontAttributes="Bold,Italic">
    <Label.GestureRecognizers>
        <TapGestureRecognizer NumberOfTapsRequired="2" />
    </Label.GestureRecognizers>
</Label>

Label 类型具有名为 GestureRecognizers 的属性。 通过使用 Property Element 形式,可以将 TapGestureRecognizer 添加到 Label 的手势列表中。

默认内容属性

某些 .NET MAUI 控件具有默认内容属性。 利用内容属性,可以在控件上指定属性的值,而无需在 XAML 中显式指出。 查看以下 XAML 片段:

<VerticalStackLayout>
    <VerticalStackLayout.Children>
        <Label Text="Please log in" />
    </VerticalStackLayout.Children>
</VerticalStackLayout>

此代码创建一个 VerticalStackLayout 并添加一个 Label 作为子元素。 由于将子级添加到 VerticalStackLayout 很常见,因此其 Children 属性为默认内容属性。 这意味着可以在不显式指定 Children 的情况下添加子级,如下所示:

<VerticalStackLayout>
    <Label Text="Please log in" />
</VerticalStackLayout>

知识检查

1.

类型转换器的作用是什么?

2.

如何将自己的自定义类型引入 XAML 文件中的范围?