XAML 标记扩展

已完成

大部分的 XAML 定义会在编译时确定。 你通常知道应将元素定位到何处、将使用哪些颜色和字体,以及应将哪些文本值分配给属性。

但是,有时你需要将属性值设置为在编译时无法确定的值。 这些值只有在程序运行时才能知晓。 在这些情况下,可以创建一个对象,该对象在运行时为 XAML 提供值。 XAML 支持使用标记扩展实现此目的

在本单元中,你将了解如何创建和使用标记扩展。

什么是标记扩展?

标记扩展是 XAML 中用于访问运行时值的类。 假设 XAML UI 中定义了多个标签,并且你要在整个应用中将 FontSize 属性设置为相同的值,以确保所有标签样式一致。 可以使用 XAML 设置 FontSize 属性,如以下示例所示:

<Label Text="Hello, World!"
            Grid.Row="0"
            SemanticProperties.HeadingLevel="Level1"
            FontSize="28"
            HorizontalOptions="CenterAndExpand"/>

可以为每个标签重复使用此相同设置,但如果以后想要更改此值,该怎么办? 需要找到此属性的每个实例并进行更改。 此外,假设你不知道要使用什么值,可以在运行时基于设备方向、屏幕分辨率或其他注意事项等因素来计算。 在这些情况下,你需要比硬编码文本更复杂的内容。 因此,标记扩展非常有用。 标记扩展使获取 XAML 中使用的值的方式变得灵活。

创建标记扩展

标记扩展是用于实现 Microsoft.Maui.Controls.Xaml.IMarkupExtension 接口的类。 此接口使用以下签名定义一个名为 ProvideValue 的方法:

public object ProvideValue(IServiceProvider serviceProvider)
{
    ...
}

此方法的目的是为 XAML 标记提供值。 请注意,返回类型为 object,因此只要值适合使用该值的位置,该值就可以为任何类型。 例如,在计算和返回字号的标记扩展中,返回类型应为 double

serviceProvider 参数包含有关在 XAML 代码中使用标记扩展的位置的上下文信息;在其他信息中,它用于标识要向其应用扩展的控件。

FontSize 属性的标记扩展可以保持简单。 在下面的示例中,MainPage 类公开名为 MyFontSizedouble 字段。 GlobalFontSizeExtension 类实现 IMarkupExtension 接口,并且 ProvideValue 方法返回 MyFontSize 变量的值:

namespace MyMauiApp;

public partial class MainPage : ContentPage
{
    public const double MyFontSize = 28;

    public MainPage()
    {
        InitializeComponent();
        ...
    }
    ...
}

public class GlobalFontSizeExtension : IMarkupExtension
{
    public object ProvideValue(IServiceProvider serviceProvider)
    {
        return MainPage.MyFontSize;
    }
}

注意

MyFontSize 字段必须是 MainPage 类的 static 成员,才能以这种方式在 ProvideValue 方法中引用它。 好的做法意味着在这种情况下,变量也应该是常量。 值 conststatic

ProvideValue 方法还可以根据方向和设备外形规格对返回的值进行调整。

将标记扩展应用于 XAML 中的控件

若要在 XAML 代码中使用标记扩展,请将包含 GlobalFontSizeExtension 类的命名空间添加到 ContentPage 标记中的命名空间列表。 在以下示例中,为此命名空间提供了别名 mycode:

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

可以使用标记扩展设置 FontSize 属性,如下所示。 请注意,按照约定,标记扩展的名称中包含后缀“Extension”。 XAML 可识别此后缀,在从 XAML 代码调用扩展时无需包含它。 在以下示例中,GlobalFontSizeExtension 类仅引用为 GlobalFontSize

<Label Text="Hello, World!"
            Grid.Row="0"
            SemanticProperties.HeadingLevel="Level1"
            FontSize="{mycode:GlobalFontSize}"
            HorizontalOptions="CenterAndExpand"/>

对于需要指定字号的任何控件,可以在整个 XAML 代码中应用相同的标记扩展。 以后如果决定更改字号,只需修改 MainPage 类中 MyFontSize 变量的定义。

StaticExtension 类

GlobalFontSize 标记扩展一样有用,但你不太可能创建此类扩展。 原因很简单;.NET MAUI 已经提供了更通用的扩展,可用于引用代码中的任何静态值。 此扩展命名为 StaticExtension,简称 Static。 以下代码显示了此扩展类的基本大纲:

[ContentProperty ("Member")]
public class StaticExtension : IMarkupExtension
{
    public string Member {get; set;}
    public object ProvideValue (IServiceProvider serviceProvider)
    {
        ...
    }
}

注意

自定义标记扩展的目的是允许你处理更复杂的情况,而不是简单的静态情况。 例如,可能需要基于设备外形规格动态地更改字号。

若要在 XAML 代码中使用此类,请提供要在 Member 属性中引用的静态变量的名称,ProvideValue 方法会在此变量中返回值。 下面的示例演示了如何使用该类:

<Label Text="Hello, World!"
            Grid.Row="0"
            SemanticProperties.HeadingLevel="Level1"
            FontSize="{x:Static Member=mycode:MainPage.MyFontSize}"
            HorizontalOptions="CenterAndExpand"/>

.NET MAUI 提供一组其他标记扩展类,可用于数据绑定、引用动态资源和样式以及处理数据数组等方案。

知识检查

1.

哪个标记扩展可将 XAML 属性设置为在代码隐藏类中定义的静态值?

2.

使用哪个接口来创建自定义标记扩展?