Información general sobre WPF

Windows Presentation Foundation (WPF) le permite crear aplicaciones de cliente de escritorio para Windows con experiencias de usuario visualmente impactantes.

Ejemplo Contoso Healthcare UI

El núcleo de WPF es un motor de representación basado en vectores e independiente de la resolución que está diseñado para sacar partido al moderno hardware gráfico. WPF amplía el núcleo con un conjunto completo de características de desarrollo de aplicaciones que incluyen Extensible Application Markup Language (XAML), controles, enlace de datos, diseño, gráficos en 2D y 3D, animación, estilos, plantillas, documentos, elementos multimedia, texto y tipografía. WPF es parte de. NET, así que permite compilar aplicaciones que incorporan otros elementos de la API de .NET.

Esta introducción está dirigida a personas que aún no conocen WPF, y en ella se abordan sus conceptos y capacidades principales.

Programar con WPF

WPF existe como un subconjunto de tipos de .NET que, en su mayoría, están ubicados en el espacio de nombres System.Windows. Si ha compilado previamente aplicaciones con .NET mediante tecnologías administradas como ASP.NET y Windows Forms, la experiencia de programación fundamental en WPF debe resultarle familiar; cree instancias de clases, defina propiedades, llame a métodos y controle eventos con el lenguaje de programación de .NET que prefiera, como C# o Visual Basic.

WPF incluye construcciones de programación adicionales que mejoran las propiedades y los eventos: las propiedades de dependencia y los eventos enrutados.

Marcado y código subyacente

WPF permite desarrollar una aplicación que use tanto marcado como código subyacente, una experiencia que les resultará familiar a los desarrolladores de ASP.NET. En general, se usa marcado XAML para implementar la apariencia de una aplicación y lenguajes de programación administrados (código subyacente) para implementar su comportamiento. Esta separación entre la apariencia y el comportamiento tiene las ventajas siguientes:

  • Se reducen los costes de desarrollo y mantenimiento porque el marcado específico de la apariencia no está asociado estrechamente al código específico del comportamiento.

  • La programación es más eficaz porque los diseñadores pueden implementar la apariencia de una aplicación al mismo tiempo que los programadores implementan su comportamiento.

  • Laglobalización y localización de las aplicaciones WPF se ha simplificado.

marcado

XAML es un lenguaje de marcado basado en XML que se usa para implementar la apariencia de una aplicación mediante declaración. Se suele usar para crear ventanas, cuadros de diálogo, páginas y controles de usuario, así como para rellenarlos con controles, formas y gráficos.

En el ejemplo siguiente se usa XAML para implementar la apariencia de una ventana que contiene un solo botón:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    Title="Window with Button"
    Width="250" Height="100">

  <!-- Add button to window -->
  <Button Name="button">Click Me!</Button>

</Window>

En concreto, este código XAML define una ventana y un botón mediante el uso de los elementos Window y Button , respectivamente. Cada elemento se configura con atributos como, por ejemplo, el atributo Window del elemento Title para especificar el texto de la barra de título de la ventana. En tiempo de ejecución, WPF convierte los elementos y los atributos que se definen en el marcado en instancias de clases de WPF. Por ejemplo, el elemento Window se convierte en una instancia de la clase Window cuya propiedad Title es el valor del atributo Title .

En la ilustración siguiente se muestra la interfaz de usuario definida mediante el código XAML del ejemplo anterior:

Ventana que contiene un botón

Dado que XAML se basa en XML, la interfaz de usuario que se crea con este lenguaje se ensambla en una jerarquía de elementos anidados, que se denomina árbol de elementos. El árbol de elementos proporciona una manera lógica e intuitiva para crear y administrar las interfaces de usuario.

Código subyacente

El comportamiento principal de una aplicación consiste en implementar la funcionalidad que responde a las interacciones del usuario, lo que incluye controlar los eventos (por ejemplo, hacer clic en un menú, una barra de herramientas o un botón) y llamar, en respuesta, a la lógica de negocios y al acceso a los datos. En WPF, este comportamiento se implementa en el código que está asociado al marcado. Este tipo de código se conoce como código subyacente. En el ejemplo siguiente se muestra el marcado actualizado del ejemplo anterior y el código subyacente:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.AWindow"
    Title="Window with Button"
    Width="250" Height="100">

  <!-- Add button to window -->
  <Button Name="button" Click="button_Click">Click Me!</Button>

</Window>
using System.Windows; // Window, RoutedEventArgs, MessageBox

namespace SDKSample
{
    public partial class AWindow : Window
    {
        public AWindow()
        {
            // InitializeComponent call is required to merge the UI
            // that is defined in markup with this class, including  
            // setting properties and registering event handlers
            InitializeComponent();
        }

        void button_Click(object sender, RoutedEventArgs e)
        {
            // Show message box when button is clicked.
            MessageBox.Show("Hello, Windows Presentation Foundation!");
        }
    }
}
Namespace SDKSample

    Partial Public Class AWindow
        Inherits System.Windows.Window

        Public Sub New()

            ' InitializeComponent call is required to merge the UI
            ' that is defined in markup with this class, including  
            ' setting properties and registering event handlers
            InitializeComponent()

        End Sub

        Private Sub button_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)

            ' Show message box when button is clicked.
            MessageBox.Show("Hello, Windows Presentation Foundation!")

        End Sub

    End Class

End Namespace

En este ejemplo, el código subyacente implementa una clase que deriva de la clase Window . El atributo x:Class se usa para asociar el marcado a la clase de código subyacente. Se llama a InitializeComponent desde el constructor de la clase de código subyacente para combinar la interfaz de usuario que se define en el marcado con la clase de código subyacente. (InitializeComponent se genera automáticamente cuando se compila la aplicación, por lo que no es necesario implementarlo manualmente). La combinación de x:Class y InitializeComponent garantiza que la implementación se inicialice correctamente cada vez que se crea. La clase de código subyacente también implementa un controlador de eventos para el evento Click del botón. Cuando se hace clic en el botón, el controlador de eventos muestra un cuadro de mensaje mediante una llamada al método System.Windows.MessageBox.Show .

En la siguiente ilustración se muestra el resultado de hacer clic en el botón:

MessageBox

Controles

Las experiencias de usuario proporcionadas por el modelo de aplicación son controles construidos. En WPF, control es un término genérico que se aplica a una categoría de clases de WPF que se hospedan en una ventana o una página, tienen una interfaz de usuario e implementan un comportamiento determinado.

Para obtener más información, consulte Controles.

Controles de WPF por función

A continuación se muestra la lista de controles de WPF integrados:

Entrada y comandos

Los controles casi siempre detectan las acciones del usuario y responden a ellas. El sistema de entrada de WPF usa eventos directos y enrutados para admitir la entrada de texto, la administración del enfoque y la posición del mouse.

Las aplicaciones a menudo tienen tener requisitos de entrada complejos. WPF proporciona un sistema de comandos que separa las acciones de entrada del usuario del código que responde a esas acciones.

Diseño

Al crear crear una interfaz de usuario, se organizan los controles según su ubicación y tamaño para crear un diseño. Un requisito fundamental de cualquier diseño es adaptarse a los cambios de tamaño de la ventana y de configuración de pantalla. En lugar de obligarle a escribir código que adapte el diseño en estas circunstancias, WPF le proporciona un sistema de diseño extensible de primera clase.

La piedra angular del sistema de diseño es la posición relativa, que aumenta la capacidad de adaptación a los cambios en la configuración de las ventanas y la pantalla. Además, el sistema de diseño administra la negociación entre los controles para determinar el diseño. La negociación es un proceso de dos pasos: en primer lugar, el control indica a su elemento primario qué ubicación y tamaño necesita; en segundo lugar, el elemento primario indica al control cuánto espacio dispone.

El sistema de diseño se expone a los controles secundarios mediante las clases base de WPF. Para los diseños comunes, como las cuadrículas, el apilamiento y el acoplamiento, WPF incluye varios controles de diseño:

  • Canvas: los controles secundarios proporcionan su propio diseño.

  • DockPanel: los controles secundarios se alinean con los bordes del panel.

  • Grid: los controles secundarios se colocan por filas y columnas.

  • StackPanel: los controles secundarios se apilan vertical u horizontalmente.

  • VirtualizingStackPanel: los controles secundarios se virtualizan y se organizan en una sola línea en sentido horizontal o verticalmente.

  • WrapPanel: los controles secundarios se sitúan en orden de izquierda a derecha y se ajustan a la línea siguiente cuando hay más controles de los que caben en la línea actual.

En el ejemplo siguiente se usa un control DockPanel para situar varios controles TextBox:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.LayoutWindow"
    Title="Layout with the DockPanel" Height="143" Width="319">
  
  <!--DockPanel to layout four text boxes--> 
  <DockPanel>
    <TextBox DockPanel.Dock="Top">Dock = "Top"</TextBox>
    <TextBox DockPanel.Dock="Bottom">Dock = "Bottom"</TextBox>
    <TextBox DockPanel.Dock="Left">Dock = "Left"</TextBox>
    <TextBox Background="White">This TextBox "fills" the remaining space.</TextBox>
  </DockPanel>

</Window>

DockPanel permite que los controles secundarios TextBox le indiquen cómo se deben organizar. Para ello, DockPanel implementa una propiedad adjunta Dock que se expone a los controles secundarios para permitir que cada uno de ellos especifique un estilo de acoplamiento.

Nota

Una propiedad implementada por un control principal para que la usen los controles secundarios es una construcción de WPF denominada propiedad adjunta.

En la siguiente ilustración se muestra el resultado del marcado XAML del ejemplo anterior:

Página de DockPanel

Enlace de datos

La mayoría de las aplicaciones se crean para proporcionar a los usuarios recursos que les permitan ver y editar datos. Para las aplicaciones de WPF, el trabajo de almacenamiento de datos y el acceso a ellos ya se proporciona mediante tecnologías existentes, como SQL Server y ADO .NET. Una vez que se tiene acceso a los datos y se cargan en los objetos administrados de la aplicación, comienza la tarea ardua de las aplicaciones WPF. En esencia, esto implica dos cosas:

  1. Copiar los datos de los objetos administrados a los controles, donde los datos se pueden mostrar y editar.

  2. Asegurarse de que los cambios realizados en los datos mediante los controles se vuelvan a copiar a los objetos administrados.

Para simplificar el desarrollo de aplicaciones, WPF ofrece un motor de enlace de datos que sigue estos pasos automáticamente. La unidad que constituye el núcleo del motor de enlace de datos es la clase Binding , que se encarga de enlazar un control (el destino de enlace) a un objeto de datos (el origen de enlace). Esta relación se muestra en la ilustración siguiente:

Diagrama de enlace de datos básico

En el ejemplo siguiente, se muestra cómo enlazar un control TextBox a una instancia de un objeto Person personalizado. La implementación de Person se muestra en el código siguiente:

Namespace SDKSample

    Class Person

        Private _name As String = "No Name"

        Public Property Name() As String
            Get
                Return _name
            End Get
            Set(ByVal value As String)
                _name = value
            End Set
        End Property

    End Class

End Namespace
namespace SDKSample
{
    class Person
    {
        string name = "No Name";

        public string Name
        {
            get { return name; }
            set { name = value; }
        }
    }
}

El siguiente marcado enlaza el TextBox a una instancia de un objeto Person personalizado:

 <Window
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     x:Class="SDKSample.DataBindingWindow">

   <!-- Bind the TextBox to the data source (TextBox.Text to Person.Name) -->
   <TextBox Name="personNameTextBox" Text="{Binding Path=Name}" />

 </Window>
Imports System.Windows ' Window

Namespace SDKSample

    Partial Public Class DataBindingWindow
        Inherits Window

        Public Sub New()
            InitializeComponent()

            ' Create Person data source
            Dim person As Person = New Person()

            ' Make data source available for binding
            Me.DataContext = person

        End Sub

    End Class

End Namespace
using System.Windows; // Window

namespace SDKSample
{
    public partial class DataBindingWindow : Window
    {
        public DataBindingWindow()
        {
            InitializeComponent();

            // Create Person data source
            Person person = new Person();

            // Make data source available for binding
            this.DataContext = person;
        }
    }
}

En este ejemplo, se crea una instancia de la clase Person en el código subyacente y se establece como el contexto de datos para DataBindingWindow. En el marcado, la propiedad Text de TextBox se enlaza a la propiedad Person.Name (mediante la sintaxis "{Binding ... }" de XAML). Este código XAML indica a WPF que enlace el control TextBox al objeto Person almacenado en la propiedad DataContext de la ventana.

El motor de enlace de datos de WPF proporciona compatibilidad adicional que incluye validación, ordenación, filtrado y agrupación. Además, el enlace de datos admite el uso de plantillas de datos para crear una interfaz de usuario personalizada para los datos enlazados cuando la interfaz de usuario mostrada por los controles estándar de WPF no es adecuada.

Para obtener más información, vea Información general sobre el enlace de datos.

Gráficos

WPF incluye un conjunto extenso, escalable y flexible de características de gráficos que tienen las siguientes ventajas:

  • Gráficos independientes de la resolución e independientes del dispositivo. La unidad de medida básica del sistema de gráficos de WPF es el píxel independiente del dispositivo, que es 1/96 de pulgada (sea cual fuere la resolución de pantalla real), y proporciona la base para la representación independiente de la resolución y del dispositivo. Cada píxel independiente del dispositivo se escala automáticamente para que coincida con la configuración de puntos por pulgada (ppp) del sistema en que se representa.

  • Precisión mejorada. El sistema de coordenadas de WPF se mide con números de punto flotante de precisión doble en lugar de precisión sencilla. Las transformaciones y los valores de opacidad también se expresan como de precisión doble. WPF admite, además, una gama de color amplia (scRGB) y ofrece compatibilidad integrada para la administración de entradas de diferentes espacios de color.

  • Compatibilidad con gráficos y animación avanzados. WPF simplifica la programación de gráficos administrando automáticamente las escenas de animación. No tendrá que preocuparse por el procesamiento de escenas, los bucles de presentación ni la interpolación bilineal. Además, WPF admite la prueba de clics y proporciona compatibilidad plena con la composición alfa.

  • Aceleración de hardware. El sistema de gráficos de WPF saca partido del hardware de gráficos para minimizar el uso de la CPU.

Formas en 2D

WPF proporciona una biblioteca de formas en 2D comunes dibujadas mediante vectores, como los rectángulos y las elipses que se muestran en la ilustración siguiente:

Elipses y rectángulos

Una función interesante de las formas es que no sirven únicamente para su presentación; las formas implementan muchas de las características que cabe esperar de los controles, incluida la entrada de datos desde el teclado y el mouse. En el ejemplo siguiente se muestra el evento MouseUp de un Ellipse que se está controlando:

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.EllipseEventHandlingWindow"
    Title="Click the Ellipse">
    <Ellipse Name="clickableEllipse" Fill="Blue" MouseUp="clickableEllipse_MouseUp" />
</Window>
Imports System.Windows ' Window, MessageBox
Imports System.Windows.Input ' MouseButtonEventArgs

Namespace SDKSample

    Public Class EllipseEventHandlingWindow
        Inherits Window

        Public Sub New()
            InitializeComponent()
        End Sub

        Private Sub clickableEllipse_MouseUp(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
            MessageBox.Show("You clicked the ellipse!")
        End Sub

    End Class

End Namespace
using System.Windows; // Window, MessageBox
using System.Windows.Input; // MouseButtonEventHandler

namespace SDKSample
{
    public partial class EllipseEventHandlingWindow : Window
    {
        public EllipseEventHandlingWindow()
        {
            InitializeComponent();
        }

        void clickableEllipse_MouseUp(object sender, MouseButtonEventArgs e)
        {
            // Display a message
            MessageBox.Show("You clicked the ellipse!");
        }
    }
}

La siguiente ilustración muestra lo que genera el código anterior:

Ventana con el texto

Para obtener más información, vea Información general sobre formas y dibujo básico en WPF.

Geometrías en 2D

Las formas en 2D que proporciona WPF abarcan el conjunto estándar de formas básicas. Pero tal vez sea necesario crear formas personalizadas para facilitar el diseño de una interfaz de usuario personalizada. Para ello, WPF proporciona las geometrías. En la siguiente ilustración se muestra el uso de geometrías para crear una forma personalizada que se puede dibujar directamente, usar como un pincel o usar para recortar otras formas y controles.

Los objetosPath se pueden usar para dibujar formas cerradas o abiertas, varias formas o incluso formas curvas.

Los objetos Geometry se pueden usar para el recorte, la prueba de posicionamiento y la representación de datos de gráficos en 2D.

Varios usos de una ruta de acceso

Para obtener más información, vea Información general sobre geometría.

Efectos en 2D

Un subconjunto de funciones en 2D de WPF incluye los efectos visuales, tales como degradados, mapas de bits, dibujos, pintar con vídeos, rotación, escala y sesgado. Todos ellos se realizan con pinceles; en la siguiente ilustración se muestran algunos ejemplos:

Ilustración de diferentes pinceles

Para obtener más información, vea Información general sobre pinceles de WPF.

Representación en 3D

WPF también incluye funciones de representación en 3D que se integran con los gráficos en 2D para permitir la creación de interfaces de usuario más atractivas e interesantes. Por ejemplo, en la siguiente ilustración se muestran imágenes en 2D representadas en formas en 3D:

Captura de pantalla de ejemplo Visual3D

Para obtener más información, vea Información general sobre gráficos 3D.

Animación

El soporte de animación de WPF permite hacer que los controles crezcan, tiemblen, giren o se desvanezcan, crear transiciones de página interesantes y mucho más. Se puede animar la mayoría de las clases de WPF, incluso las clases personalizadas. En la siguiente ilustración se muestra una animación simple en acción:

Imágenes de un cubo animado

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

Medios

Una manera de mostrar contenido enriquecido es mediante el uso de medios audiovisuales (multimedia). WPF proporciona compatibilidad especial con imágenes, vídeo y audio.

Imágenes

Las imágenes son comunes a la mayoría de las aplicaciones y WPF proporciona varias maneras de usarlas. La siguiente ilustración muestra una interfaz de usuario con un cuadro de lista que contiene imágenes en miniatura. Cuando se selecciona una miniatura, aparece la imagen a tamaño completo.

Imágenes en miniatura e imagen con tamaño total

Para obtener más información, vea Información general sobre imágenes.

Vídeo y audio

El control MediaElement es capaz de reproducir vídeo y audio, y es lo suficientemente flexible como para ser la base de un reproductor multimedia personalizado. El marcado XAML siguiente implementa un reproductor multimedia:

<MediaElement 
  Name="myMediaElement" 
  Source="media/wpf.wmv" 
  LoadedBehavior="Manual" 
  Width="350" Height="250" />

La ventana en la siguiente ilustración muestra el control MediaElement en acción:

Control MediaElement con audio y vídeo

Para obtener más información, vea Gráficos y multimedia.

Texto y tipografía

Para facilitar la representación de texto de alta calidad, WPF ofrece las siguientes características:

  • Compatibilidad con fuentes OpenType.

  • Mejoras de ClearType.

  • Alto rendimiento que saca partido de la aceleración de hardware.

  • Integración de texto con multimedia, gráficos y animación.

  • Compatibilidad con fuentes internacionales y mecanismos de reserva.

A modo de demostración de la integración del texto con gráficos, en la siguiente ilustración se muestra la implementación de decoraciones en el texto:

Texto con diversas decoraciones de texto

Para obtener más información, consulte Tipografía en Windows Presentation Foundation.

Personalización de aplicaciones de WPF

Hasta este punto, hemos estudiado los bloques de creación de WPF fundamentales para desarrollar aplicaciones. El modelo de aplicación se usa para hospedar y distribuir el contenido de las aplicaciones, que está compuesto principalmente de controles. Para simplificar la organización de los controles de una interfaz de usuario y asegurarse de que la organización se conserve aunque se modifiquen el tamaño de la ventana y la configuración de pantalla, se usa el sistema de diseño de WPF. Debido a que la mayoría de las aplicaciones permiten a los usuarios interactuar con los datos, se usa el enlace de datos para reducir el trabajo de la integración de la interfaz de usuario con los datos. Para mejorar la apariencia visual de la aplicación, se usa la amplia gama de gráficos, animación y multimedia compatibilidad que proporciona WPF.

Sin embargo, a menudo los conceptos básicos no bastan para crear y administrar realmente una experiencia del usuario diferenciada y visualmente impactante. Es posible que los controles estándar de WPF no se integren con la apariencia deseada de la aplicación. Es posible que los datos no se muestren del modo más eficaz. La apariencia y el funcionamiento predeterminados de los temas de Windows pueden no ser adecuados para proporcionar la experiencia global del usuario con respecto a la aplicación. En muchos aspectos, una tecnología de presentación requiere de extensibilidad visual tanto como cualquier otro tipo de extensibilidad.

Por este motivo, WPF proporciona una variedad de mecanismos para crear experiencias del usuario únicas, incluido un modelo de contenido enriquecido para los controles, desencadenadores, control y plantillas de datos, estilos, recursos de interfaz de usuario y temas y máscaras.

Modelo de contenido

El propósito principal de la mayoría de los controles de WPF es mostrar el contenido. En WPF, el tipo y el número de elementos que pueden constituir el contenido de un control se conoce como modelo de contenidodel control. Algunos controles pueden contener un solo elemento y tipo de contenido. Por ejemplo, el contenido de un control TextBox es un valor de cadena que se asigna a la propiedad Text . En el siguiente ejemplo se establece el contenido de un TextBox:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.TextBoxContentWindow"
    Title="TextBox Content">

    <TextBox Text="This is the content of a TextBox." />
</Window>

En la siguiente ilustración se muestra el resultado:

Control TextBox que contiene texto

Otros controles, sin embargo, pueden contener varios elementos de diferentes tipos de contenido. El contenido de un control Button, especificado por la propiedad Content, puede contener diversos elementos, incluidos los controles de diseño, texto, imágenes y formas. En el siguiente ejemplo se muestra un Button con contenido que incluye un DockPanel, un Label, un Border y un MediaElement:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.ButtonContentWindow"
    Title="Button Content">

  <Button Margin="20">
    <!-- Button Content -->
    <DockPanel Width="200" Height="180">
      <Label DockPanel.Dock="Top" HorizontalAlignment="Center">Click Me!</Label>
      <Border Background="Black" BorderBrush="Yellow" BorderThickness="2"
        CornerRadius="2" Margin="5">
        <MediaElement Source="media/wpf.wmv" Stretch="Fill" />
      </Border>
    </DockPanel>
  </Button>
</Window>

En la siguiente ilustración se muestra el contenido de este botón:

Botón que tiene varios tipos de contenido

Para obtener más información sobre los tipos de contenido admitidos por los diversos controles, vea Modelo de contenido de WPF.

Desencadenadores

Aunque el propósito principal del marcado XAML es implementar la apariencia de la aplicación, también se puede usar XAML para implementar algunos aspectos del comportamiento de la aplicación. Un ejemplo es el uso de desencadenadores para cambiar la apariencia de una aplicación según las interacciones del usuario. Para más información, vea Estilos y plantillas.

Plantillas de control

Normalmente, las interfaces de usuario predeterminadas para los controles de WPF se construyen a partir de otros controles y formas. Por ejemplo, un control Button está compuesto por los controles ButtonChrome y ContentPresenter . ButtonChrome proporciona la apariencia del botón estándar, mientras que ContentPresenter muestra el contenido del botón, según lo especificado por la propiedad Content .

A veces, la apariencia predeterminada de un control puede ser incongruente con la apariencia general de una aplicación. En este caso, se puede usar un control ControlTemplate para cambiar la apariencia de la interfaz de usuario del control sin modificar su contenido ni su comportamiento.

En el ejemplo siguiente se muestra cómo cambiar la apariencia de un Button mediante un ControlTemplate:

<Window 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.ControlTemplateButtonWindow"
  Title="Button with Control Template" Height="158" Width="290">

  <!-- Button using an ellipse -->
  <Button Content="Click Me!" Click="button_Click">
    <Button.Template>
      <ControlTemplate TargetType="{x:Type Button}">
        <Grid Margin="5">
          <Ellipse Stroke="DarkBlue" StrokeThickness="2">
            <Ellipse.Fill>
              <RadialGradientBrush Center="0.3,0.2" RadiusX="0.5" RadiusY="0.5">
                <GradientStop Color="Azure" Offset="0.1" />
                <GradientStop Color="CornflowerBlue" Offset="1.1" />
              </RadialGradientBrush>
            </Ellipse.Fill>
          </Ellipse>
          <ContentPresenter Name="content" HorizontalAlignment="Center" 
            VerticalAlignment="Center"/>
        </Grid>
      </ControlTemplate>
    </Button.Template>

  </Button>

</Window>
using System.Windows; // Window, RoutedEventArgs, MessageBox

namespace SDKSample
{
    public partial class ControlTemplateButtonWindow : Window
    {
        public ControlTemplateButtonWindow()
        {
            InitializeComponent();
        }

        void button_Click(object sender, RoutedEventArgs e)
        {
            // Show message box when button is clicked
            MessageBox.Show("Hello, Windows Presentation Foundation!");
        }
    }
}
Imports System.Windows ' Window, RoutedEventArgs, MessageBox

Namespace SDKSample

    Public Class ControlTemplateButtonWindow
        Inherits Window

        Public Sub New()

            InitializeComponent()

        End Sub

        Private Sub button_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            MessageBox.Show("Hello, Windows Presentation Foundation!")
        End Sub

    End Class

End Namespace

En este ejemplo, la interfaz de usuario del botón predeterminado se ha reemplazado por una forma Ellipse que tiene un borde azul marino y se rellena mediante RadialGradientBrush. El control ContentPresenter muestra el contenido de Button, «Hacer clic aquí». Cuando se hace clic en Button, el evento Click se sigue produciendo como parte del comportamiento predeterminado del control Button. El resultado se muestra en la ilustración siguiente:

Botón elíptico y una segunda ventana

Plantillas de datos

Mientras que una plantilla de control permite especificar la apariencia de un control, una plantilla de datos permite especificar la apariencia del contenido del control. Las plantillas de datos se usan con frecuencia para mejorar la manera de mostrar los datos enlazados. En la siguiente ilustración se muestra la apariencia predeterminada de un control ListBox que está enlazado a una colección de objetos Task, donde cada tarea tiene un nombre, una descripción y una prioridad:

Cuadro de lista con el aspecto predeterminado

La apariencia predeterminada es la que cabría esperar de un control ListBox. Sin embargo, la apariencia predeterminada de cada tarea contiene únicamente el nombre de tarea. Para mostrar el nombre de la tarea, la descripción y la prioridad, la apariencia predeterminada de los elementos de lista enlazados al control ListBox se debe modificar mediante una plantilla de datos DataTemplate. En el código XAML siguiente se define la plantilla de datos DataTemplate, que se aplica a cada tarea mediante el atributo ItemTemplate:

<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.DataTemplateWindow"
  Title="With a Data Template">
  <Window.Resources>
    <!-- Data Template (applied to each bound task item in the task collection) -->
    <DataTemplate x:Key="myTaskTemplate">
      <Border Name="border" BorderBrush="DarkSlateBlue" BorderThickness="2"
        CornerRadius="2" Padding="5" Margin="5">
        <Grid>
          <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
          </Grid.RowDefinitions>
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
          </Grid.ColumnDefinitions>
          <TextBlock Grid.Row="0" Grid.Column="0" Padding="0,0,5,0" Text="Task Name:"/>
          <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=TaskName}"/>
          <TextBlock Grid.Row="1" Grid.Column="0" Padding="0,0,5,0" Text="Description:"/>
          <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}"/>
          <TextBlock Grid.Row="2" Grid.Column="0" Padding="0,0,5,0" Text="Priority:"/>
          <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Priority}"/>
        </Grid>
      </Border>
    </DataTemplate>
  </Window.Resources>

  <!-- UI -->
  <DockPanel>
    <!-- Title -->
    <Label DockPanel.Dock="Top" FontSize="18" Margin="5" Content="My Task List:"/>

    <!-- Data template is specified by the ItemTemplate attribute -->
    <ListBox
      ItemsSource="{Binding}"
      ItemTemplate="{StaticResource myTaskTemplate}"
      HorizontalContentAlignment="Stretch"
      IsSynchronizedWithCurrentItem="True"
      Margin="5,0,5,5" />

 </DockPanel>
</Window>

En la siguiente ilustración se muestra el efecto de este código:

Cuadro de lista que usa una plantilla de datos

Observe que el control ListBox conserva su comportamiento y apariencia general; sólo se modificó la apariencia del contenido mostrado por el cuadro de lista.

Para obtener más información, vea Información general sobre plantillas de datos.

Estilos

Los estilos permiten que los desarrolladores y diseñadores estandaricen un aspecto determinado de su producto. WPF proporciona un modelo de estilo eficaz, cuya base es el elemento Style . En el siguiente ejemplo se crea un estilo que establece el color de fondo de todos los Button de una ventana en Orange:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.StyleWindow"
    Title="Styles">
    
    <Window.Resources>
        <!-- Style that will be applied to all buttons for this window -->
        <Style TargetType="{x:Type Button}">
            <Setter Property="Background" Value="Orange" />
            <Setter Property="BorderBrush" Value="Crimson" />
            <Setter Property="FontSize" Value="20" />
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="Margin" Value="5" />
        </Style>
    </Window.Resources>
    <StackPanel>

        <!-- This button will have the style applied to it -->
        <Button>Click Me!</Button>

        <!-- This label will not have the style applied to it -->
        <Label>Don't Click Me!</Label>

        <!-- This button will have the style applied to it -->
        <Button>Click Me!</Button>
        
    </StackPanel>
</Window>

Dado que este estilo tiene como destino todos los controles Button, se aplica automáticamente a todos los botones de la ventana, tal como se muestra en la ilustración siguiente:

Dos botones de color naranja

Para más información, vea Estilos y plantillas.

Recursos

Los controles de una aplicación deben compartir la misma apariencia, que puede incluir todo tipo de recursos, desde fuentes y colores de fondo hasta plantillas de control, plantillas de datos y estilos. Se puede usar la compatibilidad de WPF con los recursos de la interfaz de usuario para encapsular estos recursos en una ubicación única y poder reutilizarlos.

En el ejemplo siguiente se define un color de fondo común que comparten los controles Button y Label:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.ResourcesWindow"
    Title="Resources Window">

  <!-- Define window-scoped background color resource -->
  <Window.Resources>
    <SolidColorBrush x:Key="defaultBackground" Color="Red" />
  </Window.Resources>

  <!-- Button background is defined by window-scoped resource -->
  <Button Background="{StaticResource defaultBackground}">One Button</Button>

  <!-- Label background is defined by window-scoped resource -->
  <Label Background="{StaticResource defaultBackground}">One Label</Label>
</Window>

En este ejemplo se implementa un recurso de color de fondo mediante el elemento de propiedad Window.Resources . Este recurso está disponible para todos los elementos secundarios de Window. Hay una gran variedad de ámbitos de recursos, incluidos los siguientes, que se muestran en el orden en que se resuelven:

  1. Un control individual (mediante la propiedad System.Windows.FrameworkElement.Resources heredada).

  2. Window o Page (también mediante la propiedad System.Windows.FrameworkElement.Resources heredada).

  3. Application (mediante la propiedad System.Windows.Application.Resources ).

La variedad de ámbitos aporta flexibilidad al desarrollador con respecto a la manera de definir y compartir los recursos.

Como alternativa para asociar directamente los recursos a un ámbito determinado, se puede empaquetar uno o varios recursos mediante un ResourceDictionary independiente al que puede hacer referencia en otras partes de una aplicación. Por ejemplo, a continuación se define un color de fondo predeterminado en un diccionario de recursos:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <!-- Define background color resource -->
  <SolidColorBrush x:Key="defaultBackground" Color="Red" />

  <!-- Define other resources -->
</ResourceDictionary>

En el ejemplo siguiente se hace referencia al diccionario de recursos definido en el ejemplo anterior para compartirlo en toda la aplicación:

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.App">

  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="BackgroundColorResources.xaml"/>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application>

Los recursos y los diccionarios de recursos constituyen la base de la compatibilidad de WPF con los temas y las máscaras.

Para obtener más información, consulte Recursos.

Controles personalizados

Aunque WPF proporciona una amplísima compatibilidad con funciones de personalización, puede encontrar situaciones en que los controles existentes de WPF no satisfagan las necesidades de la aplicación o de los usuarios. Esto puede suceder si:

  • La UI que se necesita no puede crearse personalizando la apariencia y el funcionamiento de las implementaciones de WPF existentes.

  • Las implementaciones de WPF existentes no admiten el comportamiento que se necesita (o lo admiten, pero no fácilmente).

Sin embargo, en este punto puede sacar partido de uno de los tres modelos de WPF para crear un nuevo control. Cada modelo está destinado a un escenario concreto y necesita que el control personalizado se derive de una clase base de WPF determinada. A continuación se muestran los tres modelos:

  • Modelo de control de usuario. El control personalizado se deriva de UserControl y se crea a partir de uno o varios controles.

  • Modelo de control. El control personalizado se deriva de Control y se usa para crear implementaciones que separan su comportamiento de su apariencia mediante plantillas, de un modo muy similar a la mayoría de los controles de WPF. Al derivarse de Control , permite mayor libertad para la creación de una UI personalizada que los controles de usuario, pero puede requerir más esfuerzo.

  • Modelo de elemento de marco de trabajo. Un control personalizado se deriva de FrameworkElement cuando su apariencia se define mediante la lógica de representación personalizada (no mediante plantillas).

En el ejemplo siguiente se muestra un control numérico de arriba/abajo personalizado derivado de UserControl:

<UserControl
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.NumericUpDown">

  <Grid>

    <Grid.RowDefinitions>
      <RowDefinition/>
      <RowDefinition/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition/>
      <ColumnDefinition/>
    </Grid.ColumnDefinitions>

    <!-- Value text box -->
    <Border BorderThickness="1" BorderBrush="Gray" Margin="2" Grid.RowSpan="2" 
      VerticalAlignment="Center" HorizontalAlignment="Stretch">
      <TextBlock Name="valueText" Width="60" TextAlignment="Right" Padding="5"/>
    </Border>

    <!-- Up/Down buttons -->
    <RepeatButton Name="upButton" Click="upButton_Click" Grid.Column="1" 
      Grid.Row="0">Up</RepeatButton>
    <RepeatButton Name="downButton" Click="downButton_Click" Grid.Column="1" 
      Grid.Row="1">Down</RepeatButton>

  </Grid>

</UserControl>
using System; // EventArgs
using System.Windows; // DependencyObject, DependencyPropertyChangedEventArgs,
                      // FrameworkPropertyMetadata, PropertyChangedCallback,
                      // RoutedPropertyChangedEventArgs
using System.Windows.Controls; // UserControl

namespace SDKSample
{
    public partial class NumericUpDown : UserControl
    {
        // NumericUpDown user control implementation
    }
}
imports System 'EventArgs
imports System.Windows 'DependencyObject, DependencyPropertyChangedEventArgs, 
                       ' FrameworkPropertyMetadata, PropertyChangedCallback, 
                       ' RoutedPropertyChangedEventArgs
imports System.Windows.Controls 'UserControl

Namespace SDKSample

    ' Interaction logic for NumericUpDown.xaml
    Partial Public Class NumericUpDown
        Inherits System.Windows.Controls.UserControl

        'NumericUpDown user control implementation

    End Class

End Namespace

En el ejemplo siguiente se muestra el código XAML necesario para incorporar el control de usuario a un Window:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.UserControlWindow"
    xmlns:local="clr-namespace:SDKSample" 
    Title="User Control Window">

  <!-- Numeric Up/Down user control -->
  <local:NumericUpDown />

</Window>

En la ilustración siguiente se muestra el control NumericUpDown hospedado en un Window:

UserControl personalizado

Para obtener más información sobre los controles personalizados, vea Información general sobre la creación de controles.

Procedimientos recomendados en WPF

Como sucede con cualquier plataforma de desarrollo, WPF se puede usar de diversas maneras para lograr el resultado deseado. Para asegurarse de que las aplicaciones de WPF proporcionen la experiencia del usuario necesaria y satisfagan las exigencias del público en general, existen procedimientos recomendados de accesibilidad, globalización y localización, y rendimiento. Para más información, consulte:

Pasos siguientes

Hemos analizado las principales características de WPF. Ahora es el momento de que compile su primera aplicación de WPF.

Vea también