Creación de extensiones de marcado XAML
En el nivel de desarrollador, una extensión de marcado XAML de interfaz de usuario de aplicaciones multiplataforma de .NET (.NET MAUI) es una clase que implementa la interfaz IMarkupExtension o IMarkupExtension<T>. También es posible definir tus propias extensiones de marcado XAML personalizadas derivadas de IMarkupExtension o IMarkupExtension<T>. Usa el formulario genérico si la extensión de marcado obtiene el valor de un tipo determinado. Este es el caso de varias de las extensiones de marcado .NET MAUI:
TypeExtension
deriva deIMarkupExtension<Type>
.ArrayExtension
deriva deIMarkupExtension<Array>
.DynamicResourceExtension
deriva deIMarkupExtension<DynamicResource>
.BindingExtension
deriva deIMarkupExtension<BindingBase>
.
Todas las clases que implementan IMarkupExtension o IMarkupExtension<T> deben anotarse con o RequireServiceAttribute o AcceptEmptyServiceProviderAttribute. Para más información, consulta Proveedores de servicios.
Las dos interfaces IMarkupExtension definen sólo un método cada una, denominado ProvideValue
:
public interface IMarkupExtension
{
object ProvideValue(IServiceProvider serviceProvider);
}
public interface IMarkupExtension<out T> : IMarkupExtension
{
new T ProvideValue(IServiceProvider serviceProvider);
}
Dado que IMarkupExtension<T> deriva de IMarkupExtension e incluye la palabra clave new
en ProvideValue
, contiene ambos métodos ProvideValue
.
A menudo, las extensiones de marcado XAML definen propiedades que contribuyen al valor devuelto, y el método ProvideValue
tiene un único argumento de tipo IServiceProvider
. Para obtener más información sobre los proveedores de servicios, consulta Proveedores de servicios.
Creación de una extensión de marcado
La siguiente extensión de marcado XAML muestra cómo crear tu propia extensión de marcado. Permite construir un valor Color con componentes de matiz, saturación y luminosidad. Define cuatro propiedades para los cuatro componentes del color, incluido un componente alfa que se inicializa en 1. La clase deriva de IMarkupExtension<Color>
para indicar un valor devuelto 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);
}
}
Esta extensión de marcado se anota con AcceptEmptyServiceProviderAttribute porque no usa un servicio del proveedor de servicios. Para más información, consulta Proveedores de servicios.
Como IMarkupExtension<T> deriva de IMarkupExtension, la clase debe contener dos métodos ProvideValue
, uno que devuelva un Color y otro que devuelva un object
, pero el segundo método puede llamar al primero.
Consumo de una extensión de marcado
El siguiente XAML demuestra una variedad de enfoques que se pueden usar para invocar el HslColorExtension
para especificar el color para un 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>
En este ejemplo, cuando HslColorExtension
es una etiqueta XML, las cuatro propiedades se establecen como atributos, pero cuando aparece entre llaves, las cuatro propiedades están separadas por comas sin comillas. Los valores predeterminados de H
, S
y L
son 0, y el valor predeterminado de A
es 1, por lo que esas propiedades pueden omitirse si deseas que tengan valores predeterminados. En el último ejemplo se muestra un ejemplo en el que la luminosidad es 0, que normalmente da como resultado negro, pero el canal alfa es 0,5, por lo que es medio transparente y aparece gris en el fondo blanco de la página:
Proveedores de servicios
Con el argumento IServiceProvider
de ProvideValue
, las extensiones de marcado XAML pueden acceder a datos sobre el archivo XAML en el que se están usando. Por ejemplo, el servicio IProvideValueTarget
habilita la recuperación datos sobre el objeto al que se aplica la extensión de marcado:
IProvideValueTarget provideValueTarget = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
La interfaz IProvideValueTarget
define dos propiedades, TargetObject
y TargetProperty
. Cuando esta información se obtiene en la clase HslColorExtension
, TargetObject
es la propiedad BoxView y TargetProperty
es la propiedad Color
de BoxView. Esta es la propiedad en la que se ha establecido la extensión de marcado XAML.
Todas las clases que implementan IMarkupExtension o IMarkupExtension<T> deben anotarse con o RequireServiceAttribute o AcceptEmptyServiceProviderAttribute:
Por cada uso de
serviceProvider.GetService(typeof(T))
en el métodoProvideValue
, la clase debe anotarse con[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"); ... } }
Si la extensión de marcado no usa ningún servicio del proveedor de servicios, la clase debe anotarse con
[AcceptEmptyServiceProvider]
.
Estas anotaciones son necesarias debido a una optimización del compilador XAML que permite la generación de un código más eficaz, lo que ayuda a reducir el tamaño de la aplicación y mejorar el rendimiento en tiempo de ejecución.