使用 XAML 标记扩展

Browse sample. 浏览示例

.NET Multi-platform App UI (.NET MAUI) XAML 标记扩展支持从各种源设置元素属性,从而帮助增强 XAML 的功能和灵活性。

例如,通常会按如下设置 BoxViewColor 属性:

<BoxView Color="Blue" />

但是,你可能更原意改为从存储在资源字典中的值、从已创建的类的静态属性的值或从页面上另一个元素的 Color 类型的属性设置 Color 特性,或者设置从单独的色调值、饱和度值和亮度值构造的该特性。 所有这些选项都可以使用 XAML 标记扩展来实现。

标记扩展是表达元素属性的另一种方法。 .NET MAUI XAML 标记扩展通常由括在大括号中的属性值标识:

<BoxView Color="{StaticResource themeColor}" />

大括号中的任何属性值始终是 XAML 标记扩展。 但是,也可以在不使用大括号的情况下引用 XAML 标记扩展。

注意

多个 XAML 标记扩展是 XAML 2009 规范的一部分。 它们出现在 XAML 文件中,带有惯用的 x 命名空间前缀,并且通常用此前缀来进行引用。

除了本文中讨论的标记扩展之外,.NET MAUI 还包含以下标记扩展,并在其他文章中进行了讨论:

x:Static 标记扩展

StaticExtension 类支持 x:Static 标记扩展。 该类具有类型为 string 的名为 Member 的单一属性,你可以将其设置为公共常量、静态属性、静态字段或枚举成员的名称。

一种使用 x:Static 的方法是先定义一个带有一些常量或静态变量的类,例如这个 AppConstants 类:

static class AppConstants
{
    public static double NormalFontSize = 18;
}

以下 XAML 演示了在 Label.FontSize 属性元素标记之间实例化 StaticExtension 类的最详细的方法:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:sys="clr-namespace:System;assembly=netstandard"
             xmlns:local="clr-namespace:MarkupExtensions"
             x:Class="MarkupExtensions.StaticDemoPage"
             Title="x:Static Demo">
    <StackLayout Margin="10, 0">
        <Label Text="Label No. 1">
            <Label.FontSize>
                <x:StaticExtension Member="local:AppConstants.NormalFontSize" />
            </Label.FontSize>
        </Label>
        ···
    </StackLayout>
</ContentPage>

XAML 分析程序还支持将 StaticExtension 类缩写为 x:Static

<Label Text="Label No. 2">
    <Label.FontSize>
        <x:Static Member="local:AppConstants.NormalFontSize" />
    </Label.FontSize>
</Label>

通过将 StaticExtension 类和成员设置置于大括号中,可以进一步简化此语法。 生成的表达式直接设置为 FontSize 属性:

<Label Text="Label No. 3"
       FontSize="{x:StaticExtension Member=local:AppConstants.NormalFontSize}" />

在此示例中,大括号中没有引号StaticExtensionMember 属性不再是 XML 特性, 而是标记扩展表达式的一部分。

正如将 x:StaticExtension 用作对象元素时可以将其缩写为 x:Static 一样,你也可以在大括号内的表达式中将其缩写:

<Label Text="Label No. 4"
       FontSize="{x:Static Member=local:AppConstants.NormalFontSize}" />

StaticExtension 类具有引用属性 MemberContentProperty 特性,该特性将此属性标记为该类的默认内容属性。 对于用大括号表示的 XAML 标记表达式,可以删除这类表达式的 Member= 部分:

<Label Text="Label No. 5"
       FontSize="{x:Static local:AppConstants.NormalFontSize}" />

这是 x:Static 标记扩展最常见的形式。

XAML 示例的根标记还包含 .NET System 命名空间的 XML 命名空间声明。 这可以让 Label 字号设置为静态字段 Math.PI。 这会导致文本相当小,因此 Scale 属性设置为 Math.E

<Label Text="&#x03C0; &#x00D7; E sized text"
       FontSize="{x:Static sys:Math.PI}"
       Scale="{x:Static sys:Math.E}"
       HorizontalOptions="Center" />

以下屏幕截图显示了 XAML 输出:

x:Static demo.

x:Reference 标记扩展

ReferenceExtension 类支持 x:Reference 标记扩展。 该类具有 string 类型的名为 Name 的单一属性,可以将其设置为页面上已使用 x:Name 命名的元素的名称。 此 Name 属性是 ReferenceExtension 的内容属性,因此如果大括号中有 x:Reference,则不需要 Name=x:Reference 标记扩展专用于数据绑定。 若要深入了解数据绑定,请参阅数据绑定

以下 XAML 示例演示了 x:Reference 与数据绑定的两种用法,第一个用于设置 Binding 对象的 Source 属性,第二个用于设置两个数据绑定的 BindingContext 属性:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MarkupExtensions.ReferenceDemoPage"
             x:Name="page"
             Title="x:Reference Demo">    
    <StackLayout Margin="10, 0">        
        <Label Text="{Binding Source={x:Reference page},
                              StringFormat='The type of this page is {0}'}"
               FontSize="18"
               VerticalOptions="Center"
               HorizontalTextAlignment="Center" />
        <Slider x:Name="slider"
                Maximum="360"
                VerticalOptions="Center" />
        <Label BindingContext="{x:Reference slider}"
               Text="{Binding Value, StringFormat='{0:F0}&#x00B0; rotation'}"
               Rotation="{Binding Value}"
               FontSize="24"
               HorizontalOptions="Center"
               VerticalOptions="Center" />        
    </StackLayout>
</ContentPage>

在此示例中,两个 x:Reference 表达式都使用 ReferenceExtension 类名的缩写版本,并去掉了表达式的 Name= 部分。 在第一个示例中,x:Reference 标记扩展嵌入在 Binding 标记扩展中,并且 SourceStringFormat 属性用逗号分隔。

以下屏幕截图显示了 XAML 输出:

x:Reference demo.

x:Type 标记扩展

x:Type 标记扩展是 C# typeof 关键字的 XAML 等效项。 TypeExtension 类对其提供支持,该类定义了一个名为 TypeNamestring 类型属性,该属性应设置为类或结构名称。 x:Type 标记扩展返回该类或结构的 Type 对象。 TypeNameTypeExtension 的内容属性,因此 x:Type 与大括号一起出现时,不需要 TypeName=

x:Type 标记扩展通常与 x:Array 标记扩展一起使用。 有关详细信息,请参阅 x:Array 标记扩展

以下 XAML 示例演示如何使用 x:Type 标记扩展实例化 .NET MAUI 对象并将其添加到 StackLayout。 XAML 包含三个 Button 元素,其 Command 属性设置为 BindingCommandParameter 属性设置为三种 .NET MAUI 视图的类型:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MarkupExtensions.TypeDemoPage"
             Title="x:Type Demo">    
    <StackLayout x:Name="stackLayout"
                 Padding="10, 0">        
        <Button Text="Create a Slider"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                Command="{Binding CreateCommand}"
                CommandParameter="{x:Type Slider}" />
        <Button Text="Create a Stepper"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                Command="{Binding CreateCommand}"
                CommandParameter="{x:Type Stepper}" />
        <Button Text="Create a Switch"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                Command="{Binding CreateCommand}"
                CommandParameter="{x:Type Switch}" />
    </StackLayout>
</ContentPage>

代码隐藏文件定义并初始化 CreateCommand 属性:

public partial class TypeDemoPage : ContentPage
{
    public ICommand CreateCommand { get; private set; }

    public TypeDemoPage()
    {
        InitializeComponent();

        CreateCommand = new Command<Type>((Type viewType) =>
        {
            View view = (View)Activator.CreateInstance(viewType);
            view.VerticalOptions = LayoutOptions.Center;
            stackLayout.Add(view);
        });

        BindingContext = this;
    }
}

当按下 Button 时,将创建 CommandParameter 参数的新实例并将其添加到 StackLayout。 然后,这三个 Button 对象与动态创建的视图共享页面:

x:Type demo.

x:Array 标记扩展

通过 x:Array 标记扩展,可以在标记中定义数组。 ArrayExtension 类对其提供支持,该类定义两个属性:

  • Type 类型的 Type,表示数组中元素的类型。 此属性应设置为 x:Type 标记扩展。
  • IList 类型的 Items,它是项本身的集合。 这是 ArrayExtension 的内容属性。

x:Array 标记扩展本身不会出现在大括号中。 但 x:Array 的开始标记和结束标记会分隔项列表。

以下 XAML 示例演示如何通过将 ItemsSource 属性设置为数组,从而使用 x:Array 将项目添加到 ListView

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MarkupExtensions.ArrayDemoPage"
             Title="x:Array Demo Page">
    <ListView Margin="10">
        <ListView.ItemsSource>
            <x:Array Type="{x:Type Color}">
                <Color>Aqua</Color>
                <Color>Black</Color>
                <Color>Blue</Color>
                <Color>Fuchsia</Color>
                <Color>Gray</Color>
                <Color>Green</Color>
                <Color>Lime</Color>
                <Color>Maroon</Color>
                <Color>Navy</Color>
                <Color>Olive</Color>
                <Color>Pink</Color>
                <Color>Purple</Color>
                <Color>Red</Color>
                <Color>Silver</Color>
                <Color>Teal</Color>
                <Color>White</Color>
                <Color>Yellow</Color>
            </x:Array>
        </ListView.ItemsSource>
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <BoxView Color="{Binding}"
                             Margin="3" />
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>     

在此示例中,ViewCell 为每个颜色条目创建简单的 BoxView 项:

x:Array demo.

注意

定义字符串或数字等常见类型数组时,请使用传递参数中列出的 XAML 语言基元标记。

x:Null 标记扩展

NullExtension 类支持 x:Null 标记扩展。 它没有属性,只是 C# null 关键字的 XAML 等效项。

以下 XAML 示例演示如何使用 x:Null 标记扩展:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MarkupExtensions.NullDemoPage"
             Title="x:Null Demo">
    <ContentPage.Resources>
        <Style TargetType="Label">
            <Setter Property="FontSize" Value="48" />
            <Setter Property="FontFamily" Value="OpenSansRegular" />
        </Style>
    </ContentPage.Resources>

    <StackLayout Padding="10, 0">
        <Label Text="Text 1" />
        <Label Text="Text 2" />
        <Label Text="Text 3"
               FontFamily="{x:Null}" />
        <Label Text="Text 4" />
        <Label Text="Text 5" />
    </StackLayout>
</ContentPage>      

在此示例中,为 Label 定义了隐式 Style,其中包含将 FontFamily 属性设置为特定字体的 Setter。 但是,第三个 Label 会避免使用隐式样式中定义的字体,方法是将其 FontFamily 设置为 x:Null

x:Null demo.

DataTemplate 标记扩展

通过 DataTemplate 标记扩展,可以将类型转换为 DataTemplateDataTemplateExtension 类支持该标记扩展,该类定义了 string 类型的 TypeName 属性,该属性设置为要转换为 DataTemplate 的类型的名称。 TypeName 属性是 DataTemplateExtension 的内容属性。 因此,对于用大括号表示的 XAML 标记表达式,可以去掉这类表达式的 TypeName= 部分。

注意

XAML 分析程序允许将 DataTemplateExtension 类缩写为 DataTemplate

Shell 应用程序中提供了此标记扩展的常见用法,如以下示例所示:

<ShellContent Title="Monkeys"
              Icon="monkey.png"
              ContentTemplate="{DataTemplate views:MonkeysPage}" />

在此示例中,MonkeysPageContentPage 转换为 DataTemplate,并将其设置为 ShellContent.ContentTemplate 属性的值。 这可确保仅在导航到页面时创建 MonkeysPage,而不是在应用程序启动时创建。

有关 Shell 应用的详细信息,请参阅 Shell