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 deIMarkupExtension<Type>
.ArrayExtension
deriva deIMarkupExtension<Array>
.DynamicResourceExtension
deriva deIMarkupExtension<DynamicResource>
.BindingExtension
deriva deIMarkupExtension<BindingBase>
.ConstraintExpression
deriva deIMarkupExtension<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 BoxView
arquivo :
<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:
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:
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.