Aplicar estilos y plantillas

Los estilos y plantillas de Windows Presentation Foundation (WPF) hacen referencia a un conjunto de características (estilos, plantillas, desencadenadores y guiones gráficos) que permiten a los desarrolladores y diseñadores crear efectos visualmente atractivos y crear una apariencia coherente para el producto. Aunque los desarrolladores y los diseñadores pueden personalizar ampliamente la apariencia de las aplicaciones una por una, se necesita un modelo robusto de estilos y plantillas para permitir el mantenimiento y el uso compartido de la apariencia tanto dentro de una aplicación como entre las diversas aplicaciones. Windows Presentation Foundation (WPF) proporciona ese modelo.

Otra característica del modelo de estilos de WPF es la separación de la presentación y la lógica. Esto significa que los diseñadores pueden trabajar en la apariencia de una aplicación usando solamente XAML, al mismo tiempo que los desarrolladores trabajan en la lógica de programación usando C# o Visual Basic.

Esta información general se centra en los aspectos de estilos y plantillas de la aplicación y no aborda los conceptos de enlace de datos. Para obtener información sobre el enlace de datos, vea Información general sobre el enlace de datos.

Además, es importante entender los recursos, que permiten reutilizar los estilos y las plantillas. Para obtener más información sobre los recursos, vea Información general sobre recursos.

Este tema contiene las secciones siguientes.

  • Ejemplo de aplicar estilos y plantillas
  • Fundamentos de estilos
  • Plantillas de datos
  • Plantillas de control
  • Desencadenadores
  • Recursos y temas compartidos
  • Temas relacionados

Ejemplo de aplicar estilos y plantillas

Los ejemplos de código empleados en esta información general se basan en una foto sencilla de ejemplo que se muestra en la siguiente ilustración:

ListView con estilo

Esta foto sencilla de ejemplo usa estilos y plantillas para crear una experiencia de usuario visualmente atractiva. El ejemplo tiene dos elementos TextBlock y un control ListBox que está enlazado a una lista de imágenes. Para obtener el ejemplo completo, vea Introducción al ejemplo de estilos y plantillas.

Fundamentos de estilos

Puede considerar un objeto Style como una manera cómoda de aplicar un conjunto de valores de propiedades a más de un elemento. Por ejemplo, considere los siguientes elementos TextBlock y su apariencia predeterminada:

<TextBlock>My Pictures</TextBlock>
<TextBlock>Check out my new pictures!</TextBlock>

Captura de pantalla de ejemplo de aplicación de estilos

Puede cambiar la apariencia predeterminada estableciendo propiedades como FontSize y FontFamily en cada elemento TextBlock directamente. Sin embargo, si desea que los elementos TextBlock compartan algunas propiedades, puede crear un objeto Style en la sección Resources del archivo XAML, como se muestra aquí:

<Window.Resources>


...


<!--A Style that affects all TextBlocks-->
<Style TargetType="TextBlock">
  <Setter Property="HorizontalAlignment" Value="Center" />
  <Setter Property="FontFamily" Value="Comic Sans MS"/>
  <Setter Property="FontSize" Value="14"/>
</Style>


...


</Window.Resources>

Cuando establezca la propiedad TargetType del estilo en el tipo TextBlock, el estilo se aplicará a todos los elementos TextBlock de la ventana.

Ahora, los elementos TextBlock aparecerán de la manera siguiente:

Captura de pantalla de ejemplo de aplicación de estilos

Extender los estilos

Quizás desee que los dos elementos TextBlock compartan algunos valores de propiedad, como FontFamily y un valor de HorizontalAlignment centrado, pero también desee que el texto "My Pictures" tenga algunas propiedades adicionales. Para ello, cree un nuevo estilo basado en el primer estilo, como se muestra aquí:

<Window.Resources>


...


<!--A Style that extends the previous TextBlock Style-->
<!--This is a "named style" with an x:Key of TitleText-->
<Style BasedOn="{StaticResource {x:Type TextBlock}}"
       TargetType="TextBlock"
       x:Key="TitleText">
  <Setter Property="FontSize" Value="26"/>
  <Setter Property="Foreground">
  <Setter.Value>
      <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
        <LinearGradientBrush.GradientStops>
          <GradientStop Offset="0.0" Color="#90DDDD" />
          <GradientStop Offset="1.0" Color="#5BFFFF" />
        </LinearGradientBrush.GradientStops>
      </LinearGradientBrush>
    </Setter.Value>
  </Setter>
</Style>


...


</Window.Resources>

Observe que al estilo anterior se le asigna un atributo x:Key. Para aplicar el estilo, establezca la propiedad Style del elemento TextBlock en el valor de x:Key, como se muestra aquí:

<TextBlock Style="{StaticResource TitleText}" Name="textblock1">My Pictures</TextBlock>
<TextBlock>Check out my new pictures!</TextBlock>

El estilo de este elemento TextBlock tiene ahora el valor de la propiedad HorizontalAlignment establecido en Center, el valor de la propiedad FontFamily establecido en Comic Sans MS, el valor de la propiedad FontSize establecido en 26 y el valor de la propiedad Foreground establecido en el objeto LinearGradientBrush que se muestra en el ejemplo. Observe que invalida el valor de FontSize del estilo base. Si hay más de un objeto Setter que establezca la misma propiedad de Style, el objeto Setter que se declare en último lugar tendrá prioridad.

A continuación se muestra la apariencia que tendrán los elementos TextBlock:

TextBlocks con estilo

Este estilo TitleText extiende el estilo creado para el tipo TextBlock. También puede extender un estilo que tenga un atributo x:Key utilizando el valor de x:Key. Para ver un ejemplo, vea el que se proporciona para la propiedad BasedOn.

Relación entre la propiedad TargetType y el atributo x:Key

Como se muestra en el primer ejemplo, si se establece la propiedad TargetType en TextBlock sin asignar al estilo un atributo x:Key, el estilo se aplicará a todos los elementos TextBlock. En este caso, el atributo x:Key se establece implícitamente en {x:Type TextBlock}. Esto significa que si se establece explícitamente el atributo x:Key en un valor que no sea {x:Type TextBlock}, el objeto Style no se aplicará automáticamente a todos los elementos TextBlock. Es preciso aplicar explícitamente el estilo (utilizando el valor de x:Key) a los elementos TextBlock. Si el estilo está en la sección de recursos y no se establece la propiedad TargetType del estilo, se deberá proporcionar un atributo x:Key.

Además de proporcionar un valor predeterminado para x:Key, la propiedad TargetType especifica el tipo al que se aplican las propiedades del establecedor. Si no se especifica un valor de TargetType, se deberán calificar las propiedades de los objetos Setter con un nombre de clase, usando la sintaxis Property="ClassName.Property". Por ejemplo, en lugar de establecer Property="FontSize", se deberá establecer Property en "TextBlock.FontSize" o "Control.FontSize".

Observe también que muchos controles de WPF son una combinación de otros controles de WPF. Si crea un estilo que se aplique a todos los controles de un tipo, puede que obtenga resultados inesperados. Por ejemplo, si crea un estilo cuyo destino sea el tipo de TextBlock en un objeto Window, el estilo se aplicará a todos los controles TextBlock de la ventana, aunque el elemento TextBlock forme parte de otro control, como un control ListBox.

Estilos y recursos

Puede utilizar un estilo en cualquier elemento que derive de FrameworkElement o FrameworkContentElement. La manera más común de declarar un estilo es declararlo como un recurso en la sección Resources de un archivo XAML, como se muestra en los ejemplos anteriores. Puesto que los estilos son recursos, siguen las mismas reglas de ámbito que se aplican a todos los recursos; la ubicación donde se declara un estilo afecta a la parte donde se puede aplicar. Por ejemplo, si se declara el estilo en el elemento raíz del archivo XAML de definición de la aplicación, el estilo se puede usar en cualquier parte de la aplicación. Si se crea una aplicación de navegación y se declara el estilo en uno de sus archivos XAML, el estilo sólo se puede usar en ese archivo XAML. Para obtener más información sobre las reglas de ámbito para los recursos, vea Información general sobre recursos.

Además, hay más información sobre los estilos y recursos en la sección Recursos y temas compartidos, más adelante en esta información general.

Establecer estilos mediante programación

Para asignar mediante programación un estilo con nombre a un elemento, obtenga el estilo de la colección de recursos y asígnelo a la propiedad Style del elemento. Observe que los elementos de una colección de recursos son del tipo Object. Por tanto, debe convertir el estilo recuperado a Style antes de asignarlo a la propiedad Style. Por ejemplo, para establecer el estilo TitleText definido de un control TextBlock denominado textblock1, siga este procedimiento:

textblock1.Style = CType(Me.Resources("TitleText"), Style)
textblock1.Style = (Style)(this.Resources["TitleText"]);

Tenga en cuenta que, una vez aplicado un estilo, éste queda sellado y no puede cambiarse. Si desea cambiar dinámicamente un estilo que ya se ha aplicado, deberá crear un nuevo estilo para reemplazar el existente. Para obtener más información, vea la propiedad IsSealed.

Puede crear un objeto que elija el estilo que se va a aplicar basándose en una lógica personalizada. Para obtener un ejemplo, vea el que se proporciona para la clase StyleSelector.

Enlaces, recursos dinámicos y controladores de eventos

Observe que puede usar la propiedad Setter.Value para especificar una Enlazar extensión de marcado o una Extensión de marcado DynamicResource. Para obtener más información, vea los ejemplos proporcionados para la propiedad Setter.Value.

Hasta ahora, en esta información general solo se ha explicado el uso de establecedores para establecer el valor de propiedad. En un estilo, también se pueden especificar los controladores de eventos. Para obtener más información, vea EventSetter.

Plantillas de datos

En esta aplicación de ejemplo, hay un control ListBox enlazado a una lista de fotos:

<ListBox ItemsSource="{Binding Source={StaticResource MyPhotos}}"
         Background="Silver" Width="600" Margin="10" SelectedIndex="0"/>

Este control ListBox tiene ahora la siguiente apariencia:

ListBox antes de aplicar una plantilla

La mayoría de los controles tienen algún tipo de contenido y ese contenido suele proceder de los datos a los que se está enlazando. En este ejemplo, los datos son la lista de fotos. En WPF, use DataTemplate para definir la representación visual de los datos. Básicamente, el contenido de DataTemplate determina la apariencia de los datos en la aplicación representada.

En la aplicación de ejemplo, cada objeto Photo personalizado tiene una propiedad Source de tipo cadena que especifica la ruta de acceso del archivo de imagen. Actualmente, los objetos de foto aparecen como rutas de acceso de archivo.

Para que las fotos aparezcan como imágenes, debe crear un objeto DataTemplate como un recurso:

<Window.Resources>


...


<!--DataTemplate to display Photos as images
    instead of text strings of Paths-->
<DataTemplate DataType="{x:Type local:Photo}">
  <Border Margin="3">
    <Image Source="{Binding Source}"/>
  </Border>
</DataTemplate>


...


</Window.Resources>

Observe que la propiedad DataType es muy similar a la propiedad TargetType de Style. Si DataTemplate está en la sección de recursos, al especificar la propiedad DataType en un tipo y no asignarle un atributo x:Key, DataTemplate se aplicará siempre que aparezca ese tipo. Siempre tiene la opción de asignar a DataTemplate un atributo x:Key y, a continuación, establecerlo como StaticResource para las propiedades que admiten tipos DataTemplate, como la propiedad ItemTemplate o la propiedad ContentTemplate.

El objeto DataTemplate del ejemplo anterior define esencialmente que, siempre que haya un objeto Photo, éste debe aparecer como un control Image dentro de un control Border. Con este objeto DataTemplate, la aplicación tiene ahora esta apariencia:

Imagen fotográfica

El modelo de plantillas de datos proporciona otras características. Por ejemplo, si se muestran datos de colección que contengan otras colecciones con un tipo HeaderedItemsControl como un control Menu o un control TreeView, ahí está el objeto HierarchicalDataTemplate. Otra característica de las plantillas de datos es el objeto DataTemplateSelector, que permite elegir el objeto DataTemplate que se va a utilizar basándose en una lógica personalizada. Para obtener más información, vea Información general sobre plantillas de datos, que ofrece una explicación más detallada de las diferentes características de las plantillas de datos.

Plantillas de control

En WPF, el objeto ControlTemplate de un control define la apariencia del control. Puede cambiar la estructura y la apariencia de un control definiendo un nuevo objeto ControlTemplate para el control. En muchos casos, esto ofrece suficiente flexibilidad como para no tener que escribir controles personalizados. Para obtener más información, vea Personalizar la apariencia de un control existente creando una clase ControlTemplate.

Desencadenadores

Un desencadenador establece propiedades o inicia acciones, como una animación, cuando cambia un valor de propiedad o cuando se genera un evento. Style, ControlTemplate y DataTemplate tienen una propiedad Triggers que puede contener un conjunto de desencadenadores. Hay varios tipos de desencadenadores.

Desencadenadores de propiedades

Un objeto Trigger que establece valores de propiedad o inicia acciones según el valor de una propiedad se denomina desencadenador de propiedad.

Para mostrar cómo se usan los desencadenadores de propiedades, puede hacer que cada elemento ListBoxItem sea parcialmente transparente a menos que esté seleccionado. El estilo siguiente establece el valor de la propiedad Opacity de un elemento ListBoxItem en 0.5. Cuando el valor de la propiedad IsSelected es true, sin embargo, el valor de Opacity se establece en 1.0:

<Style TargetType="ListBoxItem">
  <Setter Property="Opacity" Value="0.5" />
  <Setter Property="MaxHeight" Value="75" />
  <Style.Triggers>
    <Trigger Property="IsSelected" Value="True">
        <Setter Property="Opacity" Value="1.0" />
    </Trigger>


...


  </Style.Triggers>
</Style>

En este ejemplo se utiliza una clase Trigger para establecer un valor de propiedad, pero observe que la clase Trigger también tiene las propiedades EnterActions y ExitActions que permiten a un desencadenador realizar acciones.

Observe que la propiedad MaxHeight de ListBoxItem está establecida en 75. En la siguiente ilustración, el tercer elemento es el elemento seleccionado:

ListView con estilo

Desencadenadores de eventos y guiones gráficos

Otro tipo de desencadenador es EventTrigger, que inicia un conjunto de acciones en función de la aparición de un evento. Por ejemplo, los siguientes objetos EventTrigger especifican que, cuando el puntero del mouse entre en el elemento ListBoxItem, la propiedad MaxHeight se establezca en 90 durante un período de 0.2 segundos. Cuando el mouse sale del elemento, la propiedad se establece de nuevo en el valor original durante un período de 1 segundo. Observe que no es necesario especificar el valor de la propiedad To para la animación MouseLeave. Esto se debe a que la animación puede realizar un seguimiento del valor original.

        <EventTrigger RoutedEvent="Mouse.MouseEnter">
          <EventTrigger.Actions>
            <BeginStoryboard>
              <Storyboard>
                <DoubleAnimation
                  Duration="0:0:0.2"
                  Storyboard.TargetProperty="MaxHeight"
                  To="90"  />
              </Storyboard>
            </BeginStoryboard>
          </EventTrigger.Actions>
        </EventTrigger>
        <EventTrigger RoutedEvent="Mouse.MouseLeave">
          <EventTrigger.Actions>
            <BeginStoryboard>
              <Storyboard>
                <DoubleAnimation
                  Duration="0:0:1"
                  Storyboard.TargetProperty="MaxHeight"  />
              </Storyboard>
            </BeginStoryboard>
          </EventTrigger.Actions>
        </EventTrigger>

Para obtener más información, vea Información general sobre objetos Storyboard.

En la ilustración siguiente, el mouse está señalando el tercer elemento:

Captura de pantalla de ejemplo de aplicación de estilos

MultiTrigger, DataTrigger y MultiDataTrigger

Además de Trigger y EventTrigger, hay otros tipos de desencadenadores. MultiTrigger permite establecer valores de propiedad según varias condiciones. DataTrigger y MultiDataTrigger se utilizan cuando la propiedad de la condición está enlazada a datos.

Recursos y temas compartidos

Una aplicación Windows Presentation Foundation (WPF) típica puede tener varios recursos de interfaz de usuario aplicados en toda la aplicación. Colectivamente, este conjunto de recursos puede considerarse el tema para la aplicación. Windows Presentation Foundation (WPF) proporciona compatibilidad para empaquetar recursos de la interfaz de usuario (IU) como un tema usando un diccionario de recurso que se encapsula como la clase ResourceDictionary.

Los temas de Windows Presentation Foundation (WPF) se definen mediante el mecanismo de estilos y plantillas que Windows Presentation Foundation (WPF) expone para personalizar los efectos visuales de cualquier elemento.

Los recursos de tema de Windows Presentation Foundation (WPF) están almacenados en diccionarios de recursos incrustados. Estos diccionarios de recursos deben incrustarse en un ensamblado firmado y pueden incrustarse en el mismo ensamblado que el propio código o en un ensamblado paralelo. En el caso de PresentationFramework.dll, el ensamblado que contiene los controles de Windows Presentation Foundation (WPF), los recursos de tema están en una serie de ensamblados paralelos.

El tema se convierte en el último lugar donde se va a buscar el estilo de un elemento. Normalmente, la búsqueda de un recurso adecuado se realizará recorriendo primero de manera ascendente el árbol de elementos; a continuación, se buscará en la colección de recursos de la aplicación y, finalmente, se consultará el sistema. De este modo, los desarrolladores de aplicaciones podrán redefinir el estilo de cualquier objeto en el nivel de árbol o de aplicación antes de alcanzar el tema.

Los diccionarios de recursos se pueden definir como archivos individuales que permiten reutilizar un tema en varias aplicaciones. También se pueden crear temas intercambiables definiendo varios diccionarios de recursos que proporcionen los mismos tipos de recursos pero con diferentes valores. Redefinir estos estilos u otros recursos en el nivel de aplicación es el enfoque recomendado para aplicar una máscara a una aplicación.

Para compartir un conjunto de recursos entre aplicaciones, incluidos los estilos y las plantillas, puede crear un archivo XAML y definir un objeto ResourceDictionary. Por ejemplo, examine la siguiente ilustración que forma parte de Ejemplo de estilos con ControlTemplates:

Ejemplos de plantillas de controles

Si observa los archivos XAML del ejemplo, verá que todos los archivos tienen lo siguiente:

<ResourceDictionary.MergedDictionaries>
  <ResourceDictionary Source="Shared.xaml" />
</ResourceDictionary.MergedDictionaries>

Es el uso compartido de shared.xaml, que define un objeto ResourceDictionary que contiene un conjunto de recursos de estilo y de pincel que permite que los controles del ejemplo tengan una apariencia coherente.

Para obtener más información, vea Diccionarios de recursos combinados.

Si está creando un tema para el control personalizado, vea la sección Biblioteca de controles externos de Información general sobre la creación de controles.

Vea también

Tareas

Cómo: Buscar elementos generados por un objeto ControlTemplate

Cómo: Buscar elementos generados por un objeto DataTemplate

Conceptos

Empaquetar URI en WPF