Compartilhar via


Extensões de marcação XAML

Procurar amostra. Procurar no exemplo

As extensões de marcação XAML do .NET MAUI (.NET Multi-platform App UI) permitem que as propriedades sejam definidas como objetos ou valores referenciados indiretamente de outras fontes. As extensões de marcação XAML são particularmente importantes para compartilhar objetos e referenciar constantes usadas em um aplicativo, mas encontram sua maior utilidade em associações de dados.

Normalmente, você usa XAML para definir propriedades de um objeto como valores explícitos, como uma cadeia de caracteres, um número, um membro de enumeração ou uma cadeia de caracteres que é convertida em um valor nos bastidores. Às vezes, no entanto, as propriedades devem fazer referência a valores definidos em outro lugar ou que podem exigir um pouco de processamento por código em runtime. Para essas finalidades, extensões de marcação XAML estão disponíveis.

As extensões de marcação XAML são assim chamadas porque são apoiadas por código em classes que implementam IMarkupExtension. Também é possível escrever suas próprias extensões de marcação personalizadas.

Em muitos casos, as extensões de marcação XAML são instantaneamente reconhecíveis em arquivos XAML porque aparecem como valores de atributo delimitados por chaves, { e }, mas às vezes as extensões de marcação também aparecem na marcação como elementos convencionais.

Importante

As extensões de marcação podem ter propriedades, mas não são definidas como atributos XML. Em uma extensão de marcação, as configurações de propriedade são separadas por vírgulas e nenhuma aspa aparece entre chaves.

Recursos compartilhados

Algumas páginas XAML contêm vários modos de exibição com propriedades definidas com os mesmos valores. Por exemplo, muitas das configurações de propriedade para esses objetos Button são as mesmas:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">
    <StackLayout>
        <Button Text="Do this!"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                BorderWidth="3"
                Rotation="-15"
                TextColor="Red"
                FontSize="24" />
        <Button Text="Do that!"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                BorderWidth="3"
                Rotation="-15"
                TextColor="Red"
                FontSize="24" />
        <Button Text="Do the other thing!"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                BorderWidth="3"
                Rotation="-15"
                TextColor="Red"
                FontSize="24" />
    </StackLayout>
</ContentPage>

Se uma dessas propriedades precisar ser alterada, talvez você prefira fazer a alteração apenas uma vez em vez de três vezes. Se isso fosse código, você provavelmente estaria usando constantes e objetos estáticos de somente leitura para ajudar a manter esses valores consistentes e fáceis de modificar.

Em XAML, uma solução popular é armazenar esses valores ou objetos em um dicionário de recursos. A classe VisualElement define uma propriedade chamada Resources do tipo ResourceDictionary, que é um dicionário com chaves do tipo string e valores do tipo object. Você pode colocar objetos nesse dicionário e, em seguida, referenciá-los a partir da marcação, tudo em XAML.

Para usar um dicionário de recursos em uma página, inclua um par de marcas de elemento de propriedade Resources na parte superior da página e adicione recursos dentro dessas marcas. Objetos e valores de vários tipos podem ser adicionados ao dicionário de recursos. Esses tipos devem ser instanciáveis. Eles não podem ser classes abstratas, por exemplo. Esses tipos devem ter um construtor público sem parâmetros. Cada item requer uma chave de dicionário especificada com o atributo x:Key:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">
    <ContentPage.Resources>
        <LayoutOptions x:Key="horzOptions"
                       Alignment="Center" />
        <LayoutOptions x:Key="vertOptions"
                       Alignment="Center" />
    </ContentPage.Resources>
    ...
</ContentPage>

Neste exemplo, os dois recursos são valores do tipo de estrutura LayoutOptions e cada um tem uma chave exclusiva e uma ou duas propriedades definidas. Em código e marcação, é muito mais comum usar os campos estáticos de LayoutOptions, mas aqui é mais conveniente definir as propriedades.

Observação

Marcas opcionais ResourceDictionary podem ser incluídas como filho das marcas Resources.

Os recursos podem ser consumidos pelos objetos Button usando a extensão de marcação StaticResource XAML para definir suas propriedades HorizontalOptions e VerticalOptions:

<Button Text="Do this!"
        HorizontalOptions="{StaticResource horzOptions}"
        VerticalOptions="{StaticResource vertOptions}"
        BorderWidth="3"
        Rotation="-15"
        TextColor="Red"
        FontSize="24" />

A extensão de marcação StaticResource é sempre delimitada com chaves e inclui a chave do dicionário. O nome StaticResource o distingue de DynamicResource, que o .NET MAUI também suporta. DynamicResource é para chaves de dicionário associadas a valores que podem ser alterados em runtime, enquanto StaticResource acessa elementos do dicionário apenas uma vez quando os elementos na página são construídos. Sempre que o analisador XAML encontra uma extensão de marcação StaticResource, ele pesquisa a árvore visual e usa a primeira ResourceDictionary que encontrar contendo essa chave.

É necessário armazenar doubles no dicionário para as propriedades BorderWidth, Rotation, e FontSize. O XAML define marcas convenientemente para tipos de dados comuns, como x:Double e x:Int32:

<ContentPage.Resources>
        <LayoutOptions x:Key="horzOptions"
                       Alignment="Center" />
        <LayoutOptions x:Key="vertOptions"
                       Alignment="Center" />
        <x:Double x:Key="borderWidth">3</x:Double>
        <x:Double x:Key="rotationAngle">-15</x:Double>
        <x:Double x:Key="fontSize">24</x:Double>        
</ContentPage.Resources>

Esses três recursos adicionais podem ser referenciados da mesma forma que os valores LayoutOptions:

<Button Text="Do this!"
        HorizontalOptions="{StaticResource horzOptions}"
        VerticalOptions="{StaticResource vertOptions}"
        BorderWidth="{StaticResource borderWidth}"
        Rotation="{StaticResource rotationAngle}"
        TextColor="Red"
        FontSize="{StaticResource fontSize}" />

Para recursos do tipo Color, você pode usar as mesmas representações de cadeia de caracteres que você usa ao atribuir diretamente atributos desses tipos. Os conversores de tipo incluídos no .NET MAUI são invocados quando o recurso é criado. Também é possível usar a classe OnPlatform no dicionário de recursos para definir valores diferentes para as plataformas. O exemplo a seguir usa essa classe para definir cores de texto diferentes:

<OnPlatform x:Key="textColor"
            x:TypeArguments="Color">
    <On Platform="iOS" Value="Red" />
    <On Platform="Android" Value="Aqua" />
</OnPlatform>

O recurso OnPlatform obtém um atributo x:Key porque é um objeto no dicionário, e um atributo x:TypeArguments porque é uma classe genérica. Os atributos iOS e Android são convertidos em valores Color quando o objeto é inicializado.

O exemplo a seguir mostra os três botões acessando seis valores compartilhados:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">
    <ContentPage.Resources>
        <LayoutOptions x:Key="horzOptions"
                       Alignment="Center" />
        <LayoutOptions x:Key="vertOptions"
                       Alignment="Center" />
        <x:Double x:Key="borderWidth">3</x:Double>
        <x:Double x:Key="rotationAngle">-15</x:Double>
        <x:Double x:Key="fontSize">24</x:Double>    
        <OnPlatform x:Key="textColor"
                    x:TypeArguments="Color">
            <On Platform="iOS" Value="Red" />
            <On Platform="Android" Value="Aqua" />
            <On Platform="WinUI" Value="#80FF80" />
        </OnPlatform>
    </ContentPage.Resources>

    <StackLayout>
        <Button Text="Do this!"
                HorizontalOptions="{StaticResource horzOptions}"
                VerticalOptions="{StaticResource vertOptions}"
                BorderWidth="{StaticResource borderWidth}"
                Rotation="{StaticResource rotationAngle}"
                TextColor="{StaticResource textColor}"
                FontSize="{StaticResource fontSize}" />
        <Button Text="Do that!"
                HorizontalOptions="{StaticResource horzOptions}"
                VerticalOptions="{StaticResource vertOptions}"
                BorderWidth="{StaticResource borderWidth}"
                Rotation="{StaticResource rotationAngle}"
                TextColor="{StaticResource textColor}"
                FontSize="{StaticResource fontSize}" />
        <Button Text="Do the other thing!"
                HorizontalOptions="{StaticResource horzOptions}"
                VerticalOptions="{StaticResource vertOptions}"
                BorderWidth="{StaticResource borderWidth}"
                Rotation="{StaticResource rotationAngle}"
                TextColor="{StaticResource textColor}"
                FontSize="{StaticResource fontSize}" />
    </StackLayout>
</ContentPage>

A captura de tela a seguir verifica o estilo consistente:

Captura de tela dos controles estilizados.

Embora seja comum definir a coleção Resources na parte superior da página, você pode ter coleções Resources em outros elementos da página. Por exemplo, o exemplo a seguir mostra recursos adicionados a um StackLayout:

<StackLayout>
    <StackLayout.Resources>
        <Color x:Key="textColor">Blue</Color>
    </StackLayout.Resources>
    ...
</StackLayout>

Um dos tipos mais comuns de objetos armazenados em dicionários de recursos é o Style do .NET MAUI, que define uma coleção de configurações de propriedade. Para obter mais informações sobre estilos, consulte Criar o estilo de aplicativos usando XAML.

Observação

A finalidade de um dicionário de recursos é compartilhar objetos. Portanto, não faz sentido colocar controles como um Label ou Button em um dicionário de recursos. Os elementos visuais não podem ser compartilhados porque a mesma instância não pode aparecer duas vezes em uma página.

Extensão de marcação x:Static

Além da extensão de marcação StaticResource, há também uma extensão de marcação x:Static. No entanto, embora StaticResource retorne um objeto de um dicionário de recursos, x:Static acessa um campo estático público, uma propriedade estática pública, um campo constante público ou um membro de enumeração.

Observação

A extensão de marcação StaticResource é suportada por implementações XAML que definem um dicionário de recursos, enquanto x:Static é uma parte intrínseca do XAML, como o prefixo x revela.

O exemplo a seguir demonstra como x:Static consegue fazer referência explícita a campos estáticos e membros de enumeração:

<Label Text="Hello, XAML!"
       VerticalOptions="{x:Static LayoutOptions.Start}"
       HorizontalTextAlignment="{x:Static TextAlignment.Center}"
       TextColor="{x:Static Colors.Aqua}" />

O principal uso da extensão de marcação x:Static é referenciar campos estáticos ou propriedades de seu próprio código. Por exemplo, aqui está uma classe AppConstants que contém alguns campos estáticos que você pode querer usar em várias páginas em um aplicativo:

namespace XamlSamples
{
    static class AppConstants
    {
        public static readonly Color BackgroundColor = Colors.Aqua;
        public static readonly Color ForegroundColor = Colors.Brown;
    }
}

Para fazer referência aos campos estáticos dessa classe em um arquivo XAML, você precisa usar uma declaração de namespace de XML para indicar onde esse arquivo está localizado. Cada declaração de namespace de XML adicional define um novo prefixo. Para acessar classes locais para o namespace do aplicativo raiz, como AppConstants, você pode usar o prefixo local. A declaração de namespace deve indicar o nome do namespace CLR (Common Language Runtime), também conhecido como nome do namespace do .NET, que é o nome que aparece em uma definição de C# namespace ou em uma diretiva using:

xmlns:local="clr-namespace:XamlSamples"

Você também pode definir declarações de namespace de XML para namespaces .NET. Por exemplo, aqui está prefixo sys para o namespace padrão do .NET System que está no assembly netstandard. Como este é outro assembly, você também deve especificar o nome do assembly, neste caso netstandard:

xmlns:sys="clr-namespace:System;assembly=netstandard"

Observação

A palavra-chave clr-namespace é seguida por dois-pontos e, em seguida, o nome do namespace do .NET, seguido por um ponto-e-vírgula, a palavra-chave assembly, um sinal de igual e o nome do assembly.

Os campos estáticos podem ser consumidos após a declaração do namespace de XML:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:XamlSamples"
             xmlns:sys="clr-namespace:System;assembly=netstandard"
             x:Class="XamlSamples.StaticConstantsPage"
             Title="Static Constants Page"
             Padding="5,25,5,0">
    <StackLayout>
       <Label Text="Hello, XAML!"
              TextColor="{x:Static local:AppConstants.BackgroundColor}"
              BackgroundColor="{x:Static local:AppConstants.ForegroundColor}"
              FontAttributes="Bold"
              FontSize="30"
              HorizontalOptions="Center" />
      <BoxView WidthRequest="{x:Static sys:Math.PI}"
               HeightRequest="{x:Static sys:Math.E}"
               Color="{x:Static local:AppConstants.ForegroundColor}"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand"
               Scale="100" />
    </StackLayout>
</ContentPage>

Neste exemplo, as dimensões BoxView são definidas como Math.PI e Math.E, mas dimensionadas por um fator de 100:

Captura de tela dos controles usando a extensão de marcação x:Static.

Outras extensões de marcação

Várias extensões de marcação são intrínsecas ao XAML e têm suporte no .NET MAUI XAML. Alguns delas não são usadas com muita frequência, mas são essenciais quando você precisa delas:

  • Se uma propriedade tiver um valor não null por padrão, mas você quiser defini-lo como null, defina-o como a extensão de marcação {x:Null}.
  • Se uma propriedade for do tipo Type, você poderá atribuí-la a um objeto Type usando a extensão de marcação {x:Type someClass}.
  • Você pode definir matrizes em XAML usando a extensão de marcação x:Array. Essa extensão de marcação tem um atributo obrigatório chamado Type que indica o tipo dos elementos na matriz.

Para obter mais informações sobre extensões de marcação XAML, consulte Consumir extensões de marcação XAML.

Próximas etapas

As associações de dados do .NET MAUI permitem que as propriedades de dois objetos sejam vinculadas para que uma alteração em um cause uma alteração no outro.