创建 XAML 标记扩展
在开发者级别,.NET Multi-platform App UI (.NET MAUI) XAML 标记扩展是实现 IMarkupExtension<T> 或 IMarkupExtension 接口的类。 还可以通过从 IMarkupExtension 或 IMarkupExtension<T> 派生来定义自己的自定义 XAML 标记扩展。 如果标记扩展获取特定类型的值,请使用泛型形式。 下面是多个 .NET MAUI 标记扩展的情况:
TypeExtension
派生自IMarkupExtension<Type>
ArrayExtension
派生自IMarkupExtension<Array>
DynamicResourceExtension
派生自IMarkupExtension<DynamicResource>
BindingExtension
派生自IMarkupExtension<BindingBase>
用于实现 IMarkupExtension 或 IMarkupExtension<T> 的所有类都需要通过 RequireServiceAttribute 或 AcceptEmptyServiceProviderAttribute 进行批注。 有关详细信息,请参阅服务提供商。
两个 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 标记,则这四种属性会设置为特性,但如果它出现在大括号中,则四种属性用逗号分隔,不带引号。 H
、S
和 L
的默认值为 0,A
的默认值为 1,因此如果希望将这些属性设置为默认值,则可以省略这些属性。 最后一个示例演示了亮度为 0 的情况,这通常会导致显示黑色,但 alpha 通道为 0.5,因此它是半透明的,并且在页面的白色背景下显示为灰色:
服务提供商
通过使用 ProvideValue
的 IServiceProvider
参数,XAML 标记扩展可以访问有关使用它们的 XAML 文件的数据。 例如,可以通过 IProvideValueTarget
服务检索有关应用标记扩展的对象的数据:
IProvideValueTarget provideValueTarget = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
IProvideValueTarget
接口定义了两种属性:TargetObject
和 TargetProperty
。 在 HslColorExtension
类中获取此信息时,TargetObject
是 BoxView,TargetProperty
是 BoxView 的 Color
属性。 这是已设置 XAML 标记扩展的属性。
用于实现 IMarkupExtension 或 IMarkupExtension<T> 的所有类都需要通过 RequireServiceAttribute 或 AcceptEmptyServiceProviderAttribute 进行批注:
每次在
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 编译器优化可使生成的代码更加高效,因此需要这些批准,这有助于减小应用大小并提高运行时性能。