Dicionários de recurso

Um ResourceDictionary do .NET MAUI (.NET Multi-Platform App UI) é um repositório de recursos usados por um aplicativo .NET MAUI. Os recursos típicos armazenados em um ResourceDictionary incluem estilos, modelos de controle, modelos de dados, conversores e cores.

Os recursos XAML armazenados em um ResourceDictionary podem ser referenciados e aplicados a elementos usando a extensão de marcação StaticResource ou DynamicResource. Em C#, os recursos também podem ser definidos em um ResourceDictionary e, em seguida, referenciados e aplicados a elementos usando um indexador baseado em cadeia de caracteres.

Dica

No Visual Studio, um arquivo ResourceDictionary baseado em XAML com suporte de um arquivo code-behind pode ser adicionado ao seu projeto pelo modelo de item .NET MAUI ResourceDictionary (XAML).

Criar recursos

Cada objeto derivado VisualElement tem uma propriedade Resources, que é um ResourceDictionary que pode conter recursos. Da mesma forma, um objeto derivado Application tem uma propriedade Resources, que é um ResourceDictionary que pode conter recursos.

Um aplicativo .NET MAUI pode conter apenas uma única classe derivada de Application, mas geralmente usa muitas classes derivadas de VisualElement, incluindo páginas, layouts e exibições. Qualquer um desses objetos pode ter sua propriedade Resources definida como um ResourceDictionary com recursos. Escolhendo onde colocar um determinado ResourceDictionary impacta onde os recursos podem ser usados:

  • Os recursos em um ResourceDictionary que está anexado a uma exibição, como Button ou Label, só podem ser aplicados a esse objeto específico.
  • Os recursos em um ResourceDictionary anexado a um layout, como StackLayout ou Grid, podem ser aplicados ao layout e a todos os filhos desse layout.
  • Recursos em um ResourceDictionary definido no nível da página podem ser aplicados à página e seus filhos.
  • Recursos em um ResourceDictionary definido no nível do aplicativo podem ser aplicados em todo o aplicativo.

Com exceção dos estilos implícitos, cada recurso no dicionário de recursos deve ter uma chave de cadeia de caracteres exclusiva definida com o atributo x:Key.

O XAML a seguir mostra os recursos definidos em um ResourceDictionary de nível de aplicativo no arquivo App.xaml:

<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ResourceDictionaryDemo.App">
    <Application.Resources>

        <Thickness x:Key="PageMargin">20</Thickness>

        <!-- Colors -->
        <Color x:Key="AppBackgroundColor">AliceBlue</Color>
        <Color x:Key="NavigationBarColor">#1976D2</Color>
        <Color x:Key="NavigationBarTextColor">White</Color>
        <Color x:Key="NormalTextColor">Black</Color>

        <!-- Images -->
        <x:String x:Key="BackgroundImage">background</x:String>
        <x:String x:Key="MenuIcon">menu.png</x:String>
        <x:String x:Key="SearchIcon">search.png</x:String>

        <!-- Implicit styles -->
        <Style TargetType="NavigationPage">
            <Setter Property="BarBackgroundColor"
                    Value="{StaticResource NavigationBarColor}" />
            <Setter Property="BarTextColor"
                    Value="{StaticResource NavigationBarTextColor}" />
        </Style>

        <Style TargetType="ContentPage"
               ApplyToDerivedTypes="True">
            <Setter Property="BackgroundColor"
                    Value="{StaticResource AppBackgroundColor}" />
        </Style>

    </Application.Resources>
</Application>

Neste exemplo, o dicionário de recursos define um recurso Thickness, vários recursos Color e dois recursos implícitos Style.

Importante

A inserção de recursos diretamente entre as marcas de elemento de propriedade Resources cria automaticamente um objeto ResourceDictionary. No entanto, também é válido colocar todos os recursos entre marcas ResourceDictionary opcionais.

Consumir recursos

Cada recurso tem uma chave que é especificada usando o atributo x:Key, que se torna sua chave de dicionário no ResourceDictionary. A chave é usada para fazer referência a um recurso do ResourceDictionary com a extensão de marcação XAML StaticResource ou DynamicResource.

A extensão de marcação StaticResource é semelhante à extensão de marcação DynamicResource, pois ambas usam uma chave de dicionário para fazer referência a um valor de um dicionário de recurso. No entanto, enquanto a extensão de marcação StaticResource executa uma única pesquisa de dicionário, a extensão de marcação DynamicResource mantém um link para a chave de dicionário. Portanto, se a entrada do dicionário associada à chave for substituída, a alteração será aplicada ao elemento visual. Isso permite que alterações de recurso de tempo de execução sejam feitas em um aplicativo. Para obter mais informações sobre extensões de marcação, consulte Extensões de marcação XAML.

O exemplo XAML a seguir mostra como consumir recursos e definir um recurso adicional em um StackLayout:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ResourceDictionaryDemo.MainPage"
             Title="Main page">
    <StackLayout Margin="{StaticResource PageMargin}"
                 Spacing="6">
        <StackLayout.Resources>
            <!-- Implicit style -->
            <Style TargetType="Button">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="BackgroundColor" Value="#1976D2" />
                <Setter Property="TextColor" Value="White" />
                <Setter Property="CornerRadius" Value="5" />
            </Style>
        </StackLayout.Resources>

        <Label Text="This app demonstrates consuming resources that have been defined in resource dictionaries." />
        <Button Text="Navigate"
                Clicked="OnNavigateButtonClicked" />
    </StackLayout>
</ContentPage>

Neste exemplo, o objeto ContentPage consome o estilo implícito definido no dicionário de recursos no nível do aplicativo. O objeto StackLayout consome o recurso PageMargin definido no dicionário de recursos no nível do aplicativo, enquanto o objeto Button consome o estilo implícito definido no dicionário de recursos StackLayout. Isso resulta na aparência mostrada na captura de tela a seguir:

Consumindo recursos do dicionário de recursos.

Importante

Recursos que são específicos de uma página não deve ser incluído no dicionário de recursos no nível do aplicativo, já que os recursos serão analisados na inicialização do aplicativo, e não quando exigido por uma página. Para obter mais informações, consulte Reduzir o tamanho do dicionário de recursos do aplicativo.

Comportamento de pesquisa de recursos

O seguinte processo de pesquisa ocorre quando um recurso é referenciado com a extensão de marcação StaticResource ou DynamicResource:

  • A chave solicitada é verificada no dicionário de recursos, ou seja, se ela existe, para o elemento que define a propriedade. Se a chave solicitada for encontrada, seu valor será retornado e o processo de pesquisa será encerrado.
  • Se uma correspondência não for encontrada, o processo de pesquisa pesquisará a árvore visual de baixo para cima, verificando o dicionário de recursos de cada elemento pai. Se a chave solicitada for encontrada, seu valor será retornado e o processo de pesquisa será encerrado. Caso contrário, o processo continua para cima até que o elemento raiz seja alcançado.
  • Se uma correspondência não for encontrada no elemento raiz, o dicionário de recursos no nível do aplicativo será examinado.
  • Se uma correspondência ainda não for encontrada, uma XamlParseException será gerada.

Portanto, quando o analisador XAML encontra uma extensão de marcação StaticResource ou DynamicResource, ele procura uma chave correspondente viajando pela árvore visual, usando a primeira correspondência encontrada. Se essa pesquisa terminar na página e a chave ainda não tiver sido encontrada, o analisador XAML pesquisará o ResourceDictionary anexado ao objeto App. Se o fluxo não for encontrado, uma exceção será gerada.

Substituição de recursos

Quando os recursos compartilham chaves, os recursos definidos mais abaixo na árvore visual terão precedência sobre aqueles definidos mais acima. Por exemplo, a definição de um recurso AppBackgroundColor como AliceBlue no nível do aplicativo será substituída por um recurso de nível de página AppBackgroundColor definido como Teal. Da mesma forma, um recurso de nível de página AppBackgroundColor será substituído por um recurso AppBackgroundColor de nível de layout ou de exibição.

Dicionários de recursos autônomos

Um ResourceDictionary também pode ser criado como um arquivo XAML autônomo que não é apoiado por um arquivo code-behind. Para criar um ResourceDictionary autônomo, adicione um novo arquivo ResourceDictionary ao projeto com o modelo de item .NET MAUI ResourceDictionary (XAML) e exclua seu arquivo code-behind. Em seguida, no arquivo XAML, remova o atributo x:Class da marca ResourceDictionary perto do início do arquivo. Além disso, adicione <?xaml-comp compile="true" ?> após o cabeçalho XML para garantir que o XAML seja compilado.

Um ResourceDictionary também pode ser criado como um arquivo XAML autônomo que não é apoiado por um arquivo code-behind. Para criar um ResourceDictionary autônomo, adicione um novo arquivo ResourceDictionary ao projeto com o modelo de item .NET MAUI ResourceDictionary (XAML) e exclua seu arquivo code-behind. Em seguida, no arquivo XAML, remova o atributo x:Class da marca ResourceDictionary perto do início do arquivo. Por padrão, um ResourceDictionary autônomo tem seu XAML compilado, a menos que <?xaml-comp compile="false" ?> seja especificado após o cabeçalho XML.

Observação

Um ResourceDictionary autônomo deve ter uma ação de compilação de MauiXaml.

O exemplo XAML a seguir mostra um ResourceDictionary autônomo chamado MyResourceDictionary.xaml:

<?xml version="1.0" encoding="UTF-8" ?>
<ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
    <DataTemplate x:Key="PersonDataTemplate">
        <ViewCell>
            <Grid RowSpacing="6"
                  ColumnSpacing="6">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="0.5*" />
                    <ColumnDefinition Width="0.2*" />
                    <ColumnDefinition Width="0.3*" />
                </Grid.ColumnDefinitions>
                <Label Text="{Binding Name}"
                       TextColor="{StaticResource NormalTextColor}"
                       FontAttributes="Bold" />
                <Label Grid.Column="1"
                       Text="{Binding Age}"
                       TextColor="{StaticResource NormalTextColor}" />
                <Label Grid.Column="2"
                       Text="{Binding Location}"
                       TextColor="{StaticResource NormalTextColor}"
                       HorizontalTextAlignment="End" />
            </Grid>
        </ViewCell>
    </DataTemplate>
</ResourceDictionary>

Neste exemplo, o ResourceDictionary contém um único recurso, que é um objeto do tipo DataTemplate. MyResourceDictionary.xaml pode ser consumido mesclando-o em outro dicionário de recursos.

Mesclar dicionário de recursos

Os dicionários de recursos podem ser combinados mesclando um ou mais objetos ResourceDictionary em outro ResourceDictionary.

Mesclar dicionários de recursos locais

Um arquivo local ResourceDictionary pode ser mesclado em outro ResourceDictionary criando um objeto ResourceDictionary cuja propriedade Source é definida como o nome do arquivo XAML com os recursos:

<ContentPage ...>
    <ContentPage.Resources>
        <!-- Add more resources here -->
        <ResourceDictionary Source="MyResourceDictionary.xaml" />
        <!-- Add more resources here -->
    </ContentPage.Resources>
    ...
</ContentPage>

Essa sintaxe não instancia a classe MyResourceDictionary. Em vez disso, ele faz referência ao arquivo XAML. Por esse motivo, ao definir a propriedade Source, um arquivo code-behind não é necessário e o atributo x:Class pode ser removido da marca raiz do arquivo MyResourceDictionary.xaml.

Importante

A propriedade ResourceDictionary.Source só pode ser definida a partir de XAML.

Mesclar dicionários de recursos de outros assemblies

Um ResourceDictionary também pode ser mesclado em outro ResourceDictionary ao adicioná-lo à propriedade MergedDictionaries do ResourceDictionary. Essa técnica permite que dicionários de recursos sejam mesclados, independentemente do assembly em que residem. A mesclagem de dicionários de recursos de assemblies externos requer que o ResourceDictionary tenha uma ação de compilação definida como MauiXaml, um arquivo code-behind e que defina o atributo x:Class na marca raiz do arquivo.

Aviso

A classe ResourceDictionary também define uma propriedade MergedWith. No entanto, essa propriedade foi preterida e não deve mais ser usada.

O exemplo de código a seguir mostra dois dicionários de recursos sendo adicionados à coleção MergedDictionaries de um nível de página ResourceDictionary:

<ContentPage ...
             xmlns:local="clr-namespace:ResourceDictionaryDemo"
             xmlns:theme="clr-namespace:MyThemes;assembly=MyThemes">
    <ContentPage.Resources>
        <ResourceDictionary>
            <!-- Add more resources here -->
            <ResourceDictionary.MergedDictionaries>
                <!-- Add more resource dictionaries here -->
                <local:MyResourceDictionary />
                <theme:DefaultTheme />
                <!-- Add more resource dictionaries here -->
            </ResourceDictionary.MergedDictionaries>
            <!-- Add more resources here -->
        </ResourceDictionary>
    </ContentPage.Resources>
    ...
</ContentPage>

Neste exemplo, um dicionário de recursos do mesmo assembly e um dicionário de recursos de um assembly externo são mesclados no dicionário de recursos no nível da página. Além disso, você também pode adicionar outros objetos ResourceDictionary dentro das marcas de elemento de propriedade MergedDictionaries e outros recursos fora dessas marcas.

Importante

Pode haver apenas uma marca de elemento de propriedade MergedDictionaries em um ResourceDictionary, mas você pode colocar quantos objetos ResourceDictionary forem necessários.

Quando os recursos mesclados ResourceDictionary compartilham valores de atributo idênticos x:Key, o .NET MAUI usa a seguinte precedência de recurso:

  1. Os recursos locais para o dicionário de recursos.
  2. Os recursos contidos nos dicionários de recursos que foram mesclados por meio da coleção MergedDictionaries, na ordem inversa são listados na propriedade MergedDictionaries.

Dica

A pesquisa de dicionários de recursos pode ser uma tarefa computacionalmente intensiva se um aplicativo contiver vários dicionários de recursos grandes. Portanto, para evitar pesquisas desnecessárias, você deve garantir que cada página em um aplicativo use apenas dicionários de recursos apropriados para a página.

Consumir um dicionário de recursos baseado em XAML do código

Os dicionários de recursos definidos em XAML podem ser consumidos no código, desde que ResourceDictionary seja apoiado por um arquivo code-behind. No Visual Studio, os arquivos baseados em XAML ResourceDictionary que são apoiados por arquivos code-behind podem ser adicionados ao seu projeto pelo modelo de item .NET MAUI ResourceDictionary (XAML):

Captura de tela de dicionários de recursos apoiados por code-behind.

Os dicionários de recursos baseados em XAML que são apoiados por arquivos code-behind podem ser consumidos do C# adicionando-os à coleção MergedDictionaries do dicionário de recursos:

Resources.MergedDictionaries.Add(new MyMauiApp.Resources.Styles.MyColors());
Resources.MergedDictionaries.Add(new MyMauiApp.Resources.Styles.MyStyles());

Acessar recursos por chave do código

Você pode acessar recursos em um dicionário de recursos a partir do código como qualquer outro dicionário.

Este exemplo mostra como recuperar e aplicar um recurso do dicionário de recursos de uma página:

// Retrieve the Primary color value which is in the page's resource dictionary
var hasValue = Resources.TryGetValue("Primary", out object primaryColor);

if (hasValue)
{
    myLabel.TextColor = (Color)primaryColor;
}

Essa é a abordagem recomendada que garante que o .NET MAUI não gere um KeyNotFoundException se não for possível recuperar um recurso do código. Isso pode ocorrer quando um dicionário de recursos mesclado é composto de recursos definidos em um arquivo XAML e recursos embutidos. Para obter mais informações, consulte Problema do GitHub nº 11214.

Observação

Para recuperar recursos de todo o aplicativo do código, acesse o dicionário de recursos App.Current.Resources.