Pues eso… es alqo que me ha estado dando un poco la lata en las aplicaciones de Xamarin.Forms, y es acceder a los recursos definidos en el fichero App.xaml tanto dentro de Application.Resources como en las definiciones de ResourceDictionary.
Aquí te explico cómo hacerlo en los dos casos, además de usar un fichero externo con las definiciones de los recursos.
Contenido:
Empezaré con las definiciones directas en los recursos de App.xaml.
Acceder desde código a las definiciones de recursos definidos en App.xaml
—
Acceder desde código a las definiciones de Application.Resources
Para acceder a los recursos definidos en Application.Resources desde código es simple (al menos cuando se sabe cómo 😉 ).
Por ejemplo, si tenemos dos recursos definidos, uno ColorAzul1 y el otro ColorAzul2, de esta forma:
<Application.Resources>
<Color x:Key="ColorAzul">#0073cf</Color>
<Color x:Key="ColorRojo">Firebrick</Color>
</Application.Resources>
Si queremos usar uno u otro desde código dependiendo de alguna condición, por ejemplo que estemos usando la app en Android o en iOS y que ocurra alguna otra condición (ver nota), podemos hacerlo de esta forma:
if (esUnaPrueba)
LabelInternet.TextColor = (Color)Application.Current.Resources["ColorAzul"];
else
LabelInternet.TextColor = (Color)Application.Current.Resources["ColorRojo"];
Como ves en el código a los recursos definidos en Application.Resources se accede usando Application.Current.Resources["key del recurso"];
El cast usando en el código de ejemplo es necesario ya que el valor devuelto por ese elemento de la colección Resources es de tipo object.
Esa forma de hacerlo la conocí gracias a esta entrada en los foros de Xamarin.
Nota: Lo de que ocurra alguna otra condición es porque en los recursos podemos indicar para qué plataforma lo definimos. Aunque para el caso de los colores no sé muy bien como se haría, para el resto puedes ver esto.
—
Acceder desde código a las definiciones de ResourceDictionary
Por otro lado, si el recurso lo tenemos en una rama de ResourceDictionary, por ejemplo de esta forma:
<ResourceDictionary>
<Color x:Key="Color2Azul">#0073cf</Color>
<Color x:Key="Color2Rojo">Firebrick</Color>
</ResourceDictionary>
</Application.Resources>
Para acceder a esos recursos definidos en ResourceDictionary desde el código de C# lo tendrás que hacer de esta otro forma:
if (esAzul)
LabelInternet2.SetDynamicResource(Label.TextColorProperty, "Color2Azul");
else
LabelInternet2.SetDynamicResource(Label.TextColorProperty, "Color2Rojo");
El truco para usar esa otro forma, lo saqueé del mismo post que te indiqué antes, pero estaba abajo del todo, concretamente de aquí.
A lo mejor hay otra forma, pero… a mí me ha servido esta que te muestro.
Ahora toca explicarte cómo agregar ficheros de recursos (ResourceDictionary) y usarlos directamente en los recursos del fichero App.Xaml.
Definir recursos (ResourceDictionary) en fichero separado y usarlo en App.xaml
También puedes usar esos ficheros de recursos desde una página concreta.
Ahora también te lo explicaré, que a mí me da mucho coraje cuando se comenta algo así en un post y después no se explica porque se supone que se sabe cómo hacerlo… y aunque puede que sepas cómo hacerlo (eso hasta yo lo sabía hace unos cuantos años cuando empecé con todo esto de XAML, hará al menos 16 años), puede que hayas encontrado este post al buscar cómo hacer eso concretamente y no el resto de cosas que estoy explicando.
Crear un fichero XAML de recursos del tipo ResourceDictionary
En el proyecto añade un nuevo fichero del tipo XAML, yo no he visto cómo agregar uno directamente desde la ventana de agregar nuevo elemento al proyecto, así que, puedes agregar un fichero de texto y después cambiarle la extensión de .txt a .xaml 😉
En la figura 1 puedes ver más claro cómo agregar ese fichero.
Nota: Después de agregar el fichero y cambiar la extensión (ya sea a la hora de crearlo como es el ejemplo de la figura 1 o si le cambias posteriormente la extensión, es recomendable cerrarlo y después abrirlo, si no, el Intellisense no se cosca de que es del tipo que tiene la extensión. Esto también es válido para ficheros de código al que inicialmente lo has creado como .TXT y después lo cambias a .CS.
Ese fichero estará vacío, así que… habrá que ponerle algo de código, concretamente este que te muestro para que sea un fichero válido del tipo ResourceDictionary.
<?xml version="1.0" encoding="utf-8"?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
</ResourceDictionary>
Aquí voy a definir algunas cosas para ver cómo funciona, tanto en modo diseño (en el código XAML de las páginas) como desde el código (en estos ejemplos solo de C# que es lo que me permite usar loss proyectos de Xamarin.Forms.
Aunque lo mismo hago pruebas con aplicaciones del tipo UWP que ahí sí se pueden crear proyetos de C# o de mi querido Visual Basic 😉
En ese fichero de recursos agrega las siguientes definiciones de colores y estilos de etiquetas para que quede como te muestro a continuación.
<?xml version="1.0" encoding="utf-8"?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<Color x:Key="Color3Azul">#0073cf</Color>
<Color x:Key="Color3Rojo">Firebrick</Color>
<Color x:Key="Color3Blanco">White</Color>
<Style x:Key="LabelEjemplo3" TargetType="{x:Type Label}">
<Setter Property="FontSize" Value="Small"/>
<Setter Property="FontAttributes" Value="None" />
<Setter Property="HorizontalTextAlignment" Value="Start"/>
<Setter Property="HorizontalOptions" Value="FillAndExpand"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="TextColor" Value="Green"/>
<Setter Property="BackgroundColor" Value="{DynamicResource Key=Color3Blanco}"/>
</Style>
<Style x:Key="LabelEjemplo3Azul" TargetType="{x:Type Label}">
<Setter Property="FontSize" Value="Small"/>
<Setter Property="FontAttributes" Value="Italic" />
<Setter Property="HorizontalTextAlignment" Value="Start"/>
<Setter Property="HorizontalOptions" Value="FillAndExpand"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="TextColor" Value="{StaticResource Color3Azul}"/>
<Setter Property="BackgroundColor" Value="{StaticResource Color3Blanco}"/>
</Style>
<Style x:Key="LabelEjemplo3Rojo" TargetType="{x:Type Label}">
<Setter Property="FontSize" Value="Small"/>
<Setter Property="FontAttributes" Value="Bold" />
<Setter Property="HorizontalTextAlignment" Value="Start"/>
<Setter Property="HorizontalOptions" Value="FillAndExpand"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="TextColor" Value="{StaticResource Color3Rojo}"/>
<Setter Property="BackgroundColor" Value="{StaticResource Color3Blanco}"/>
</Style>
</ResourceDictionary>
—
Usar un fichero de recursos (ResourceDictionary) desde una página
Y con eso puedes hacer lo siguiente para incluir el fichero de recursos en una página concreta.
Se supone que la página está en una carpeta (en mi caso en la carpeta elGuille) y el fichero de recursos está en el raíz del proyecto. Por tanto tengo que usar ../ para acceder al fichero que está en un path superior. Esto es como cuando accedes a los ficheros, así que no creo que tengas problemas para entenderlo… o eso espero, porque en esto sí que no voy a profundizar 😉
<ContentPage.Resources>
<ResourceDictionary Source="../ResourceDictionaryElGuille.xaml"/>
</ContentPage.Resources>
Nota: Si estuviera en otra carpeta, tendrás que indicarlo en el path de la propiedad Source. Por ejemplo si la carpeta está en el raíz del proyecto y se llama Recursos, el valor de Source quedaría así: Source
="/Recursos/ResourceDictionary2.xaml"
.
Usar por código un recurso (estilo) definido en ResourceDictionary.
Para usar, en este ejemplo, un estilo definido, habría que hacerlo de esta forma:
if (usarEstiloAzul)
LabelInternet3.SetDynamicResource(Label.StyleProperty, "LabelEjemplo3Azul");
else
LabelInternet3.SetDynamicResource(Label.StyleProperty, "LabelEjemplo3Rojo");
Es decir, como en el caso de los colores, pero en lugar de usar TextColorProperty, es decir, una propiedad, uso StyleProperty para indicar que es un estilo lo que quiero aplicar desde el fichero de recursos.
Por último voy a explicarte cómo agregar ese fichero de recursos (ResourceDictionary) al fichero App.xaml y así poder usarlo en toda la aplicación.
Usar un fichero de recursos (ResourceDictionary) en App.xaml
Seguramente esto parece no tener mucho sentido, ya que si defines los recursos directamente en el fichero App.xaml ¿por qué hacerlo en otro fichero?
En mi caso, la respuesta es la siguiente:
Yo tengo un proyecto de Xamarin Forms que define las clases a usar en la aplicación móvil (Android, iOS y UWP), llamaré Proyecto1 a ese proyecto base (el que se usa desde los proyectos específicos de cada plataforma).
Ahora creo otro proyecto móvil (que llamaré Proyecto2) que tiene también como punto de entrada el fichero App (es lo habitual) y que usa la DLL del proyecto anterior (Proyecto1). Lo hago así, porque la funcionalidad es la misma, pero por circunstacias que no viene al caso profundizar mucho en ellas, tuve que crear otro proyecto móvil (el que usa lo definido en Proyecto2), pero para no repetir todo el código que ya tengo hecho, añadí una referencia a Proyecto1 en el Proyecto2.
Y lo curioso es que aunque las clases definidas en Proyecto1 se supone que utiliza los recursos que haya en el App.xaml del Proyecto1, al ejecutar la aplicación (que usa Proyecto2) me da error de que no encuentra los recursos, por tanto, tuve que copiar todas las definiciones del fichero App.xaml del Proyecto1 en el App.xaml del Proyecto2.
Hasta aquí todo bien, sin problemas. ¿No?
Pero ahora decido añadir un nuevo estilo o cambiar alguno de los que hay… ¿Qué tengo que hacer? Pues muy fácil: MODIFICAR ambos ficheros App.xaml con esos estilos modificados.
Y la verdad es que para un despistao como yo, eso es unproblema, ya que me puedo tirar un buen rato comprobando porqué no funciona con los nuevos estilos definidos… Hasta caer en la cuenta de que no están definidos en los dos ficheros.
Sí, así de torpe es elGuille 😉
¿Solución? Crear un fichero en el que estén todos los recursos y ese fichero usarlo en los dos proyectos. Y eso es lo que he hecho y como he visto que en la rede de redes no está explicado cómo hacer esto, seguramente porque es algo trivial o fácil, pero la cuestión es que para mí no era tan trivial ni fácil, y como no encontraba la solución… me puse a probar con lo que ya había encontrado y… ahora ya lo tengo claro y para que tú también lo tengas claro, si es que necesitas hacerlo, aquí te lo explico.
En realidad la solución es fácil, lo que me traía de cabeza es que usando esas definiciones con esto: Application.Current.Resources["estilo"]
, no me funciona (y eso aún no lo he solucionado), así que, tuve que usar lo que te he comentado antes de aplicar los estilos que están en una declaración ResourceDictionary (Acceder desde código a las definiciones de ResourceDictionary), es decir, usando esto: Control.SetDynamicResource(TipoControl.StyleProperty, "estilo")
, ya que al fin y al cabo, esos recursos definidos en otro fichero en realidad están definidos como ResourceDictionary.
Vamos con los ejemplos de código, empezando por el código XAML a usar en App.xaml:
<Application.Resources>
<ResourceDictionary Source="ResourceDictionaryElGuille.xaml"/>
Es decir, se hace de la misma forma que para añadirlo a una página normal.
En este caso no indico ningún path en Source porque ese fichero de recursos está en el mimo directorio que App.xaml.
Si ves el código fuente en GitHub (es el mismo proyecto que los ejemplos de los Trucos para Xamarin.Forms y .NET MAUI en dispositivos) comprobarás que en App.xaml he dejado ese código anterior (el de usar un ResourceDictionary definido en otro fichero) además de otras definiciones que también están en bloques ResourceDictionary.
–
A tener en cuenta
Lo que sí debes tener en cuenta es que si todo lo defines en bloques de ResourceDictionary no podrás acceder a esas definiciones por código usando Application.Current.Resources[«key»].
Es decir, el código usado en el primer ejemplo fallará si esos dos recursos (ColorAzul y ColorRojo) no están directamente definidos en los recursos de la aplicación.
Pero puedes tenerlos definidos en dos sitios diferentes sin conflictos.
Para que todos estos ejemplos funcionen he definido esos dos valores tanto en Application.Resources como en el ResourceDictionary del fichero ese externo.
Por tanto, el código XAML de App.xaml empezaría de esta forma: (sigue con otras definiciones de ResourceDictionary, una que estaba ya en ese fichero cuando se creé el proyecto y los que he definido para usar los colores Color2Azul y Color2Rojo del segundo ejemplo que te mostré antes).
También he definido los recursos ColorAzul y ColorRojo para que el primer ejemplo no de error. Y sí, esas mismas claves están definidas en ambos ficheros, y sin conflictos. 😉
<Application.Resources>
<ResourceDictionary Source="ResourceDictionaryElGuille.xaml"/>
<ResourceDictionary>
<Color x:Key="Color2Azul">#0073cf</Color>
<Color x:Key="Color2Rojo">Firebrick</Color>
</ResourceDictionary>
Como ya te he explicado antes, la forma de usar esos valores definidos en un ResourceDictionary es usando el código del segundo ejemplo de este post.
Y esto es todo… espero que te haya sido de utilidad.
El código fuente de estos ejemplos (y los del 2 de junio) están en GitHub: ElGuille-Ejemplos.
Nos vemos.
Guillermo