XAML 入门

浏览示例。 浏览示例

在 .NET 多平台应用 UI (.NET MAUI) 应用中,XAML 主要用于定义页面的视觉内容,并与 C# 代码隐藏文件协同工作。 代码隐藏文件为标记提供代码支持。 这两个文件共同为包含子视图和属性初始化的新类定义做出贡献。 在 XAML 文件中,使用 XML 元素和属性引用类和属性,并建立标记和代码之间的链接。

XAML 文件的剖析

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

新 .NET MAUI 应用结构的屏幕截图。

第一个文件配对是 App.xaml、XAML 文件和 App.xaml.cs,它是与 XAML 文件关联的 C# 代码隐藏 文件。 App.xamlApp.xaml.cs都为派生自AppApplication类做出贡献。 第二个文件配对是 AppShell.xamlAppShell.xaml.cs,它们有助于创建了一个继承自 Shell 的名为 AppShell 的类。 具有 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 文件中定义的标记没有前缀,例如 ContentPage,引用 .NET MAUI 中的类。 第二个命名空间声明定义前缀 x。 这用于 XAML 本身固有的多个元素和属性,这些元素和属性受 XAML 的其他实现支持。 但是,根据 URI 中嵌入的年份,这些元素和属性略有不同。 .NET MAUI 支持 2009 XAML 规范

在第一个标记的末尾,前缀 用于一个名为 的属性。 由于对 x 前缀在 XAML 命名空间中几乎被普遍使用,因此 Class 几乎总是被称为 x:Class 的 XAML 属性。 该 x:Class 特性指定完全限定的 .NET 类名: MainPage 命名空间中的 MyMauiApp 类。 这意味着,此 XAML 文件定义了一个名为 MainPage 的新类,该类位于 MyMauiApp 命名空间,并派生自 ContentPage(属性出现在该标记的 x:Class 中)。

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

MainPage.xaml.cs文件如下所示:

namespace MyMauiApp;

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

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

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

Note

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

设置页面内容

A ContentPage 应包含一个子元素,可以是视图本身或包含子视图的布局。 ContentPage 的子元素会被自动设置为 ContentPage.Content 属性的值。

以下示例显示了ContentPage中包含Label

<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>

XAML 源生成

重要

此功能需要选择加入。 若要使用它,请根据以下说明在项目文件中设置 XAML 缩进器。 请在 .NET MAUI 存储库中告知你对此(好或坏)的体验。

从 .NET 10 开始,你可以选择通过使用 Roslyn 源代码生成,让 XAML 生成 C#,而不是生成中间语言(IL)。 通过生成源文件并直接在其中设置断点来提升您调试 XAML 的能力,能够加快页面渲染的调试性能,并提高应用在调试和发布构建模式之间的行为一致性。

若要为整个项目启用 XAML 源生成,请将此项添加到项目文件:

<PropertyGroup>
   <MauiXamlInflator>SourceGen</MauiXamlInflator>
</PropertyGroup>

若要更精细地控制哪些 XAML 文件使用 XAML 源生成,可以配置单个文件或模式:

<ItemGroup>
    <MauiXaml Update="**/*.xaml" Inflator="SourceGen" />
</ItemGroup>

可以通过在属性中 Inflator 配置模式来包括或排除某些 XAML 文件。 如果要选择退出某些文件或模式,请取消设置(或设置为“”) Inflator 以确保正确的默认值(默认值是调试上的运行时膨胀,XamlC on Release)

参考 源生成器配置文档 ,以使用类似 EmitCompilerGeneratedFiles选项。

全局 XML 命名空间(预览版)

在 .NET 10 中,可以选择使用全局 XAML 命名空间来减少页面中的样本 xmlns: 声明。

重要

此功能处于预览状态。 若要试用,请在项目文件中启用预览功能。 行为和默认值可能会更改。

** 若要启用,请添加一个带有程序集级 XmlnsDefinition 属性的 GlobalXmlns.cs,用于将全局命名空间映射到您的 CLR 命名空间,并在 XAML 根元素中引用 http://schemas.microsoft.com/dotnet/maui/global 命名空间:

// GlobalXmlns.cs
using Microsoft.Maui.Controls;

[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "MyApp.Views")]
[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "MyApp.Controls")]
<!-- Root element uses the global namespace -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/maui/global"
                         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                         x:Class="MyApp.MainPage">
        <TagView x:DataType="Tag" />
        <!-- No xmlns prefixes for TagView/Tag when globally mapped -->
        <!-- Add explicit xmlns prefixes only when disambiguation is needed -->

</ContentPage>

(可选)设置默认前缀:

using XmlnsPrefixAttribute = Microsoft.Maui.Controls.XmlnsPrefixAttribute;

[assembly: XmlnsPrefix("MyApp.Controls", "controls")]

若要启用最简化的体验(隐式默认命名空间),请添加:

<PropertyGroup>
    <DefineConstants>$(DefineConstants);MauiAllowImplicitXmlnsDeclaration</DefineConstants>
    <EnablePreviewFeatures>true</EnablePreviewFeatures>
</PropertyGroup>

现在,项目将隐式包含这 2 个命名空间,自 .NET MAUI 首次发布以来,你已习惯于在每个 XAML 文件中看到这些命名空间。

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

Note

由于 XAML 加载器使用x:,因此仍需使用该前缀。 仅此更改,就可以使视图的 XAML 更加紧凑。

之前

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

<ContentPage x:Class="MyApp.Pages.MyContentPage">
</ContentPage>

在上面的示例中,类、属性和 XML 之间的关系应该很明显。 .NET MAUI 类(例如 ContentPageLabel)以 XML 元素的形式出现在 XAML 文件中。 该类的属性,包括 Title on ContentPage 和七个 Label,通常显示为 XML 属性。

存在许多快捷方式来设置这些属性的值。 某些属性是基本数据类型。 例如,TitleText属性的类型为string,而Rotation属性的类型为doubleHorizontalTextAlignment 属性是 TextAlignment 类型的枚举。 对于任何枚举类型的属性,只需提供成员名称。

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

  • LayoutOptionsConverterVerticalOptions 属性。 此转换器将结构的公共静态字段 LayoutOptions 的名称转换为类型的 LayoutOptions值。
  • ColorTypeConverterTextColor 属性。 此转换器用于转换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

旋转的标签文本的屏幕截图。

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

Note

此导航模型的替代方法是使用 .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:

页面上多个控件的屏幕截图。

但是,虽然可以与 Slider 进行交互,但 Button UI 不会更新。 Slider 应该会导致 Label 显示当前值,并且 Button 应该执行某些操作。

Slider值的显示可以通过在 XAML 中使用Label结合数据绑定来完全实现。 但是,首先查看代码解决方案很有用。 即便如此,处理 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");
        }
    }
}
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 DisplayAlertAsync("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>

请注意,向事件分配处理程序的语法与向属性赋值具有相同的语法。 此外,为了让ValueChanged的事件处理程序使用Label来显示当前值,处理程序需要在代码中引用该对象。 因此,需要使用 x:Name 属性为 Label 指定名称。 属性 xx:Name 前缀指示此属性是 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 中。

页面上多个控件的屏幕截图,其中显示了滑块值。

在上面的示例中,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 运算符开头,该运算符在方法完成时返回。 由于此方法从Button参数中获取sender激发事件,因此同一处理程序可用于多个按钮。

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

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

后续步骤

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