Referencias a ResourceDictionary y a los recursos XAML

Puedes definir la interfaz de usuario o los recursos de la aplicación mediante XAML. Los recursos suelen ser definiciones de algún objeto que espera usar más de una vez. Para hacer referencia a un recurso XAML más adelante, se especifica una clave para un recurso, que actúa como su nombre. Puedes hacer referencia a un recurso a través de una aplicación o desde cualquier página XAML dentro de ella. Puedes definir los recursos con un elemento ResourceDictionary desde el XAML de Windows Runtime. A continuación, puedes hacer referencia a los recursos mediante una extensión de marcado de StaticResource o una extensión de marcado ThemeResource.

Los elementos XAML que quizás quieras declarar con más frecuencia como recursos XAML son Style, ControlTemplate, componentes de animación y subclases Brush. Aquí se explica cómo definir un elemento ResourceDictionary y recursos con clave, y cómo los recursos XAML se relacionan con otros recursos que definas como parte de tu aplicación o paquete de la aplicación. También se explican las características avanzadas del diccionario de recursos, como MergedDictionaries y ThemeDictionaries.

Requisitos previos

Una comprensión sólida del marcado XAML. Le recomendamos leer información general sobre XAML.

Definir y usar recursos XAML

Los recursos XAML son objetos a los que se hace referencia desde el marcado más de una vez. Los recursos se definen en un objeto ResourceDictionary, normalmente en un archivo independiente o en la parte superior de la página de marcado, como esta.

<Page
    x:Class="MSDNSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Page.Resources>
        <x:String x:Key="greeting">Hello world</x:String>
        <x:String x:Key="goodbye">Goodbye world</x:String>
    </Page.Resources>

    <TextBlock Text="{StaticResource greeting}" Foreground="Gray" VerticalAlignment="Center"/>
</Page>

En este ejemplo:

  • <Page.Resources>…</Page.Resources>: define el diccionario de recursos.
  • <x:String>: define el recurso con la clave "hola".
  • {StaticResource greeting}: busca el recurso con la clave "hola", que se asigna a la propiedad Texto del objeto TextBlock.

Nota No confundas los conceptos relativos a ResourceDictionary con la acción de compilación Recurso, los archivos de recursos (.resw) u otros "recursos" que se describen en el contexto de la estructuración del proyecto de código que genera el paquete de la aplicación.

Los recursos no tienen que ser cadenas; pueden ser cualquier objeto que se pueda compartir, como estilos, plantillas, pinceles y colores. Sin embargo, los controles, las formas y otros objetos FrameworkElement no se pueden compartir, por lo que no se pueden declarar como recursos reutilizables. Para obtener más información sobre el uso compartido, consulta la sección Los recursos XAML deben compartirse más adelante en este tema.

Aquí, tanto un pincel como una cadena se declaran como recursos y los usan los controles de una página.

<Page
    x:Class="SpiderMSDN.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Page.Resources>
        <SolidColorBrush x:Key="myFavoriteColor" Color="green"/>
        <x:String x:Key="greeting">Hello world</x:String>
    </Page.Resources>

    <TextBlock Foreground="{StaticResource myFavoriteColor}" Text="{StaticResource greeting}" VerticalAlignment="Top"/>
    <Button Foreground="{StaticResource myFavoriteColor}" Content="{StaticResource greeting}" VerticalAlignment="Center"/>
</Page>

Todos los recursos deben tener una clave. Normalmente, esa clave es una cadena definida con x:Key="myString". Sin embargo, hay otras maneras de especificar una clave:

  • Style y ControlTemplate requieren un elemento TargetType, y usarán el elemento TargetType como clave si no se especifica x:Key. En este caso, la clave es precisamente el objeto Tipo, no una cadena. (vea los siguientes ejemplos)
  • Los recursos DataTemplate que tengan un elemento TargetType usarán este elemento TargetType como clave si no se especifica x:Key. En este caso, la clave es precisamente el objeto Tipo, no una cadena.
  • Se usa x:Name en lugar de x:Key. Sin embargo, x:Name también genera un campo de código subyacente para el recurso. Como resultado, x:Name es menos eficaz que x:Key porque ese campo debe inicializarse cuando se carga la página.

La extensión de marcado StaticResource solo puede recuperar recursos con un nombre de cadena (x:Key o x:Name). Sin embargo, el marco XAML también busca los recursos de estilo implícito (aquellos que usan TargetType en lugar de x:Key o x:Name) cuando decide qué estilo y plantilla usar para un control que no haya establecido las propiedades Style y ContentTemplate o ItemTemplate.

Aquí, el objeto Style tiene una clave implícita de typeof(Button), y dado que el objeto Button situado en la parte inferior de la página no especifica una propiedad Style, busca un estilo con clave de typeof(Button):

<Page
    x:Class="MSDNSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Page.Resources>
        <Style TargetType="Button">
            <Setter Property="Background" Value="Red"/>
        </Style>
    </Page.Resources>
    <Grid>
       <!-- This button will have a red background. -->
       <Button Content="Button" Height="100" VerticalAlignment="Center" Width="100"/>
    </Grid>
</Page>

Para obtener más información sobre los estilos implícitos y cómo funcionan, consultaControles de estilo y plantillas de control.

Búsqueda de recursos en el código

Puedes acceder a los miembros del diccionario de recursos como cualquier otro diccionario.

Advertencia

Al realizar una búsqueda de recursos en el código, solo se busca en los recursos del diccionario Page.Resources. A diferencia de la extensión de marcado StaticResource, el código no vuelve al diccionario Application.Resources si los recursos no se encuentran en el primer diccionario.

 

En este ejemplo se muestra cómo recuperar el recurso redButtonStyle fuera del diccionario de recursos de una página:

<Page
    x:Class="MSDNSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Page.Resources>
        <Style TargetType="Button" x:Key="redButtonStyle">
            <Setter Property="Background" Value="red"/>
        </Style>
    </Page.Resources>
</Page>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            Style redButtonStyle = (Style)this.Resources["redButtonStyle"];
        }
    }
    MainPage::MainPage()
    {
        InitializeComponent();
        Windows::UI::Xaml::Style style = Resources().TryLookup(winrt::box_value(L"redButtonStyle")).as<Windows::UI::Xaml::Style>();
    }

Para buscar recursos para toda la aplicación desde el código, usa Application.Current.Resources para obtener el diccionario de recursos de la aplicación, como se muestra aquí.

<Application
    x:Class="MSDNSample.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SpiderMSDN">
    <Application.Resources>
        <Style TargetType="Button" x:Key="appButtonStyle">
            <Setter Property="Background" Value="red"/>
        </Style>
    </Application.Resources>

</Application>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            Style appButtonStyle = (Style)Application.Current.Resources["appButtonStyle"];
        }
    }
    MainPage::MainPage()
    {
        InitializeComponent();
        Windows::UI::Xaml::Style style = Application::Current().Resources()
                                                               .TryLookup(winrt::box_value(L"appButtonStyle"))
                                                               .as<Windows::UI::Xaml::Style>();
    }

También puedes agregar un recurso de aplicación en el código.

Hay dos cosas que hay que tener en cuenta al hacer esto.

  • En primer lugar, debes agregar los recursos antes de que cualquier página intente usar el recurso.
  • En segundo lugar, no se pueden agregar recursos en el constructor de la aplicación.

Puedes evitar ambos problemas si agregas el recurso en el método Application.OnLaunched de este modo.

// App.xaml.cs
    
sealed partial class App : Application
{
    protected override void OnLaunched(LaunchActivatedEventArgs e)
    {
        Frame rootFrame = Window.Current.Content as Frame;
        if (rootFrame == null)
        {
            SolidColorBrush brush = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 0, 255, 0)); // green
            this.Resources["brush"] = brush;
            // … Other code that VS generates for you …
        }
    }
}
// App.cpp

void App::OnLaunched(LaunchActivatedEventArgs const& e)
{
    Frame rootFrame{ nullptr };
    auto content = Window::Current().Content();
    if (content)
    {
        rootFrame = content.try_as<Frame>();
    }

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active
    if (rootFrame == nullptr)
    {
        Windows::UI::Xaml::Media::SolidColorBrush brush{ Windows::UI::ColorHelper::FromArgb(255, 0, 255, 0) };
        Resources().Insert(winrt::box_value(L"brush"), winrt::box_value(brush));
        // … Other code that VS generates for you …

Cada FrameworkElement puede tener un ResourceDictionary

FrameworkElement es una clase base de la que heredan los controles y tiene una propiedad Resources. Por lo tanto, puedes agregar un diccionario de recursos local a cualquier FrameworkElement.

En este apartado, tanto la clase Page como la clase Border tienen diccionarios de recursos, y ambos tienen un recurso denominado "greeting". El objeto TextBlock llamado "textBlock2" está dentro del objeto Borde, de modo que la búsqueda de recursos se realiza primero en los recursos del objeto Borde, luego en los recursos del objeto Página y finalmente en los del objeto Aplicación. El TextBlock leerá "Hola mundo".

Para acceder a los recursos de ese elemento desde el código, usa la propiedad Rercursos de ese elemento. Al acceder a los recursos de un FrameworkElement en el código, en lugar de XAML, solo buscará en ese diccionario, no en los diccionarios del elemento primario.

<Page
    x:Class="MSDNSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Page.Resources>
        <x:String x:Key="greeting">Hello world</x:String>
    </Page.Resources>
    
    <StackPanel>
        <!-- Displays "Hello world" -->
        <TextBlock x:Name="textBlock1" Text="{StaticResource greeting}"/>

        <Border x:Name="border">
            <Border.Resources>
                <x:String x:Key="greeting">Hola mundo</x:String>
            </Border.Resources>
            <!-- Displays "Hola mundo" -->
            <TextBlock x:Name="textBlock2" Text="{StaticResource greeting}"/>
        </Border>

        <!-- Displays "Hola mundo", set in code. -->
        <TextBlock x:Name="textBlock3"/>
    </StackPanel>
</Page>

    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            textBlock3.Text = (string)border.Resources["greeting"];
        }
    }
    MainPage::MainPage()
    {
        InitializeComponent();
        textBlock3().Text(unbox_value<hstring>(border().Resources().TryLookup(winrt::box_value(L"greeting"))));
    }

Diccionarios de recursos combinados

Un diccionario de recursos combinado combina un diccionario de recursos en otro, normalmente en otro archivo.

Sugerencia Puedes crear un archivo de diccionario de recursos en Microsoft Visual Studio mediante la opción Agregar > Nuevo elemento… > Diccionario de recursos del menú Proyecto.

Aquí, definirás un diccionario de recursos en un archivo XAML independiente denominado Dictionary1.xaml.

<!-- Dictionary1.xaml -->
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MSDNSample">

    <SolidColorBrush x:Key="brush" Color="Red"/>

</ResourceDictionary>

Para usar ese diccionario, se combina con el diccionario de la página:

<Page
    x:Class="MSDNSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Page.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Dictionary1.xaml"/>
            </ResourceDictionary.MergedDictionaries>

            <x:String x:Key="greeting">Hello world</x:String>

        </ResourceDictionary>
    </Page.Resources>

    <TextBlock Foreground="{StaticResource brush}" Text="{StaticResource greeting}" VerticalAlignment="Center"/>
</Page>

Esto es lo que sucede en este ejemplo. En <Page.Resources>, declara <ResourceDictionary>. El marco XAML crea implícitamente un diccionario de recursos para ti cuando agrega recursos a <Page.Resources>; sin embargo, en este caso, no necesitas cualquier diccionario de recursos, sino uno que contenga diccionarios combinados.

Por lo tanto, declara <ResourceDictionary> y agrega elementos a tu colección <ResourceDictionary.MergedDictionaries>. Cada una de esas entradas toma la forma <ResourceDictionary Source="Dictionary1.xaml"/>. Para agregar más de un diccionario, basta con agregar una entrada <ResourceDictionary Source="Dictionary2.xaml"/> después de la primera entrada.

Después de <ResourceDictionary.MergedDictionaries>…</ResourceDictionary.MergedDictionaries>, opcionalmente puedes colocar recursos adicionales en el diccionario principal. Los recursos de un diccionario combinado se usan igual que un diccionario normal. En el ejemplo anterior, {StaticResource brush} busca el recurso en el diccionario secundario/combinado (Dictionary1.xaml), mientras que {StaticResource greeting} encuentras tu recurso en el diccionario de página principal.

En la secuencia de búsqueda de recursos, el diccionario MergedDictionaries solo se comprueba después de comprobar todos los recursos con clave de ese ResourceDictionary. Después de buscar en ese nivel, la búsqueda alcanza los diccionarios combinados y se comprueba cada elemento de MergedDictionaries. Si existen varios diccionarios combinados, estos diccionarios se comprueban en el orden inverso en el que se declaran en la propiedad MergedDictionaries. En el ejemplo siguiente, si Dictionary2.xaml y Dictionary1.xaml declararon la misma clave, la clave de Dictionary2.xaml se usa primero porque es la última en el conjunto MergedDictionaries.

<Page
    x:Class="MSDNSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Page.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Dictionary1.xaml"/>
                <ResourceDictionary Source="Dictionary2.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Page.Resources>

    <TextBlock Foreground="{StaticResource brush}" Text="greetings!" VerticalAlignment="Center"/>
</Page>

En el ámbito de cualquier ResourceDictionary, se comprueba que la clave del diccionario sea única. Sin embargo, ese ámbito no se extiende a los diferentes elementos de los distintos archivos de MergedDictionaries.

Puedes usar la combinación de la secuencia de búsqueda y la falta de obligatoriedad de una clave única en los ámbitos de diccionarios combinados para crear una secuencia de valores de reserva de los recursos de ResourceDictionary. Por ejemplo, puedes almacenar preferencias de usuario para un color de pincel determinado en el último diccionario de recursos combinado de la secuencia, usando un diccionario de recursos que se sincroniza con los datos de estado y preferencias de usuario de la aplicación. Sin embargo, si aún no existen preferencias del usuario, puedes definir esa misma cadena de clave para un recurso de ResourceDictionary en el archivo MergedDictionaries inicial, y puede servir como valor de reserva. Recuerda que cualquier valor que proporciones en un diccionario de recursos principal siempre se comprueba antes de que se comprueben los diccionarios combinados, por lo que si deseas usar la técnica de reserva, no definas ese recurso en un diccionario de recursos principal.

Recursos de temas y diccionarios de temas

Un ThemeResource es similar a StaticResource, pero la búsqueda de recursos se vuelve a evaluar cuando cambia el tema.

En este ejemplo, estableces el primer plano de un TextBlock en un valor del tema actual.

<TextBlock Text="hello world" Foreground="{ThemeResource FocusVisualWhiteStrokeThemeBrush}" VerticalAlignment="Center"/>

Un diccionario de temas es un tipo especial de diccionario combinado que contiene los recursos que varían con el tema que un usuario está usando actualmente en su dispositivo. Por ejemplo, el tema "claro" podría usar un pincel de color blanco, mientras que el tema "oscuro" podría usar un pincel de color oscuro. El pincel cambia el recurso en el que se resuelve, pero de lo contrario, la composición de un control que usa el pincel como un recurso podría ser el mismo. Para reproducir el comportamiento de cambio de tema en tus propias plantillas y estilos, en lugar de usar MergedDictionaries como la propiedad para combinar elementos en los diccionarios principales, mejor usa la propiedad ThemeDictionaries.

Cada elemento ResourceDictionary de ThemeDictionaries debe tener un valor x:Key. El valor es una cadena que asigna un nombre al tema pertinente, por ejemplo, "Valor predeterminado", "Oscurso", "Claro" o "Alto contraste". Normalmente, Dictionary1 y Dictionary2 definirán los recursos que tienen los mismos nombres, pero valores diferentes.

Aquí, usarás el texto rojo para el tema claro y el texto azul para el tema oscuro.

<!-- Dictionary1.xaml -->
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MSDNSample">

    <SolidColorBrush x:Key="brush" Color="Red"/>

</ResourceDictionary>

<!-- Dictionary2.xaml -->
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MSDNSample">

    <SolidColorBrush x:Key="brush" Color="blue"/>

</ResourceDictionary>

En este ejemplo, estableces el primer plano de un TextBlock en un valor del tema actual.

<Page
    x:Class="MSDNSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Page.Resources>
        <ResourceDictionary>
            <ResourceDictionary.ThemeDictionaries>
                <ResourceDictionary Source="Dictionary1.xaml" x:Key="Light"/>
                <ResourceDictionary Source="Dictionary2.xaml" x:Key="Dark"/>
            </ResourceDictionary.ThemeDictionaries>
        </ResourceDictionary>
    </Page.Resources>
    <TextBlock Foreground="{StaticResource brush}" Text="hello world" VerticalAlignment="Center"/>
</Page>

Para los diccionarios de temas, el diccionario activo que se usará para la búsqueda de recursos cambia dinámicamente cuando se usa la extensión de marcado ThemeResource para hacer la referencia y el sistema detecta un cambio de tema. El comportamiento de búsqueda realizado por el sistema se basa en la asignación del tema activo a la x:Key de un diccionario de temas específico.

Puede ser útil revisar la forma en que los diccionarios de temas están estructurados en los recursos de diseño XAML predeterminados, que es similar a las plantillas que Usa Windows Runtime de forma predeterminada para tus controles. Abre los archivos XAML en \(Program Files)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\<SDK version>\Generic con un editor de texto en tu IDE. Observa cómo los diccionarios de temas se definen primero en generic.xaml y cómo cada diccionario de temas define las mismas claves. A continuación, se hace referencia a cada clave por elementos de composición en los distintos elementos clavedos que están fuera de los diccionarios de temas y que se definen más adelante en el XAML. También hay un archivo themeresources.xaml independiente para el diseño que contiene solo los recursos de tema y las plantillas adicionales, pero no las plantillas de control predeterminadas. Las áreas de tema son duplicados de lo que vería en generic.xaml.

Cuando usas herramientas de diseño XAML para editar copias de estilos y plantillas, las herramientas de diseño extraen secciones de los diccionarios de recursos de diseño XAML y las colocan como copias locales de elementos de diccionario XAML que forman parte de tu aplicación y proyecto.

Para obtener más información y una lista de los recursos especificados por temas y del sistema que hay a disposición de las aplicaciones de Windows que usan XAML, consulta Recursos de temas en XAML.

Comportamiento de búsqueda para referencias de recursos XAML

El comportamiento de búsqueda es el término que describe cómo el sistema de recursos XAML intenta encontrar un recurso XAML. La búsqueda se produce cuando se hace referencia a una clave como recursos XAML desde algún lugar del XAML de la aplicación. En primer lugar, el sistema de recursos tiene un comportamiento predecible para el lugar en el que comprobará la existencia de un recurso en función del ámbito. Si no se encuentra un recurso en el ámbito inicial, el ámbito se expande. El comportamiento de búsqueda continúa en todas las ubicaciones y ámbitos en los que un recurso XAML podría definirse posiblemente por una aplicación o por el sistema. Si todos los intentos de búsqueda de recursos posibles fallan, suele producirse un error. Normalmente, es posible eliminar estos errores durante el proceso de desarrollo.

El comportamiento de búsqueda para las referencias a recursos XAML comienza con el objeto al que se aplica el uso real y su propiedad Recursos. Si allí existe un ResourceDictionary, se comprueba si en este ResourceDictionary hay un elemento con la clave solicitada. Este primer nivel de búsqueda rara vez es relevante porque normalmente no se define y, a continuación, se hace referencia a un recurso en el mismo objeto. De hecho, la propiedad Recursos a menudo no existe aquí. Puedes hacer referencia a los recursos del XAML desde casi cualquier parte del XAML; sin limitaciones en lo que respecta a las propiedades de las subclases de FrameworkElement.

A continuación, la secuencia de búsqueda comprueba el siguiente objeto primario en el árbol de objetos en tiempo de ejecución de la aplicación. Si existe un FrameworkElement.Resources y contiene un ResourceDictionary, se solicita el elemento de diccionario con la clave especificada. Si se encuentra el recurso, la secuencia de búsqueda se detiene y el objeto se proporciona a la ubicación donde se realizó la referencia. De lo contrario, el comportamiento de búsqueda avanza al siguiente nivel primario hacia la raíz del árbol de objetos. La búsqueda continúa de forma recursiva hacia arriba hasta que se alcanza el elemento raíz del XAML, lo que agota la búsqueda de todas las ubicaciones de recursos inmediatas posibles.

Nota:

Es una práctica habitual definir todos los recursos inmediatos en el nivel raíz de una página, tanto para aprovechar este comportamiento de búsqueda de recursos como por una convención de estilo de marcado de XAML.

Si no se encuentra el recurso solicitado en los recursos inmediatos, el siguiente paso de la búsqueda es comprobar la propiedad Application.Resources. Application.Resources es el mejor lugar para colocar los recursos específicos de la aplicación a los que hacen referencia varias páginas en la estructura de navegación de la aplicación.

Importante

El orden de los recursos agregados a ResourceDictionary afecta al orden en que se aplican. El diccionario XamlControlsResources invalida muchas claves de recursos predeterminadas y, por tanto, se debe agregar primero a Application.Resources para que no invalide ningún otro estilo o recurso personalizado en tu aplicación.

Las plantillas de control tienen otra posible ubicación en la búsqueda de referencia: diccionarios de temas. Un diccionario de temas es un único archivo XAML que tiene un elemento ResourceDictionary como raíz. El diccionario de temas podría ser un diccionario combinado de Application.Resources. El diccionario de temas también puede ser el diccionario de temas específico del control para un control personalizado con plantilla.

Por último, hay una búsqueda de recursos en los recursos de la plataforma. Los recursos de la plataforma incluyen las plantillas de control definidas para cada uno de los temas de la interfaz de usuario del sistema y definen la apariencia predeterminada de todos los controles que se usan para la interfaz de usuario en una aplicación de Windows Runtime. Los recursos de la plataforma también incluyen un conjunto de recursos con nombre relacionados con la apariencia y los temas del sistema. Estos recursos son técnicamente un elemento MergedDictionaries y, por lo tanto, están disponibles para la búsqueda del XAML o de código cuando la aplicación se ha cargado. Por ejemplo, entre los recursos de tema del sistema encontramos uno llamado "SystemColorWindowTextColor" que ofrece una definición de Color que permite que el color del texto de la aplicación sea el mismo que el del texto de una ventana del sistema procedente del sistema operativo y las preferencias del usuario. Otros estilos XAML de la aplicación pueden hacer referencia a este estilo o el código puede obtener un valor de búsqueda de recursos (y convertirlo a Color en el caso de ejemplo).

Para obtener más información y una lista de los recursos del sistema y específicos del tema que hay a disposición de las aplicaciones de Windows que usan XAML, consulta Recursos de temas en XAML.

Si la clave solicitada todavía no se encuentra en ninguna de estas ubicaciones, se produce un error o excepción de análisis de XAML. En determinadas circunstancias, la excepción de análisis xaml puede ser una excepción en tiempo de ejecución que no se detecta mediante una acción de compilación de marcado XAML o por un entorno de diseño XAML.

Debido al comportamiento de búsqueda en capas de los diccionarios de recursos, puedes definir deliberadamente varios elementos de recursos cada uno con el mismo valor de cadena que la clave, siempre y cuando cada recurso se defina en un nivel diferente. En otras palabras, aunque las claves deben ser únicas en cualquier ResourceDictionary dado, este requisito no se aplica a toda la secuencia de comportamiento de búsqueda. Durante la búsqueda, solo se usa el primer objeto de este tipo que se recupera correctamente para la referencia de recursos XAML y, a continuación, se detiene la búsqueda. Es posible que utilices este comportamiento para solicitar el mismo recurso XAML por clave en varias posiciones dentro del XAML de la aplicación, pero que obtengas recursos diferentes, dependiendo del ámbito desde el que se realizó la referencia de recursos XAML y de cómo se comporta esa búsqueda concreta.

Reenvío de referencias dentro de ResourceDictionary

Las referencias de recursos XALM dentro de un diccionario de recursos deben hacer referencia a un recurso que ya se haya definido léxicamente antes que la referencia de recurso. No se pueden resolver las referencias adelantadas mediante una referencia de recursos XALM. Por este motivo, si usas referencias de recursos XAML desde otro recurso, debes diseñar la estructura del diccionario de recursos para que los recursos usados por otros recursos se definan primero en un diccionario de recursos.

Los recursos definidos en el nivel de aplicación no pueden hacer referencias a recursos inmediatos. Esto equivale a intentar una referencia hacia delante, ya que los recursos de la aplicación se procesan primero (cuando se inicia la aplicación por primera vez y antes de cargar cualquier contenido de página de navegación). Sin embargo, cualquier recurso inmediato puede hacer referencia a un recurso de aplicación y esto puede ser una técnica útil para evitar situaciones de referencia directa.

Los recursos XAML deben compartirse

Para que un objeto exista en una clase ResourceDictionary, dicho objeto se debe poder compartir.

Se requiere compartir porque, cuando se construye y usa el árbol de objetos de una aplicación en tiempo de ejecución, los objetos no pueden existir en varias ubicaciones del árbol. Internamente, el sistema de recursos crea copias de valores de recursos que se van a usar en el gráfico de objetos de la aplicación cuando se solicita cada recurso XAML.

Un ResourceDictionary y el XAML de Windows Runtime normalmente admiten estos objetos para compartirlos:

También puedes usar tipos personalizados como un recurso que se puede compartir si sigue los patrones de implementación necesarios. Define estas clases en el código de respaldo (o en componentes en tiempo de ejecución que incluya) y, a continuación, crea instancias de esas clases en XAML como un recurso. Algunos ejemplos son orígenes de datos de objeto e implementaciones de IValueConverter para el enlace de datos.

Los tipos personalizados deben tener un constructor predeterminado, ya que es lo que usa un analizador XAML para crear instancias de clase. Los tipos personalizados que se usan como recursos no pueden tener la clase UIElement en su herencia, porque un UIElement nunca se puede compartir (siempre está destinado a representar exactamente un elemento de la interfaz de usuario que existe en una posición en el gráfico de objetos de la aplicación en tiempo de ejecución).

Ámbito de uso de UserControl

Un elemento UserControl es un caso especial del comportamiento de la búsqueda de recursos porque tiene los conceptos inherentes de un ámbito de definición y un ámbito de uso. Un UserControl que hace referencia a un recurso XAML a partir de su ámbito de definición debe ser capaz de admitir la búsqueda de ese recurso dentro de su propia secuencia de búsqueda de ámbito de definición, es decir, no puede acceder a los recursos de la aplicación. Desde un ámbito de uso de UserControl, una referencia de recursos se trata como dentro de la secuencia de búsqueda hacia su raíz de página de uso (al igual que cualquier otra referencia de recursos realizada desde un objeto en un árbol de objetos cargado) y puede acceder a los recursos de la aplicación.

ResourceDictionary y XamlReader.Load

Un ResourceDictionary se puede usar como raíz o como parte de la entrada XAML para el método XamlReader.Load. También puedes incluir referencias de recursos XAML en ese XAML si todas estas referencias están completamente independientes en el XAML enviado para cargar. XamlReader.Load analiza el código XAML en un contexto que no tiene constancia de otros objetos ResourceDictionary, ni siquiera de la propiedad Application.Resources. Además, no uses {ThemeResource} desde xaml enviado a XamlReader.Load.

Uso de ResourceDictionary desde código

La mayoría de los escenarios de una clase ResourceDictionary se controlan exclusivamente en XAML. Declara el contenedor ResourceDictionary y los recursos dentro de un archivo XAML o un conjunto de nodos XAML en un archivo de definición de interfaz de usuario. Y, a continuación, usa referencias de recursos XAML para solicitar esos recursos de otras partes de XAML. Aun así, hay ciertos escenarios en los que es posible que la aplicación quiera ajustar el contenido de un ResourceDictionary mediante un código que se ejecuta mientras la aplicación está en uso, o al menos que quiera consultar el contenido de un ResourceDictionary para ver si un recurso ya está definido. Las llamadas a este código se realizan en una instancia de ResourceDictionary, por lo que primero tienes que recuperar una, bien un ResourceDictionary inmediato en algún lugar del árbol de objetos mediante la obtención de FrameworkElement.Resources, o bien, Application.Current.Resources.

En el código C# o Microsoft Visual Basic, puedes hacer referencia a un recurso en una clase ResourceDictionary determinada, mediante el indexador (Elemento). ResourceDictionary es un diccionario con clave de cadena, por lo que el indexador usa la clave de cadena en lugar de un índice entero. En extensiones de componentes de Visual C++ (C++/CX), usa Buscar.

Cuando uses código para examinar o cambiar un objeto ResourceDictionary, el comportamiento de API como Lookup o Item, no se desvía de los recursos inmediatos a los recursos de la aplicación, ya que ese es un comportamiento del analizador XAML que solo tiene lugar al cargar las páginas XAML. En tiempo de ejecución, el ámbito de las claves está autocontenido en la instancia de ResourceDictionary que está usando en el momento. De todos modos, cabe señalar que ese ámbito sí se extiende a la propiedad MergedDictionaries.

Además, si solicitas una clave que no existe en la clave ResourceDictionary, puede que no se produzca un error, sino que el valor devuelto sea simplemente null. Sin embargo, es posible que se produzca un error si intentas usar el valor null devuelto como un valor. El error provendría del establecedor de la propiedad, no de la llamada a ResourceDictionary. La única manera de evitar un error es si la propiedad acepta null como un valor válido. Ten en cuenta cómo este comportamiento contrasta con el comportamiento de búsqueda XAML en tiempo de análisis XAML; un error al resolver la clave proporcionada de XAML en tiempo de análisis produce un error de análisis XAML, incluso en los casos en los que la propiedad podría haber aceptado null.

Los diccionarios de recursos combinados se incluyen en el ámbito de índice del diccionario de recursos principal que hace referencia al diccionario combinado en tiempo de ejecución. En otras palabras, puedes usar los elementos Elemento o Búsqueda del diccionario principal para encontrar los objetos que se definieron en el diccionario combinado. En este caso, el comportamiento de búsqueda se parece al comportamiento de búsqueda XAML en tiempo de análisis: si hay varios objetos en diccionarios combinados y cada uno tiene la misma clave, se devuelve el objeto del último diccionario agregado.

Está permitido agregar elementos a un objeto ResourceDictionary existente con una llamada a Agregar (C o Visual Basic) o a Insertar (C++/CX). Puedes agregar los elementos a los recursos inmediatos o a los recursos de la aplicación. Cualquiera de estas llamadas API requiere una clave que satisfaga el requisito de que cada elemento de un ResourceDictionary tenga una clave. Sin embargo, los elementos que agregas a ResourceDictionary en tiempo de ejecución no son relevantes para las referencias de recursos XAML. La búsqueda necesaria para las referencias a recursos XAML se produce cuando se analiza por primera vez XAML a medida que se carga la aplicación (o se detecta un cambio de tema). Los recursos agregados a las colecciones en tiempo de ejecución no estaban disponibles y modificar ResourceDictionary no invalida un recurso ya recuperado de él aunque cambie el valor de ese recurso.

También puedes quitar elementos de un ResourceDictionary en tiempo de ejecución, crear copias de algunos o de todos los elementos, y otras operaciones. La lista de miembros de ResourceDictionary indica qué interfaces API están disponibles. Ten en cuenta que como ResourceDictionary tiene una API proyectada para admitir las interfaces de la colección subyacentes, las opciones de tu API diferirán según si usas C o Visual Basic respecto a C++/CX.

ResourceDictionary y localización

Inicialmente, un ResourceDictionary de XAML podría contener cadenas localizables. Si es así, almacena estas cadenas como recursos de proyecto en lugar de en ResourceDictionary. Saca las cadenas del XAML y, en su lugar, asigna al elemento propietario un valor de directiva x:Uid. A continuación, define un recurso en un archivo de recursos. Proporciona un nombre de recurso con el formato XUIDValue.PropertyName y un valor de recurso de la cadena que se debe localizar.

Búsqueda de recursos personalizados

En escenarios avanzados, puedes implementar una clase que pueda tener un comportamiento diferente al comportamiento de búsqueda de referencia de recursos XAML descrito en este tema. Para ello, implementa la clase CustomXamlResourceLoader y, a continuación, podrás acceder a ese comportamiento mediante el uso de la extensión de marcado CustomResource para referencias de recursos en vez de usar StaticResource o ThemeResource. La mayoría de las aplicaciones no tendrán escenarios que requieran esto. Para obtener más información, consulta CustomXamlResourceLoader.