开始使用 XAML

Browse sample. 浏览示例

在 .NET Multi-platform App UI (.NET MAUI) 应用中,XAML 主要用于定义页面的视觉内容以及与 C# 代码隐藏文件配合使用。 代码隐藏文件为标记提供代码支持。 这两个文件共同构成一个包含子视图和属性初始化的新的类定义。 在 XAML 文件中,使用 XML 元素和特性引用类和属性,并在标记和代码之间建立链接。

XAML 文件详解

一个新 .NET MAUI 应用包含三个 XAML 文件及其关联的代码隐藏文件:

Screenshot of the structure of a new .NET MAUI app.

第一对配对文件是 App.xaml(XAML 文件)与 App.xaml.cs(与 XAML 文件关联的 C# 代码隐藏文件)。 App.xamlApp.xaml.cs 都构成名为 App 的类(派生自 Application)。 第二对配对文件是 AppShell.xamlAppShell.xaml.cs,共同构成名为 AppShell 的类(派生自 Shell)。 其他大多具有 XAML 文件的类构成了派生自 ContentPage 的类,并定义了页面的 UI。 MainPage.xamlMainPage.xaml.cs 文件的情况也是如此。

MainPage.xaml 文件的结构如下:

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

两个 XML 命名空间 (xmlns) 声明引用 microsoft.com 上的 URI。 但这些 URI 处均无内容,基本用作版本标识符。

第一个 XML 命名空间声明表示 XAML 文件中定义的没有前缀的标记引用 .NET MAUI 中的类,例如 ContentPage)。 第二个命名空间声明定义 x 的前缀。 该前缀用于 XAML 自身固有的多个元素和特性,XAML 的其他实现也支持这些元素和特性。 但这些元素和特性会有所不同,具体取决于在 URI 中嵌入的年份。 .NET MAUI 支持 2009 XAML 规范

在第一个标记的末尾,为名为 Class 的特性使用了 x 前缀。 由于 x 前缀的使用在 XAML 命名空间中基本上很普遍,因此 Class 等 XAML 特性几乎总是被称为 x:Classx:Class 特性指定完全限定的 .NET 类名:MyMauiApp 命名空间中的 MainPage 类。 这表示此 XAML 文件在派生自 ContentPage(出现 x:Class 特性的标签)的 MyMauiApp 命名空间中定义了名为 MainPage 的新类。

x:Class 特性只能出现在 XAML 文件的根元素中,用于定义派生的 C# 类。 这是 XAML 文件中定义的唯一新类。 XAML 文件中出现的其他所有内容是从现有类中进行实例化的并会进行初始化。

MainPage.xaml.cs 文件与以下类似

namespace MyMauiApp;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }
}

MainPage 类派生自 ContentPage,是一种分部类定义。

当 Visual Studio 构建项目时,源生成器会生成新的 C# 源(包含调用自 MainPage 构造函数的 InitializeComponent 方法的定义)并将其添加到编译对象。

在运行时,MauiProgram 类中的代码引导应用程序并执行 App 类构造函数,该构造函数实例化 AppShellAppShell 类会实例化要显示的应用的第一页,即 MainPageMainPage 构造函数调用 InitializeComponent(初始化 XAML 文件中定义的所有对象),将它们全部以父子关系连接,将代码中定义的事件处理程序附加到 XAML 文件中设置的事件,并将对象的结果树设置为页面的内容。

注意

AppShell 类使用 .NET MAUI Shell 设置要显示的应用的第一页。 Shell 超出了本 XAML 介绍的范围。 有关详细信息,请参阅 .NET MAUI Shell

设置页面内容

ContentPage 应包含单个子元素,该子元素可以是具有子视图的视图或布局。 ContentPage 的子级自动设置为 ContentPage.Content 属性的值。

以下示例显示了包含 LabelContentPage

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.HelloXamlPage"
             Title="Hello XAML Page">
    <Label Text="Hello, XAML!"
           VerticalOptions="Center"
           HorizontalTextAlignment="Center"
           Rotation="-15"
           FontSize="18"
           FontAttributes="Bold"
           TextColor="Blue" />
</ContentPage>

在上述示例中可看到,类、属性和 XML 之间的关系应非常明确。 .NET MAUI 类(例如 ContentPageLabel)作为 XML 元素出现在 XAML 文件中。 该类的属性(包括 ContentPage 上的 TitleLabel 的 7 个属性)通常显示为 XML 特性的属性。

有很多快捷方式可以设置这些属性的值。 部分属性是基本数据类型。 例如,TitleText 属性是 string 类型,Rotationdouble 类型。 HorizontalTextAlignment 属性是 TextAlignment 类型,是一种枚举。 对于枚举类型的任何属性,只需提供成员名称即可。

但对于更复杂类型的属性,需要使用转换器来分析 XAML。 这些是.NET MAUI 中派生自 TypeConverter 的类。 在上述示例中,自动应用了多个 .NET MAUI 转换器将字符串值转换为其正确类型:

  • 用于 VerticalOptions 属性的 LayoutOptionsConverter。 此转换器将 LayoutOptions 结构的公共静态字段名称转换为类型 LayoutOptions 的值。
  • 用于 TextColor 属性的 ColorTypeConverter。 此转换器转换 Colors 类公共静态字段的名称或十六进制 RGB 值(使用或不使用 alpha 通道)。

运行 .NET MAUI 应用时,通常会显示 MainPage。 若要查看不同页面,可以在 AppShell.xaml 文件中将其设置为新的启动页面,或者从 MainPage 导航到新页面。

若要实现导航,可在 MainPage.xaml.cs 构造函数中创建一个简单的 Button,并使用事件处理程序导航到 HelloXamlPage

public MainPage()
{
    InitializeComponent();

    Button button = new Button
    {
        Text = "Navigate!",
        HorizontalOptions = LayoutOptions.Center,
        VerticalOptions = LayoutOptions.Center
    };

    button.Clicked += async (sender, args) =>
    {
        await Navigation.PushAsync(new HelloXamlPage());
    };

    Content = button;
}

当编译和部署此应用的新版本时,屏幕上会出现一个按钮。 长按该按钮可导航到 HelloXamlPage

Screenshot of rotated Label text.

可以使用每个平台上显示的导航栏导航回 MainPage

注意

替代此导航模型的一种方法是使用 .NET MAUI Shell。 有关详细信息,请参阅 .NET MAUI Shell 概述

XAML 和代码交互

多数 ContentPage 派生物的子级都是布局,如 StackLayoutGrid,且该布局可以包含多个子级。 在 XAML 中,这些父子关系采用常规 XML 层次结构建立:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.XamlPlusCodePage"
             Title="XAML + Code Page">
    <StackLayout>
        <Slider VerticalOptions="Center" />
        <Label Text="A simple Label"
               FontSize="18"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
        <Button Text="Click Me!"
                HorizontalOptions="Center"
                VerticalOptions="Center" />
    </StackLayout>
</ContentPage>

此 XAML 文件在语法上已完整,会得到以下 UI:

Screenshot of multiple controls on a page.

但虽然可以与 SliderButton 交互,但 UI 不会更新。 Slider 须能让 Label 显示当前值,且 Button 应执行某些操作。

使用 Label 显示 Slider 值的操作完全可以在 XAML 中通过数据绑定来实现。 但最好先查看代码解决方案。 即便如此,要实现 Button 单击必然需要代码。 这意味着 XamlPlusCodePage 的代码隐藏文件必须包含 SliderValueChanged 事件和 ButtonClicked 事件的处理程序:

namespace XamlSamples
{
    public partial class XamlPlusCodePage
    {
        public XamlPlusCodePage()
        {
            InitializeComponent();
        }

        void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
        {
            valueLabel.Text = args.NewValue.ToString("F3");
        }

        async void OnButtonClicked(object sender, EventArgs args)
        {
            Button button = (Button)sender;
            await DisplayAlert("Clicked!", "The button labeled '" + button.Text + "' has been clicked", "OK");
        }
    }
}

回到 XAML 文件中,SliderButton 标记需要包含引用这些处理程序的 ValueChangedClicked 事件的特性:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.XamlPlusCodePage"
             Title="XAML + Code Page">
    <StackLayout>
        <Slider VerticalOptions="Center"
                ValueChanged="OnSliderValueChanged" />
        <Label x:Name="valueLabel"
               Text="A simple Label"
               FontSize="18"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
        <Button Text="Click Me!"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                Clicked="OnButtonClicked" />
    </StackLayout>
</ContentPage>

请注意,为事件分配处理程序和为属性分配值所用语法相同。 此外,为使 SliderValueChanged 事件处理程序能使用 Label 显示当前值,需要该处理程序从代码中引用该对象。 因此,Label 需要一个名称,该名称使用 x:Name 特性指定。 x:Name 特性的 x 前缀指示此特性是 XAML 固有的。 分配给 x:Name 特性的名称与 C# 变量名称所用规则相同。 例如,必须以字母或下划线开头,且不包含嵌入的空格。

ValueChanged 事件处理程序现可以设置 Label 显示新的 Slider 值(可从事件参数获取):

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
    valueLabel.Text = args.NewValue.ToString("F3");
}

或者,处理程序可以从 sender 参数获取生成此事件的 Slider 对象,并通过以下获取 Value 属性:

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
    valueLabel.Text = ((Slider)sender).Value.ToString("F3");
}

结果是对 Slider 的任何操作都会导致其值显示在 Label 中:

Screenshot of multiple controls on a page, with Slider value displayed.

在上述示例中,Button 通过显示含按钮的 Text 的警报来模拟对 Clicked 事件的响应。 因此,事件处理程序可以将 sender 参数强制转换为 Button,然后访问其属性:

async void OnButtonClicked(object sender, EventArgs args)
{
    Button button = (Button)sender;
    await DisplayAlert("Clicked!", "The button labeled '" + button.Text + "' has been clicked", "OK");
}

OnButtonClicked 方法定义为 async,因为 DisplayAlert 方法是异步的,并且应以 await 运算符开头(该运算符在方法完成后返回)。 因为此方法从 sender 参数获取触发事件的 Button,所以同一个处理程序可用于多个按钮。

后续步骤

XAML 主要用于实例化和初始化对象。 但通常,属性必须设置为无法轻易用 XML 字符串表示的复杂对象,有时必须对子类设置由一个类定义的属性。 满足这两个要求需使用属性元素附加属性的基本 XAML 语法功能。