Información general sobre los recursos XAML (WPF para .NET)

Un recurso es un objeto que se puede volver a usar en diferentes sitios de la aplicación. Pinceles y estilos son ejemplos de recursos. En esta información general se describe cómo usar los recursos en lenguaje XAML. También puede crear y obtener acceso a recursos mediante código.

Nota:

Los recursos XAML descritos en este artículo son diferentes de los recursos de la aplicación, que normalmente son archivos agregados a una aplicación, como contenido, datos o archivos incrustados.

Importante

La documentación de la Guía de escritorio para .NET 6 y .NET 5 (incluido .NET Core 3.1) está en elaboración.

Uso de recursos en XAML

En el ejemplo siguiente se define SolidColorBrush como un recurso en el elemento raíz de una página. Posteriormente, el ejemplo hace referencia al recurso y lo usa para establecer las propiedades de varios elementos secundarios, incluidos los elementos Ellipse, TextBlock y Button.

<Window x:Class="resources.ResExample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ResExample" Height="400" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="MyBrush" Color="#05E0E9"/>
        <Style TargetType="Border">
            <Setter Property="Background" Value="#4E1A3D" />
            <Setter Property="BorderThickness" Value="5" />
            <Setter Property="BorderBrush">
                <Setter.Value>
                    <LinearGradientBrush>
                        <GradientStop Offset="0.0" Color="#4E1A3D"/>
                        <GradientStop Offset="1.0" Color="Salmon"/>
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="TextBlock" x:Key="TitleText">
            <Setter Property="FontSize" Value="18"/>
            <Setter Property="Foreground" Value="#4E87D4"/>
            <Setter Property="FontFamily" Value="Trebuchet MS"/>
            <Setter Property="Margin" Value="0,10,10,10"/>
        </Style>
        <Style TargetType="TextBlock" x:Key="Label">
            <Setter Property="HorizontalAlignment" Value="Right"/>
            <Setter Property="FontSize" Value="13"/>
            <Setter Property="Foreground" Value="{StaticResource MyBrush}"/>
            <Setter Property="FontFamily" Value="Arial"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="Margin" Value="0,3,10,0"/>
        </Style>
    </Window.Resources>

    <Border>
        <StackPanel>
            <TextBlock Style="{StaticResource TitleText}">Title</TextBlock>
            <TextBlock Style="{StaticResource Label}">Label</TextBlock>
            <TextBlock HorizontalAlignment="Right" FontSize="36" Foreground="{StaticResource MyBrush}" Text="Text" Margin="20" />
            <Button HorizontalAlignment="Left" Height="30" Background="{StaticResource MyBrush}" Margin="40">Button</Button>
            <Ellipse HorizontalAlignment="Center" Width="100" Height="100" Fill="{StaticResource MyBrush}" Margin="10" />
        </StackPanel>
    </Border>

</Window>

Cada elemento de nivel de marco (FrameworkElement o FrameworkContentElement) tiene una propiedad Resources, que es un tipo de ResourceDictionary que contiene recursos definidos. Puede definir recursos en cualquier elemento, como Button. Sin embargo, los recursos se definen con mayor frecuencia en el elemento raíz, que en el ejemplo es Window.

Cada recurso en un diccionario de recursos debe tener una clave única. Al definir los recursos en el marcado, le asigna la clave única a través de la directiva x:Key. Normalmente, la clave es una cadena; pero puede también establecerla para otros tipos de objetos usando las extensiones de marcado apropiadas. Las claves que no son de cadena para recursos se usan por ciertas áreas de características de WPF, en particular para los estilos, los recursos de componente y los estilos de datos.

Puede usar un recurso definido con la sintaxis de extensión de marcado de recursos que especifica el nombre de clave del recurso. Por ejemplo, use el recurso como el valor de una propiedad en otro elemento.

<Button Background="{StaticResource MyBrush}"/>
<Ellipse Fill="{StaticResource MyBrush}"/>

En el ejemplo anterior, cuando el cargador de XAML procesa el valor {StaticResource MyBrush} para la propiedad Background en Button, la lógica de búsqueda de recursos comprueba primero el diccionario de recursos para el elemento Button. Si Button no tiene una definición de la clave de recurso MyBrush (en ese ejemplo no lo hace; su colección de recursos está vacía), la búsqueda siguiente comprueba el elemento primario de Button. Si el recurso no está definido en el elemento primario, continúa con la comprobación del árbol lógico del objeto hacia arriba hasta que lo encuentra.

Si define recursos en el elemento raíz, todos los elementos del árbol lógico, como Window o Page, pueden acceder a él. Y puede reutilizar el mismo recurso para establecer el valor de cualquier propiedad que acepte el mismo tipo que representa el recurso. En el ejemplo anterior el mismo recurso MyBrush establece dos propiedades diferentes: Button.Background y Rectangle.Fill.

Recursos estáticos y dinámicos

Se puede hacer referencia a un recurso como estático o dinámico. Las referencias se crean usando la extensión de marcado StaticResource o la extensión de marcado DynamicResource. Una extensión de marcado es una característica de XAML que permite especificar una referencia de objeto que tiene la extensión de marcado para procesar la cadena del atributo y devolver el objeto a un cargador XAML. Para obtener más información sobre el comportamiento de la extensión de marcado, vea Extensiones de marcado y XAML de WPF.

Cuando se usa una extensión de marcado, normalmente proporciona uno o varios parámetros en forma de cadena que son procesados por dicha extensión de marcado. La extensión de marcado StaticResource procesa una clave buscando el valor para esa clave en todos los diccionarios de recursos disponibles. El procesamiento se produce durante la carga, que es cuando el proceso de carga debe asignar el valor de propiedad. En su lugar, la extensión de marcado DynamicResource procesa una clave mediante la creación de una expresión, y esa expresión permanece sin evaluar hasta que se ejecuta la aplicación, momento en el que se evalúa la expresión para proporcionar un valor.

Al hacer referencia a un recurso, independientemente de si usa una referencia de recurso estático o una referencia de recurso dinámico, pueden influir las siguientes consideraciones:

  • A la hora de determinar el diseño general de cómo crear los recursos de la aplicación (por página, en la aplicación, en XAML dinámico o en un ensamblado solo de recursos), tenga en cuenta lo siguiente:

  • La funcionalidad de la aplicación. ¿Está actualizando los recursos en la parte en tiempo real de los requisitos de la aplicación?

  • El comportamiento de búsqueda respectivo de ese tipo de referencia de recurso.

  • La propiedad o tipo de recurso determinado y el comportamiento nativo de esos tipos.

Recursos estáticos

Las referencias de recursos estáticos trabajan mejor en las siguientes circunstancias:

  • El diseño de la aplicación concentra la mayoría de sus recursos en la página o en los diccionarios de recursos en el nivel de aplicación.

    Las referencias de recursos estáticos no se vuelven a evaluar en función de los comportamientos en tiempo de ejecución, como volver a cargar una página. Por lo tanto, puede haber una ventaja de rendimiento para evitar un gran número de referencias de recursos dinámicos cuando no son necesarias en función del diseño de la aplicación y el recurso.

  • Está estableciendo el valor de una propiedad que no está en DependencyObject o en Freezable.

  • Está creando un diccionario de recursos que se compila en un archivo DLL que se comparte entre aplicaciones.

  • Está creando un tema para un control personalizado y está definiendo recursos que se usan dentro de los temas.

    En este caso normalmente no se quiere un comportamiento de búsqueda de referencia de recursos dinámicos. En su lugar, se usa el comportamiento de referencia de recursos estáticos para que la búsqueda sea predecible y autocontenida en el tema. Con una referencia de recursos dinámicos, incluso una referencia dentro de un tema se deja sin evaluar hasta el tiempo de ejecución. Además, existe la posibilidad de que, cuando se aplique el tema, algún elemento local vuelva a definir una clave a la que el tema intenta hacer referencia, y el elemento local se encontrará delante del propio tema en la búsqueda. Si esto sucede, el tema no se comportará según lo previsto.

  • Está usando recursos para establecer grandes cantidades de propiedades de dependencia. Las propiedades de dependencia tienen un almacenamiento en caché de valores efectivos habilitado por el sistema de propiedades, así que si proporciona un valor para una propiedad de dependencia que pueda evaluarse en tiempo de carga, la propiedad de dependencia no tiene que comprobar una expresión que se ha vuelto a evaluar y puede devolver el último valor efectivo. Esta técnica puede suponer una ventaja de rendimiento.

  • Quiere cambiar el recurso subyacente para todos los consumidores o quiere mantener instancias grabables independientes para cada consumidor con el atributo x:Shared.

Comportamiento de búsqueda de recursos estáticos

En el siguiente ejemplo se describe el proceso de búsqueda que se produce automáticamente cuando una propiedad o un elemento hace referencia a un recurso estático:

  1. El proceso de búsqueda comprueba la clave solicitada en el diccionario de recursos que ha definido el elemento que establece la propiedad.

  2. Después, el proceso de búsqueda recorre el árbol lógico hacia arriba, hasta el elemento primario y su diccionario de recursos. Este proceso continúa hasta que se alcanza el elemento raíz.

  3. Se comprueban los recursos de la aplicación. Los recursos de la aplicación son aquellos recursos dentro del diccionario de recursos definido por el objeto Application para su aplicación WPF.

Las referencias de recursos estáticos del interior 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 estáticos. Por este motivo, debe diseñar la estructura del diccionario de recursos de manera que los recursos se definan en o cerca del principio de cada diccionario de recursos respectivo.

La búsqueda de recursos estáticos puede ampliarse en temas o en recursos del sistema, pero esta búsqueda solo se admite porque el cargador XAML aplaza la solicitud. El aplazamiento es necesario, de manera que el tema en tiempo de ejecución en el momento de la carga de la página se aplique correctamente en la aplicación. Sin embargo, las referencias de recursos estáticos a claves que se sabe que solo existen en los temas o como recursos del sistema no se recomiendan, ya que dichas referencias no se vuelven a evaluar si el usuario cambia el tema en tiempo real. Una referencia de recurso dinámico es más confiable cuando solicita un tema o recursos del sistema. La excepción se produce cuando un elemento de tema solicita otro recurso. Estas referencias deben ser referencias de recursos estáticos, por los motivos que se han mencionado anteriormente.

El comportamiento de excepción, si no se detecta una referencia de recursos estáticos, varía. Si el recurso se ha aplazado, entonces la excepción se produce en tiempo de ejecución. Si el recurso no se aplaza, la excepción se inicia en el tiempo de carga.

Recursos dinámicos

Los recursos dinámicos funcionan mejor en estos casos:

  • El valor del recurso, incluidos los recursos del sistema o los recursos que puede establecer el usuario, depende de las condiciones que no se conocen hasta el tiempo de ejecución. Por ejemplo, puede crear valores de establecedor que hagan referencia a las propiedades del sistema como expuestas por SystemColors, SystemFonts o SystemParameters. Estos valores son verdaderamente dinámicos porque proceden en definitiva del entorno en tiempo de ejecución del usuario y del sistema operativo. También puede tener temas en el nivel de aplicación que pueden cambiar, donde el acceso a los recursos de nivel de página también debe capturar el cambio.

  • Está creando o haciendo referencia a estilos del tema para un control personalizado.

  • Tiene previsto ajustar el contenido de ResourceDictionary durante la duración de una aplicación.

  • Tiene una estructura de recursos complicada que tiene interdependencias, donde puede necesitarse una referencia adelantada. Las referencias de recursos estáticos no admiten referencias de reenvío, pero sí las referencias de recursos dinámicos, ya que no es necesario evaluar el recurso hasta el tiempo de ejecución y, por lo tanto, las referencias de reenvío no son un concepto pertinente.

  • Está haciendo referencia a un recurso que es grande desde la perspectiva de una compilación o espacio de trabajo, y el recurso puede no usarse inmediatamente cuando se carga la página. Las referencias de recursos estáticos siempre se cargan desde XAML cuando se carga la página. Sin embargo, una referencia de recursos dinámicos no se carga hasta que se utiliza.

  • Está creando un estilo donde los valores del establecedor pueden provenir de otros valores que están influenciados por temas u otra configuración del usuario.

  • Está aplicando recursos en elementos en los que se puede cambiar el elemento primario en el árbol lógico durante la vigencia de la aplicación. Cambiar el elemento primario también cambia potencialmente el ámbito de búsqueda de recursos, por lo que si quiere que el recurso de un elemento primario que se puede cambiar se vuelva a evaluar basándose en el nuevo ámbito, use siempre una referencia de recurso dinámico.

Comportamiento de búsqueda de recursos dinámicos

El comportamiento de búsqueda de recursos para una referencia de recursos dinámicos es paralelo al comportamiento de búsqueda en el código si se llama a FindResource o SetResourceReference:

  1. La búsqueda comprueba la clave solicitada en el diccionario de recursos que ha definido el elemento que establece la propiedad:

  2. La búsqueda recorre el árbol lógico hacia arriba, hasta el elemento primario y su diccionario de recursos. Este proceso continúa hasta que se alcanza el elemento raíz.

  3. Se comprueban los recursos de la aplicación. Los recursos de la aplicación son los recursos del diccionario de recursos definidos por el objeto Application de la aplicación de WPF.

  4. Se comprueba el diccionario de recursos de temas para el tema activo actualmente. Si el tema cambia en tiempo de ejecución, el valor se vuelve a evaluar.

  5. Se comprueban los recursos del sistema.

El comportamiento de excepción (si existe) varía:

  • Si una llamada FindResource solicita un recurso y no lo encuentra, se inicia una excepción.

  • Si una llamada TryFindResource solicita un recurso y no lo encuentra, no se inicia ninguna excepción y el valor devuelto es null. Si la propiedad que se establece no acepta null, entonces sigue siendo posible que se genere una excepción más detallada, en función de la propiedad individual que se establece.

  • En XAML, si una referencia de recursos dinámicos solicita un recurso y no se encuentra, el comportamiento depende del sistema general de propiedades. El comportamiento general es como si no se produjera ninguna operación de configuración de propiedad en el nivel en el que existe el recurso. Por ejemplo, si intenta establecer el fondo en un elemento de botón individual con un recurso que no puede evaluarse, entonces no se establece ningún valor, pero el valor efectivo todavía puede provenir de otros participantes del sistema de propiedades y la precedencia de valores. Por ejemplo, el valor de fondo todavía puede provenir de un estilo de botón definido localmente o desde el estilo del tema. Para las propiedades que no se definen mediante los estilos del tema, el valor efectivo después de una evaluación de recursos incorrecta puede provenir del valor predeterminado de los metadatos de la propiedad.

Restricciones

Las referencias de recursos dinámicos tienen algunas restricciones importantes. Se debe dar como mínimo una de las siguientes condiciones:

Como la propiedad que se establece debe ser una propiedad DependencyProperty o Freezable, la mayoría de los cambios de propiedad pueden propagarse a la interfaz de usuario porque se reconoce un cambio de propiedad (el valor de recurso dinámico que se ha cambiado) mediante el sistema de propiedades. La mayoría de los controles incluyen lógica que forzará a otro diseño de un control si DependencyProperty cambia y esa propiedad puede afectar al diseño. Sin embargo, no se garantiza que todas las propiedades que tienen una extensión de marcado DynamicResource como su valor proporcionen actualizaciones en tiempo real en la interfaz de usuario. Esa funcionalidad todavía puede variar en función de la propiedad y del tipo que posee la propiedad, o incluso de la estructura lógica de la aplicación.

Estilos, plantillas de datos y claves implícitas

Aunque todos los elementos de ResourceDictionary deben tener una clave, eso no significa que todos los recursos deban tener un valor x:Key explícito. Varios tipos de objetos admiten una clave implícita cuando se definen como un recurso, donde el valor de clave está vinculado al valor de otra propiedad. Este tipo de clave se conoce como clave implícita, y un atributo x:Key es una clave explícita. Puede sobrescribir cualquier clave implícita especificando una clave explícita.

Un escenario importante para los recursos es cuando se define un elemento Style. De hecho, un elemento Style casi siempre se define como una entrada en un diccionario de recursos, porque los estilos están previstos propiamente para que se vuelvan a usar. Para obtener más información sobre los estilos, consulte Estilos y plantillas (WPF .NET).

Los estilos para los controles pueden crearse y hacerse referencia con una clave implícita. Los estilos de tema que definen la apariencia predeterminada de un control se basan en esta clave implícita. La clave implícita desde el punto de vista de su solicitud, la clave implícita es el elemento Type del propio control. Desde el punto de vista de la definición de los recursos, la clave implícita es el elemento TargetType del estilo. Por lo tanto, si va a crear temas para controles personalizados o a crear estilos que interactúan con estilos de tema existentes, no es necesario especificar una directiva x:Key para ese Style. Y si desea usar los estilos temáticos, no es necesario especificar ningún estilo. Por ejemplo, la siguiente definición de estilo funciona aunque el recurso Style no parece tener una clave:

<Style TargetType="Button">
    <Setter Property="Background" Value="#4E1A3D" />
    <Setter Property="Foreground" Value="White" />
    <Setter Property="BorderThickness" Value="5" />
    <Setter Property="BorderBrush">
        <Setter.Value>
            <LinearGradientBrush>
                <GradientStop Offset="0.0" Color="#4E1A3D"/>
                <GradientStop Offset="1.0" Color="Salmon"/>
            </LinearGradientBrush>
        </Setter.Value>
    </Setter>
</Style>

Pero ese estilo sí tiene una clave: la clave implícita: el tipo System.Windows.Controls.Button. En el marcado puede especificar un TargetType directamente como el nombre de tipo o puede usar {x:Type...} para devolver un Type, opcionalmente.

A través de los mecanismos de estilo de tema predeterminados que usa WPF, ese estilo se aplica como estilo en tiempo de ejecución de Button en la página, aunque el propio elemento Button no intente especificar su propiedad Style o una referencia de recurso específica al estilo. Su estilo definido en la página se ha detectado en la secuencia de búsqueda antes que el estilo del diccionario de temas, con la misma clave que tiene el estilo del diccionario de temas. Puede especificar simplemente <Button>Hello</Button> en cualquier parte de la página; el estilo que haya definido con TargetType de Button se aplicará a ese botón. Si quiere, todavía puede especificar explícitamente el estilo con el mismo valor de tipo que TargetType, para mayor claridad en el marcado, pero esto es opcional.

Las claves implícitas de los estilos no se aplican a un control si OverridesDefaultStyle es true. (Tenga en cuenta también que OverridesDefaultStyle se puede establecer como parte del comportamiento nativo de la clase de control, en lugar de explícitamente en una instancia del control). Además, para admitir claves implícitas para escenarios de clase derivada, el control debe invalidar DefaultStyleKey (todos los controles existentes proporcionados como parte de WPF incluyen esta invalidación). Para obtener más información sobre estilos, temas y diseño de control, vea Instrucciones para el diseño de controles con estilos.

DataTemplate también tiene una clave implícita. La clave implícita de DataTemplate es el valor de la propiedad DataType. DataType también se puede especificar como nombre del tipo en lugar de usar explícitamente {x:Type...}. Para obtener información, vea Información general sobre plantillas de datos.

Vea también