Diccionarios de recursos
.NET Multi-platform App UI (.NET MAUI) ResourceDictionary es un repositorio para los recursos que usa una aplicación .NET MAUI. Los recursos típicos que se almacenan en ResourceDictionary incluyen estilos, plantillas de control, plantillas de datos, convertidores y colores.
Se puede hacer referencia a los recursos XAML almacenados en ResourceDictionary y aplicarlos a elementos mediante la extensión de marcado StaticResource
o DynamicResource
. En C#, los recursos también se pueden definir en ResourceDictionary y, a continuación, hacer referencia y aplicar a los elementos mediante un indexador basado en cadenas.
Sugerencia
En Visual Studio, un archivo ResourceDictionary basado en XAML respaldado por un archivo de código subyacente se puede agregar al proyecto mediante la plantilla de elemento .NET MAUI ResourceDictionary (XAML).
Crear recursos
Cada objeto derivado VisualElement tiene una propiedad Resources
, que es ResourceDictionary que puede contener recursos. Del mismo modo, un objeto derivado Application
tiene una propiedad Resources
, que es ResourceDictionary que puede contener recursos.
Una aplicación .NET MAUI solo puede contener una sola clase que deriva de Application
, pero a menudo hace uso de muchas clases que derivan de VisualElement, incluidas páginas, diseños y vistas. Cualquiera de estos objetos puede tener su propiedad Resources
establecida en un recurso contenedor ResourceDictionary. Elegir dónde colocar un ResourceDictionary determinado afecta dónde se pueden usar los recursos:
- Los recursos de ResourceDictionary adjunto a una vista, como Button o Label, solo se pueden aplicar a ese objeto en particular.
- Los recursos de ResourceDictionary adjunto a un diseño, como StackLayout o Grid, se pueden aplicar al diseño y a todos los elementos secundarios de ese diseño.
- Los recursos de ResourceDictionary definidos en el nivel de página se pueden aplicar a la página y a sus elementos secundarios.
- Los recursos de ResourceDictionary definidos en el nivel de aplicación se pueden aplicar en toda la aplicación.
A excepción de los estilos implícitos, cada recurso del diccionario de recursos debe tener una clave de cadena única definida con el atributo x:Key
.
En el código XAML siguiente se muestran los recursos definidos a nivel ResourceDictionary de aplicación en el archivo 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>
En este ejemplo, el diccionario de recursos define un recurso Thickness
, varios recursos Color y dos recursos implícitos Style.
Importante
La inserción de recursos directamente entre las etiquetas de elemento de propiedad Resources
crea automáticamente un objeto ResourceDictionary. Sin embargo, también es válido colocar todos los recursos entre etiquetas opcionales ResourceDictionary.
Consumo de recursos
Cada recurso tiene una clave que se especifica mediante el atributo x:Key
, que se convierte en su clave de diccionario en ResourceDictionary. La clave se usa para hacer referencia a un recurso de ResourceDictionary con la extensión de marcado XAML StaticResource
o DynamicResource
.
La extensión de marcado StaticResource
es similar a la extensión de marcado DynamicResource
en que ambas usan una clave de diccionario para hacer referencia a un valor de un diccionario de recursos. Sin embargo, mientras que la extensión de marcado StaticResource
realiza una búsqueda de diccionario única, la extensión de marcado DynamicResource
mantiene un vínculo a la clave de diccionario. Por lo tanto, si se reemplaza la entrada del diccionario asociada a la clave, el cambio se aplica al elemento visual. Esto permite realizar cambios en los recursos en tiempo de ejecución en una aplicación. Para obtener más información sobre las extensiones de marcado, consulta Extensiones de marcado XAML.
En el ejemplo XAML siguiente se muestra cómo consumir recursos y también definir un recurso adicional en 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>
En este ejemplo, el objeto ContentPage consume el estilo implícito definido en el diccionario de recursos a nivel de aplicación. El objeto StackLayout consume el recurso PageMargin
definido en el diccionario de recursos a nivel de aplicación, mientras que el objeto Button consume el estilo implícito definido en el diccionario de recursos StackLayout. El resultado es el aspecto que se muestra en las capturas de pantalla siguientes:
Importante
Los recursos que son específicos de una página no deberían incluirse en el diccionario de recursos a nivel de aplicación, dado que los recursos se analizarán en el inicio de la aplicación en lugar de cuando los solicite una página. Para más información, consulte Reducción del tamaño del diccionario de recursos de aplicación.
Comportamiento de búsqueda de recursos
El siguiente proceso de búsqueda se produce cuando se hace referencia a un recurso con la extensión de marcado StaticResource
o DynamicResource
:
- Se comprueba la clave solicitada en el diccionario de recursos, si existe, para el elemento que establece la propiedad. Si se encuentra la clave solicitada, se devuelve su valor y finaliza el proceso de búsqueda.
- Si no se encuentra una coincidencia, el proceso de búsqueda busca en el árbol visual hacia arriba y comprueba el diccionario de recursos de cada elemento primario. Si se encuentra la clave solicitada, se devuelve su valor y finaliza el proceso de búsqueda. De lo contrario, este proceso continúa hasta que se alcanza el elemento raíz.
- Si no se encuentra una coincidencia en el elemento raíz, se examina el diccionario de recursos a nivel de aplicación.
- Si todavía no se encuentra una coincidencia, se produce
XamlParseException
.
Por lo tanto, cuando el analizador XAML encuentra una extensión de marcado StaticResource
o DynamicResource
, busca una clave coincidente al desplazarse por el árbol visual, usando la primera coincidencia que encuentra. Si esta búsqueda finaliza en la página y la clave todavía no se encuentra, el analizador XAML busca ResourceDictionary adjunto al objeto App
. Si no se encuentra la clave, se genera una excepción.
Invalidar recursos
Cuando los recursos comparten claves, los recursos definidos en la parte inferior del árbol visual tendrán prioridad sobre los definidos en la parte superior. Por ejemplo,si se establece un recurso AppBackgroundColor
en AliceBlue
en el nivel de aplicación se invalidará mediante un recurso AppBackgroundColor
de nivel de página establecido en Teal
. Del mismo modo, un recurso AppBackgroundColor
de nivel de página se invalidará mediante un recurso AppBackgroundColor
de nivel de vista o diseño.
diccionarios de recursos independientes
También se puede crear ResourceDictionary como un archivo XAML independiente que no está respaldado por un archivo de código subyacente. Para crear un archivo ResourceDictionary independiente, agrega un nuevo archivo ResourceDictionary al proyecto con la plantilla del elemento .NET MAUI ResourceDictionary (XAML) y elimina su archivo de código subyacente. Después, en el archivo XAML, quita el atributo x:Class
de la etiqueta ResourceDictionary cerca del principio del archivo. Además, agrega <?xaml-comp compile="true" ?>
después del encabezado XML para asegurarte de que se compilará el XAML.
También se puede crear ResourceDictionary como un archivo XAML independiente que no está respaldado por un archivo de código subyacente. Para crear un archivo ResourceDictionary independiente, agrega un nuevo archivo ResourceDictionary al proyecto con la plantilla del elemento .NET MAUI ResourceDictionary (XAML) y elimina su archivo de código subyacente. Después, en el archivo XAML, quita el atributo x:Class
de la etiqueta ResourceDictionary cerca del principio del archivo. De forma predeterminada, un objeto ResourceDictionary independiente tiene su XAML compilado, a menos que se especifique <?xaml-comp compile="false" ?>
después del encabezado XML.
Nota:
Un ResourceDictionary independiente debe tener una acción de compilación de MauiXaml.
En el ejemplo de XAML siguiente se muestra un elemento ResourceDictionary independiente denominado 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>
En este ejemplo, ResourceDictionary contiene un único recurso, que es un objeto de tipo DataTemplate. MyResourceDictionary.xaml se puede consumir combinándolo en otro diccionario de recursos.
Combinar diccionarios de recursos
Los diccionarios de recursos se pueden combinar combinando uno o varios objetos ResourceDictionary en otro ResourceDictionary.
Combinar diccionarios de recursos locales
Un archivo ResourceDictionary local se puede combinar en otro ResourceDictionary creando un objeto ResourceDictionary cuya propiedad Source
se establece en el nombre de archivo XAML con los recursos:
<ContentPage ...>
<ContentPage.Resources>
<!-- Add more resources here -->
<ResourceDictionary Source="MyResourceDictionary.xaml" />
<!-- Add more resources here -->
</ContentPage.Resources>
...
</ContentPage>
Esta sintaxis no crea una instancia de la clase MyResourceDictionary
. En su lugar, hace referencia al archivo XAML. Por este motivo, al establecer la propiedad Source
, no se requiere un archivo de código subyacente y el atributo x:Class
se puede quitar de la etiqueta raíz del archivo MyResourceDictionary.xaml.
Importante
La propiedad ResourceDictionary.Source
solo se puede establecer desde XAML.
Combinar diccionarios de recursos de otros ensamblados
ResourceDictionary también se puede combinar en otro ResourceDictionary agregándolo a la propiedad MergedDictionaries
de ResourceDictionary. Esta técnica permite combinar diccionarios de recursos, independientemente del ensamblado en el que residen. La combinación de diccionarios de recursos de ensamblados externos requiere que ResourceDictionary tenga una acción de compilación establecida en en MauiXaml, tenga un archivo de código subyacente y defina el atributo x:Class
en la etiqueta raíz del archivo.
Advertencia
La clase ResourceDictionary también define una propiedad MergedWith
. Pero esta propiedad ha quedado en desuso y ya no debe usarse.
En el ejemplo de código siguiente se muestran dos diccionarios de recursos que se agregan a la colección MergedDictionaries
de un nivel 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>
En este ejemplo, un diccionario de recursos del mismo ensamblado y un diccionario de recursos de un ensamblado externo se combinan en el diccionario de recursos de nivel de página. Además, también puedes agregar otros objetos ResourceDictionary dentro de las etiquetas de elemento de propiedad MergedDictionaries
y otros recursos fuera de esas etiquetas.
Importante
Solo puede haber una etiqueta de elemento de propiedad MergedDictionaries
en un ResourceDictionary, pero puedes colocar tantos objetos ResourceDictionary allí como sea necesario.
Cuando los recursos ResourceDictionary combinados comparten valores de atributo x:Key
idénticos, .NET MAUI usa la siguiente prioridad de recursos:
- Los recursos locales para el diccionario de recursos.
- Los recursos contenidos en los diccionarios de recursos que se combinaron a través de la colección
MergedDictionaries
, en el orden inverso en el que aparecen en la propiedadMergedDictionaries
.
Sugerencia
La búsqueda de diccionarios de recursos puede ser una tarea intensiva desde el punto de vista computacional si una aplicación contiene varios diccionarios de recursos de gran tamaño. Por lo tanto, para evitar búsquedas innecesarias, asegúrate de que cada página de una aplicación solo use diccionarios de recursos adecuados para la página.
Consumo de un diccionario de recursos basado en XAML a partir de código
Los diccionarios de recursos definidos en XAML se pueden consumir en código, siempre que el objeto ResourceDictionary esté respaldado por un archivo de código subyacente. En Visual Studio, los archivos ResourceDictionary basados en XAML respaldados por archivos de código subyacente se pueden agregar al proyecto mediante la plantilla de elemento ResourceDictionary (XAML) de .NET MAUI:
Los diccionarios de recursos basados en XAML respaldados por archivos de código subyacente se pueden consumir desde C# agregándolos a la colección MergedDictionaries del diccionario de recursos:
Resources.MergedDictionaries.Add(new MyMauiApp.Resources.Styles.MyColors());
Resources.MergedDictionaries.Add(new MyMauiApp.Resources.Styles.MyStyles());
Acceso a los recursos por clave desde código
Puedes acceder a los recursos de un diccionario de recursos desde código como cualquier otro diccionario.
En el ejemplo siguiente se muestra cómo recuperar y aplicar un recurso desde el diccionario de recursos de una 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;
}
Este es el enfoque recomendado que garantiza que .NET MAUI no inicie KeyNotFoundException
si no puede recuperar un recurso del código. Esto puede ocurrir cuando un diccionario de recursos combinado se compone de recursos definidos en un archivo XAML y recursos insertados. Para más información, vea el problema de GitHub n.º 11214.
Nota:
Para recuperar los recursos de toda la aplicación del código, accede al diccionario de recursos App.Current.Resources
.