创建 XAML 标记扩展

浏览示例。 浏览示例

在开发者级别,.NET Multi-platform App UI (.NET MAUI) XAML 标记扩展是实现 IMarkupExtension<T>IMarkupExtension 接口的类。 还可以通过从 IMarkupExtensionIMarkupExtension<T> 派生来定义自己的自定义 XAML 标记扩展。 如果标记扩展获取特定类型的值,请使用泛型形式。 下面是多个 .NET MAUI 标记扩展的情况:

  • TypeExtension 派生自 IMarkupExtension<Type>
  • ArrayExtension 派生自 IMarkupExtension<Array>
  • DynamicResourceExtension 派生自 IMarkupExtension<DynamicResource>
  • BindingExtension 派生自 IMarkupExtension<BindingBase>

用于实现 IMarkupExtensionIMarkupExtension<T> 的所有类都需要通过 RequireServiceAttributeAcceptEmptyServiceProviderAttribute 进行批注。 有关详细信息,请参阅服务提供商

两个 IMarkupExtension 接口各自只定义一种方法,名为 ProvideValue

public interface IMarkupExtension
{
    object ProvideValue(IServiceProvider serviceProvider);
}

public interface IMarkupExtension<out T> : IMarkupExtension
{
    new T ProvideValue(IServiceProvider serviceProvider);
}

由于 IMarkupExtension<T> 派生自 IMarkupExtension 并包含 ProvideValue 上的 new 关键字,因此它包含这两种 ProvideValue 方法。

通常,XAML 标记扩展定义用于返回值的属性,并且 ProvideValue 方法具有 IServiceProvider 类型的一个参数。 有关数据库提供程序的详细信息,请参阅数据库提供程序

创建标记扩展

以下 XAML 标记扩展演示如何创建自己的标记扩展。 这让你可以使用色调、饱和度和亮度组件构造 Color 值。 它为颜色的四个组件定义四种属性,包括初始化为 1 的 alpha 组件。 该类派生自 IMarkupExtension<Color>,表示 Color 返回值:

public class HslColorExtension : IMarkupExtension<Color>
{
    public float H { get; set; }
    public float S { get; set; }
    public float L { get; set; }
    public float A { get; set; } = 1.0f;

    public Color ProvideValue(IServiceProvider serviceProvider)
    {
        return Color.FromHsla(H, S, L, A);
    }

    object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
    {
        return (this as IMarkupExtension<Color>).ProvideValue(serviceProvider);
    }
}
[AcceptEmptyServiceProvider]
public class HslColorExtension : IMarkupExtension<Color>
{
    public float H { get; set; }
    public float S { get; set; }
    public float L { get; set; }
    public float A { get; set; } = 1.0f;

    public Color ProvideValue(IServiceProvider serviceProvider)
    {
        return Color.FromHsla(H, S, L, A);
    }

    object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
    {
        return (this as IMarkupExtension<Color>).ProvideValue(serviceProvider);
    }
}

此标记扩展通过 AcceptEmptyServiceProviderAttribute 进行批注,因为它不使用来自服务提供商的服务。 有关详细信息,请参阅服务提供商

由于 IMarkupExtension<T> 派生自 IMarkupExtension,因此该类必须包含两种 ProvideValue 方法,一种方法返回 Color,另一种返回 object,但第二种方法可以调用第一种方法。

使用标记扩展

以下 XAML 演示了各种方法,这些方法可用于调用 HslColorExtension 来指定 BoxView 的颜色:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MarkupExtensions"
             x:Class="MarkupExtensions.HslColorDemoPage"
             Title="HSL Color Demo">
    <ContentPage.Resources>
        <Style TargetType="BoxView">
            <Setter Property="WidthRequest" Value="80" />
            <Setter Property="HeightRequest" Value="80" />
            <Setter Property="HorizontalOptions" Value="Center" />
            <Setter Property="VerticalOptions" Value="Center" />
        </Style>
    </ContentPage.Resources>

    <StackLayout>
        <BoxView>
            <BoxView.Color>
                <local:HslColorExtension H="0" S="1" L="0.5" A="1" />
            </BoxView.Color>
        </BoxView>
        <BoxView>
            <BoxView.Color>
                <local:HslColor H="0.33" S="1" L="0.5" />
            </BoxView.Color>
        </BoxView>
        <BoxView Color="{local:HslColorExtension H=0.67, S=1, L=0.5}" />
        <BoxView Color="{local:HslColor H=0, S=0, L=0.5}" />
        <BoxView Color="{local:HslColor A=0.5}" />
    </StackLayout>
</ContentPage>

在此示例中,如果 HslColorExtension 是 XML 标记,则这四种属性会设置为特性,但如果它出现在大括号中,则四种属性用逗号分隔,不带引号。 HSL 的默认值为 0,A 的默认值为 1,因此如果希望将这些属性设置为默认值,则可以省略这些属性。 最后一个示例演示了亮度为 0 的情况,这通常会导致显示黑色,但 alpha 通道为 0.5,因此它是半透明的,并且在页面的白色背景下显示为灰色:

HSL 颜色演示。

服务提供商

通过使用 ProvideValueIServiceProvider 参数,XAML 标记扩展可以访问有关使用它们的 XAML 文件的数据。 例如,可以通过 IProvideValueTarget 服务检索有关应用标记扩展的对象的数据:

IProvideValueTarget provideValueTarget = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;

IProvideValueTarget 接口定义了两种属性:TargetObjectTargetProperty。 在 HslColorExtension 类中获取此信息时,TargetObjectBoxViewTargetPropertyBoxViewColor 属性。 这是已设置 XAML 标记扩展的属性。

用于实现 IMarkupExtensionIMarkupExtension<T> 的所有类都需要通过 RequireServiceAttributeAcceptEmptyServiceProviderAttribute 进行批注:

  • 每次在 ProvideValue 方法中使用 serviceProvider.GetService(typeof(T)),都应该通过 [RequireService(typeof(T))] 对类进行批注:

    [RequireService([typeof(IReferenceProvider), typeof(IProvideValueTarget)])]
    public class MyMarkupExtension : IMarkupExtension
    {
        public object ProvideValue(IServiceProvider serviceProvider)
        {
            ...
            var referenceProvider = serviceProvider.GetService<IReferenceProvider>();
            var valueProvider = serviceProvider.GetService<IProvideValueTarget>() as IProvideParentValues
                                    ?? throw new ArgumentException("serviceProvider does not provide an IProvideValueTarget");
            ...
        }
    }
    
  • 如果标记扩展不使用来自服务提供商的任何服务,则应通过 [AcceptEmptyServiceProvider] 对该类进行批注。

由于 XAML 编译器优化可使生成的代码更加高效,因此需要这些批准,这有助于减小应用大小并提高运行时性能。