Partilhar via


Criação de extensões de marcação XAML

Procurar amostra. Procurar no exemplo

No nível do desenvolvedor, uma extensão de marcação XAML do .NET MAUI (.NET Multi-platform App UI) é uma classe que implementa a interface IMarkupExtension ou IMarkupExtension<T>. Também é possível definir suas próprias extensões de marcação XAML personalizadas derivando de IMarkupExtension ou IMarkupExtension<T>. Use a forma genérica se a extensão de marcação obtiver um valor de um tipo específico. Esse é o caso de várias das extensões de marcação do .NET MAUI:

  • TypeExtension deriva de IMarkupExtension<Type>.
  • ArrayExtension deriva de IMarkupExtension<Array>.
  • DynamicResourceExtension deriva de IMarkupExtension<DynamicResource>.
  • BindingExtension deriva de IMarkupExtension<BindingBase>.

Todas as classes que implementam IMarkupExtension ou IMarkupExtension<T> precisam ser anotadas com o RequireServiceAttribute ou AcceptEmptyServiceProviderAttribute. Para obter mais informações, consulte Provedores de serviços.

As duas interfaces de IMarkupExtension definem apenas um método cada, chamado ProvideValue:

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

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

Como IMarkupExtension<T> deriva de IMarkupExtension e inclui a palavra-chave new em ProvideValue, ele contém os dois métodos ProvideValue.

Muitas vezes, as extensões de marcação XAML definem propriedades que contribuem para o valor retornado e o método ProvideValue tem um único argumento do tipo IServiceProvider. Para mais informações sobre provedores de serviços, consulte Provedores de serviços.

Criar uma extensão de marcação

A extensão de marcação XAML a seguir demonstra como criar sua própria extensão de marcação. Ela permite que você construa um valor Color usando componentes de matiz, saturação e luminosidade. Ela define quatro propriedades para os quatro componentes da cor, incluindo um componente alfa que é inicializado como 1. A classe deriva de IMarkupExtension<Color> para indicar um valor retornado 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);
    }
}

Essa extensão de marcação é anotada porque AcceptEmptyServiceProviderAttribute não usa um serviço do provedor de serviços. Para obter mais informações, consulte Provedores de serviços.

Como IMarkupExtension<T> deriva de IMarkupExtension, a classe deve conter dois métodos ProvideValue, um que retorna Color e outro que retorna object, mas o segundo método pode chamar o primeiro método.

Consumir uma extensão de marcação

O XAML a seguir demonstra uma variedade de abordagens que podem ser usadas para invocar o HslColorExtension para especificar a cor de um 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>

Neste exemplo, quando HslColorExtension é uma marca XML, as quatro propriedades são definidas como atributos, mas quando aparece entre chaves, as quatro propriedades são separadas por vírgulas sem aspas. Os valores padrão para H, S, e L são 0, e o valor padrão de A é 1, portanto, essas propriedades podem ser omitidas se você quiser que elas sejam definidas como valores padrão. O último exemplo mostra um exemplo em que a luminosidade é 0, o que normalmente resulta em preto, mas o canal alfa é 0,5, portanto, é meio transparente e fica cinza contra o fundo branco da página:

Demonstração de cores HSL.

Provedores de serviços

Usando o argumento IServiceProvider para ProvideValue, as extensões de marcação XAML podem obter acesso a dados sobre o arquivo XAML no qual estão sendo usadas. Por exemplo, o serviço IProvideValueTarget permite que você recupere dados sobre o objeto ao qual a extensão de marcação é aplicada:

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

A interface IProvideValueTarget define duas propriedades, TargetObject e TargetProperty. Quando esta informação é obtida na classe HslColorExtension, TargetObject é a BoxView e TargetProperty é a propriedade Color da BoxView. Essa é a propriedade na qual a extensão de marcação XAML foi definida.

Todas as classes que implementam IMarkupExtension ou IMarkupExtension<T> precisam ser anotadas com o RequireServiceAttribute ou AcceptEmptyServiceProviderAttribute:

  • Para cada uso de serviceProvider.GetService(typeof(T)) no método ProvideValue, a classe deve ser anotada com [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");
            ...
        }
    }
    
  • Se a extensão de marcação não usar nenhum serviço do provedor de serviços, a classe deverá ser anotada com [AcceptEmptyServiceProvider].

Essas anotações são necessárias devido a uma otimização do compilador XAML que permite a geração de código mais eficiente, o que ajuda a reduzir o tamanho do aplicativo e melhorar o desempenho do runtime.