Partilhar via


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

No nível programático, uma extensão de marcação XAML é uma classe que implementa a IMarkupExtension interface or IMarkupExtension<T> . Você pode explorar o código-fonte das extensões de marcação padrão descritas abaixo no diretório MarkupExtensions do Xamarin.Forms repositório GitHub.

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. Este é o caso de várias das Xamarin.Forms extensões de marcação:

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

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. (A exceção óbvia é NullExtension, em que ProvideValue simplesmente retorna null.) O ProvideValue método tem um único argumento do tipo IServiceProvider que será discutido posteriormente neste artigo.

Uma extensão de marcação para especificar cores

A extensão de marcação XAML a seguir permite que você construa um Color valor usando 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 double H { set; get; }

    public double S { set; get; }

    public double L { set; get; }

    public double A { set; get; } = 1.0;

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

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

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

A página Demonstração de Cores HSL mostra uma variedade de maneiras que HslColorExtension podem aparecer em um arquivo XAML para especificar a cor de um BoxViewarquivo :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MarkupExtensions"
             x:Class="MarkupExtensions.HslColorDemoPage"
             Title="HSL Color Demo">

    <ContentPage.Resources>
        <ResourceDictionary>
            <Style TargetType="BoxView">
                <Setter Property="WidthRequest" Value="80" />
                <Setter Property="HeightRequest" Value="80" />
                <Setter Property="HorizontalOptions" Value="Center" />
                <Setter Property="VerticalOptions" Value="CenterAndExpand" />
            </Style>
        </ResourceDictionary>
    </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>

Observe que quando HslColorExtension é uma marca XML, as quatro propriedades são definidas como atributos, mas quando ela 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

Uma extensão de marcação para acessar bitmaps

O argumento para ProvideValue é um objeto que implementa a IServiceProvider interface, que é definida no namespace .NET System . Essa interface tem um membro, um método nomeado GetService com um Type argumento.

A ImageResourceExtension classe mostrada abaixo mostra um possível uso de e GetService para obter um IXmlLineInfoProvider objeto que pode fornecer informações de IServiceProvider linha e caractere indicando onde um erro específico foi detectado. Nesse caso, uma exceção é gerada quando a Source propriedade não foi definida:

[ContentProperty("Source")]
class ImageResourceExtension : IMarkupExtension<ImageSource>
{
    public string Source { set; get; }

    public ImageSource ProvideValue(IServiceProvider serviceProvider)
    {
        if (String.IsNullOrEmpty(Source))
        {
            IXmlLineInfoProvider lineInfoProvider = serviceProvider.GetService(typeof(IXmlLineInfoProvider)) as IXmlLineInfoProvider;
            IXmlLineInfo lineInfo = (lineInfoProvider != null) ? lineInfoProvider.XmlLineInfo : new XmlLineInfo();
            throw new XamlParseException("ImageResourceExtension requires Source property to be set", lineInfo);
        }

        string assemblyName = GetType().GetTypeInfo().Assembly.GetName().Name;
        return ImageSource.FromResource(assemblyName + "." + Source, typeof(ImageResourceExtension).GetTypeInfo().Assembly);
    }

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

ImageResourceExtension é útil quando um arquivo XAML precisa acessar um arquivo de imagem armazenado como um recurso inserido no projeto de biblioteca do .NET Standard. Ele usa a Source propriedade para chamar o método estático ImageSource.FromResource . Esse método requer um nome de recurso totalmente qualificado, que consiste no nome do assembly, no nome da pasta e no nome do arquivo separados por pontos. O segundo argumento para o ImageSource.FromResource método fornece o nome do assembly e só é necessário para builds de versão na UWP. Independentemente disso, deve ser chamado do assembly que contém o bitmap, o que significa que essa extensão de recurso XAML não pode fazer parte de uma biblioteca externa, ImageSource.FromResource a menos que as imagens também estejam nessa biblioteca. (Veja o Imagens incorporadas para obter mais informações sobre como acessar bitmaps armazenados como recursos inseridos.)

Embora ImageResourceExtension exija que a Source propriedade seja definida, a Source propriedade é indicada em um atributo como a propriedade de conteúdo da classe. Isso significa que a Source= parte da expressão entre chaves pode ser omitida. Na página Demonstração do recurso de imagem, os Image elementos buscam duas imagens usando o nome da pasta e o nome do arquivo separados por pontos:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MarkupExtensions"
             x:Class="MarkupExtensions.ImageResourceDemoPage"
             Title="Image Resource Demo">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Image Source="{local:ImageResource Images.SeatedMonkey.jpg}"
               Grid.Row="0" />

        <Image Source="{local:ImageResource Images.FacePalm.jpg}"
               Grid.Row="1" />

    </Grid>
</ContentPage>

Este é o programa em execução:

Demonstração de recursos de imagem

Provedores de Serviços

Usando o IServiceProvider argumento para ProvideValue, as extensões de marcação XAML podem obter acesso a informações úteis sobre o arquivo XAML no qual estão sendo usadas. Mas para usar o IServiceProvider argumento com sucesso, você precisa saber que tipo de serviços estão disponíveis em contextos específicos. A melhor maneira de entender esse recurso é estudando o código-fonte das extensões de marcação XAML existentes na pasta MarkupExtensions no Xamarin.Forms repositório no GitHub. Lembre-se de que alguns tipos de serviços são internos ao Xamarin.Forms.

Em algumas extensões de marcação XAML, esse serviço pode ser útil:

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

A interface IProvideValueTarget define duas propriedades, TargetObject e TargetProperty. Quando essa informação é obtida na ImageResourceExtension classe, TargetObject é o Image e TargetProperty é um BindableProperty objeto para a Source propriedade de Image. Essa é a propriedade na qual a extensão de marcação XAML foi definida.

A GetService chamada com um argumento de typeof(IProvideValueTarget) realmente retorna um objeto do tipo SimpleValueTargetProvider, que é definido no Xamarin.Forms.Xaml.Internals namespace. Se você converter o valor retornado de GetService para esse tipo, também poderá acessar uma ParentObjects propriedade, que é uma matriz que contém o Image elemento, o Grid pai e o ImageResourceDemoPage pai do Grid.

Conclusão

As extensões de marcação XAML desempenham um papel vital no XAML, estendendo a capacidade de definir atributos de várias fontes. Além disso, se as extensões de marcação XAML existentes não fornecerem exatamente o que você precisa, você também poderá escrever suas próprias.