Sie sind auf Seite 1von 473

Manual de Windows Presentation Foundation

INDICE GENERAL
4. Controles
4.1. Informacin General sobre Controles
4.2. Tutorial: Crear un Botn Animado mediante el uso de XAML
4.3. Personalizacin de Controles
4.3.1. Informacin General sobre la Creacin de Controles
4.3.2. Instrucciones para el Diseo de Controles con Estilos
4.3.3. Adornos
4.3.3.1. Informacin General sobre Adornos
4.3.3.2. Temas Cmo de Adornos
4.3.3.2.1. Cmo: Implementar un Adorno
4.3.3.2.2 Cmo: Enlazar un Adorno a un Elemento
4.3.3.2.3. Cmo: Incluir Adornos en los Elementos Secundarios de un
Panel
4.3.3.2.4. Cmo: Quitar un Adorno de un Elemento
4.3.3.2.5. Cmo: Quitar Todos los Adornos de un Elemento
4.3.4. Ejemplos de ControlTemplate
4.3.4.1. Ejemplo de ControlTemplate de Button
4.3.4.2. Ejemplo de ControlTemplate de CheckBox
4.3.4.3. Ejemplo de ControlTemplate de ComboBox
4.3.4.4. Ejemplo de ControlTemplate de ComboBoxItem
4.3.4.5. Ejemplo de ControlTemplate de ContextMenu
4.3.4.6. Ejemplo de ControlTemplate para DocumentViewer
4.3.4.8. Ejemplo de ControlTemplate de Frame
4.3.4.9. Ejemplo de ControlTemplate de GroupBox
4.3.4.10. Ejemplo de ControlTemplate de Label
4.3.4.11. Ejemplo de ControlTemplate de ListBox
4.3.4.12. Ejemplo de ControlTemplate de ListBoxItem
4.3.4.13. Ejemplo de ControlTemplate de ListView
4.3.4.14. Ejemplo de ControlTemplate de ListViewItem
4.3.4.15. Ejemplo de ControlTemplate de Menu
4.3.4.16. Ejemplo de ControlTemplate para MenuItem
4.3.4.17. Ejemplo de ControlTemplate de NavigationWindow
4.3.4.18. Ejemplo de ControlTemplate para ProgressBar
4.3.4.19. Ejemplo de ControlTemplate de RadioButton
4.3.4.20. Ejemplo de ControlTemplate de ScrollBar
4.3.4.21. Ejemplo de ControlTemplate de ScrollViewer
4.3.4.22. Ejemplo de ControlTemplate de Slider
4.3.4.23. Ejemplo de ControlTemplate de StatusBar
4.3.4.24. Ejemplo de ControlTemplate de TabControl
4.3.4.25. Ejemplo de ControlTemplate de TabItem
4.3.4.26. Ejemplo de ControlTemplate de TextBox
4.3.4.27. Ejemplo de ControlTemplate de ToolBar
4.3.4.28. Ejemplo de ControlTemplate de ToolTip
4.3.4.29. Ejemplo de ControlTemplate de TreeView
4.3.4.30. Ejemplo de ControlTemplate para TreeViewItem
4.3.4.31. Ejemplo de ControlTemplate de Window
4.3.5. Automatizacin de la Interfaz de Usuario de un Control Personalizado de WPF
4.4. Agrupar Controles por Categora
4.5. Modelos de Contenido
4.5.1. Modelo de Contenido de WPF
4.5.2. Informacin General sobre el Modelo de Contenido de Controles
4.5.3. Informacin General sobre el Modelo de Contenido de Decorador
4.5.4. Informacin General sobre el Modelo de Contenido de Paneles
4.5.5. Informacin General sobre el Modelo de Contenido de TextBlock
4.5.6. Informacin General sobre el Modelo de Contenido de TextBox
5. Datos
5.1. Enlace de Datos
5.1.1. Informacin General sobre el Enlace de Datos

MCT: Luis Dueas

Pag 1 de 473

Manual de Windows Presentation Foundation


5.1.2. Informacin General sobre Orgenes de Enlaces
5.1.3. Informacin General sobre Plantillas de Datos
5.1.4. Informacin General sobre Declaraciones de Enlaces
5.1.5. Temas Cmo sobre Enlace de Datos
5.1.5.1. Cmo: Crear un Enlace Sencillo
5.1.5.2. Cmo: Especificar el Origen de Enlace
5.1.5.3. Cmo: Hacer que los Datos estn Disponibles para el Enlace XAML
5.1.5.4. Cmo: Controlar Cundo el Texto de TextBox Actualiza el Origen
5.1.5.5. Cmo: Especificar la Direccin del Enlace
5.1.5.6. Cmo: Enlazar a una Coleccin y Mostrar Informacin Basada en la
Seleccin
5.1.5.7. Cmo: Enlazar a una Enumeracin
5.1.5.8. Cmo: Enlazar las Propiedades de dos Controles
5.1.5.9. Cmo: Implementar la Validacin de Enlaces
5.1.5.10. Cmo: Implementar Lgica de Validacin en Objetos Personalizados
5.1.5.11. Cmo: Obtener el Objeto de Enlace a Partir de una Propiedad de
Destino Enlazada
5.1.5.12. Cmo: Implementar una CompositeCollection
5.1.5.13. Cmo: Convertir Datos Enlazados
5.1.5.14. Cmo: Crear un Enlace en Cdigo
5.1.5.15. Cmo: Obtener la Vista Predeterminada de Recoleccin de Datos
5.1.5.16. Cmo: Navegar por los Objetos de una Coleccin de Datos
mediante CollectionView
5.1.5.17. Cmo: Filtrar Datos en una Vista
5.1.5.18. Cmo: Ordenar Datos en una Vista
5.1.5.19. Cmo: Ordenar y Agrupar Datos mediante una Vista en XAML
5.1.5.20. Cmo: Usar el Patrn Principal Detalle con datos Jerrquicos
5.1.5.21. Cmo: Usar el Patrn Principal Detalle con Datos XML Jerrquicos
5.1.5.22. Cmo: Generar un Valor Basado en una Lista de Elementos
Enlazados
5.1.5.23. Cmo: Implementar la Notificacin de Cambio de Propiedad
5.1.5.24. Cmo: Crear y Enlazar a una Coleccin ObservableCollection
5.1.5.25. Cmo: Implementar PriorityBinding
5.1.5.26. Cmo: Enlazar a Datos XML mediante XMLDataProvider y
Consultas XPath
5.1.5.27. Cmo: Enlazar a los Resultados de una Consulta LINQ para XML,
XDocument o XElement
5.1.5.28. Cmo: Usar Espacios de Nombres XML en el Enlace de Datos
5.1.5.29. Cmo: Enlazar a un Origen de Datos ADO .NET
5.1.5.30. Cmo: Enlazar a un Mtodo
5.1.5.31. Cmo: Configurar la Notificacin de Actualizaciones de Enlaces
5.1.5.32. Cmo: Borrar Enlaces
5.1.5.33. Cmo: Buscar Elementos Generados por un Objeto DataTemplate
5.1.5.34. Cmo: Enlazar a un Servicio Web
5.1.5.35. Cmo: Enlazar a los Resultados de una Consulta LINQ
5.2. Arrastrar y Colocar
5.2.1. Informacin General sobre la Funcin de Arrastrar y Colocar
5.2.2. Temas Cmo de Arrastrar y Colocar
5.2.2.1. Cmo: Usar un Control Thumb para Habilitar la Accin de Arrastrar
5.2.2.2. Cmo: Crear un Objeto de Datos
5.2.2.3. Cmo: Determinar si un Formato de Datos est Presente en un
Objeto de Datos
5.2.2.4. Cmo: Mostrar los Formatos de Datos en un Objeto de Datos
5.2.2.5. Cmo: Recuperar Datos en un Formato Concreto
5.2.2.6. Cmo: Almacenar Varios Formatos de Datos en un Objeto de Datos
6. Documentos
6.1. Documentos en Windows Presentation Foundation
6.2. Almacenamiento y Serializacin de Documentos
6.3. Anotaciones
6.3.1. Informacin General sobre Anotaciones

MCT: Luis Dueas

Pag 2 de 473

Manual de Windows Presentation Foundation


6.3.2. Esquema en Anotaciones
6.4. Contenido Dinmico
6.4.1. Informacin General sobre Documentos Dinmicos
6.4.2. Informacin General sobre el Modelo de Contenido de TextElement
6.4.3. Informacin General sobre Tablas
6.4.4. Temas "Cmo..." de Elementos de Contenido Dinmico
6.4.4.1. Cmo: Ajustar el Espaciado entre Prrafos
6.4.4.2. Cmo: Generar una Tabla mediante Programacin
6.4.4.3. Cmo: Cambiar la Propiedad FlowDirection de Contenido mediante
Programacin
6.4.4.4. Cmo: Cambiar la Propiedad TextWrapping mediante Programacin
6.4.4.5. Cmo: Definir una Tabla con XAML
6.4.4.6. Cmo: Modificar la Tipografa de Texto
6.4.4.7. Cmo: Habilitar el Recorte de Texto
6.4.4.8. Cmo: Insertar un Elemento en Texto mediante Programacin
6.4.4.9. Cmo: Manipular Elementos de Contenido Dinmico mediante la
Propiedad Blocks
6.4.4.10. Cmo: Manipular Elementos de Contenido Dinmico mediante la
Propiedad Inlines
6.4.4.11. Cmo: Manipular un Objeto FlowDocument mediante la Propiedad
Blocks
6.4.4.12. Cmo: Manipular las Columnas de una Tabla mediante la Propiedad
Columns
6.4.4.13. Cmo: Manipular Grupos de Filas de una Tabla mediante la
Propiedad RowGroups
6.4.4.14. Cmo: Usar Elementos de Contenido Dinmico
6.4.4.15. Cmo: Usar Atributos de Separacin de Columnas FlowDocument
6.5. Tipografa
6.5.1. Tipografa en Windows Presentation Foundation
6.5.2. Informacin General sobre ClearType
6.5.3. Configuracin del Registro de ClearType
6.5.4. Dibujar Texto con Formato
6.5.5. Formato de Texto Avanzado
6.5.6. Fuentes en WPF
6.5.6.1. Caractersticas de las Fuentes OpenType
6.5.6.2. Empaquetar Fuentes con Aplicaciones
6.5.6.3. Paquete de Fuentes OpenType de Ejemplo
6.5.6.4. Temas Cmo sobre Fuentes
6.5.6.4.1. Cmo: Enumerar Fuentes del Sistema
6.5.6.4.2. Cmo: Utilizar la Clase FontSizeConverter
6.5.7. Glifos
6.5.7.1. Introduccin al Objeto GlyphRun y al Elemento Glyphs
6.5.7.2. Dibujar Texto mediante Grifos
6.5.8. Temas Cmo sobre Tipografa
6.5.8.1. Cmo: Crear una Decoracin de Texto
6.5.8.2. Cmo: Usar una Decoracin de Texto con un Hipervnculo
6.5.8.3. Cmo: Aplicar Transformaciones a Texto
6.5.8.4. Cmo: Aplicar Animaciones a Texto
6.5.8.5. Cmo: Crear un Efecto de Texto
6.5.8.6. Cmo: Crear Texto con Sombreado
6.5.8.7. Cmo: Crear Texto con Contorno
6.5.8.8. Cmo: Crear una Animacin de PathGeometry para Texto
6.5.8.9. Cmo: Dibujar Texto en el Fondo de un Control
6.5.8.10. Cmo: Dibujar Texto en un Elemento Visual
6.5.8.11. Cmo: Establecer Propiedades Tipogrficas
6.5.8.12. Cmo: Usar Caracteres Especiales en XAML
6.6. Imprimir y Administracin de Sistemas de Impresin
6.6.1. Informacin General sobre Impresin
6.6.2. Temas Cmo de Impresin
6.6.2.1. Cmo: Invocar un Cuadro de Dilogo de Impresin

MCT: Luis Dueas

Pag 3 de 473

Manual de Windows Presentation Foundation


6.6.2.2. Cmo: Clonar una Impresora
6.6.2.3. Cmo: Diagnosticar Trabajos de Impresin Problemticos
6.6.2.4. Cmo: Detectar si un Trabajo de Impresin se puede Imprimir en esta
Hora del Da
6.6.2.5. Cmo: Enumerar un Subconjunto de Colas de Impresin
6.6.2.6. Cmo: Ampliar el Esquema de Impresin y Crear Nuevas Clases del
Sistema de Impresin
6.6.2.7. Cmo: Obtener Propiedades de un Objeto de Sistema de Impresin
sin Reflexin
6.6.2.8. Cmo: Imprimir mediante Programacin Archivos XPS
6.6.2.9. Cmo: Supervisar de forma Remota el Estado de las Impresoras
6.6.2.10. Cmo: Validar y Combinar Elementos PrintTicket
7. Grficos y Multimedia
7.1. Informacin General sobre Caractersticas de Grficos, Animacin y Multimedia en WPF
7.2. Informacin General sobre la Representacin de Grficos en WP
7.3. Grficos
7.3.1. Efectos de Mapa de Bits
7.3.1.1. Informacin General sobre Efectos de Mapa de Bits
7.3.1.2. Temas Cmo de Efectos de Mapa de Bits
7.3.1.2.1. Cmo: Crear un Efecto de Resplandor en el Margen
Externo de un Objeto
7.3.1.2.2. Cmo: Animar un Efecto de Resplandor
7.3.1.2.3. Cmo: Animar Efectos de Mapa de Bits
7.3.1.2.4. Cmo: Aplicar un Efecto de Desenfoque a un Objeto Visual
7.3.1.2.5. Cmo: Animar un Efecto Visual de Desenfoque
7.3.1.2.6. Cmo: Crear un Efecto Visual de Sombra Paralela
7.3.1.2.7. Cmo: Animar un Efecto Visual de Sombra Paralela
7.3.1.2.8. Cmo: Crear un Efecto Visual con Bisel
7.3.1.2.9. Cmo: Animar un Efecto Visual con Bisel
7.3.1.2.10. Cmo: Crear un Efecto Visual con Relieves
7.3.1.2.11. Cmo: Animar un Efecto Visual con Relieves
7.3.1.2.12. Cmo: Crear Varios Efectos Visuales
7.3.1.2.13. Cmo: Animar Varios Efectos Visuales
7.3.1.2.14. Cmo: Usar un Efecto Visual Personalizado
7.3.1.2.15. Cmo: Aplicar un Efecto a Parte de una Imagen
7.3.1.2.16. Cmo: Animar un Efecto dentro de un BitmapEffectGroup
7.3.2. Pinceles
7.3.2.1. Informacin General sobre Pinceles de WPF
7.3.2.2. Informacin General sobre la Transformacin de Pinceles
7.3.2.3. Informacin General sobre las Mscaras de Opacidad
7.3.2.4. Informacin General sobre el Dibujo con Colores Slidos y
Degradados
7.3.2.5. Pintar con Imgenes, Dibujos y Elementos Visuales
7.3.2.6. Informacin General sobre Objetos TileBrush
7.3.2.7. Temas Cmo de Pinceles
7.3.2.7.1. Cmo: Animar el Color o la Opacidad de un Objeto
SolidColorBrush
7.3.2.7.2. Cmo: Animar la Posicin o Color de un Punto de
Degradado
7.3.2.7.3. Cmo: Crear una Reflexin
7.3.2.7.4. Cmo: Crear Patrones de Mosaico diferentes con un Objeto
TileBrush
7.3.2.7.5. Cmo: Definir un Lpiz
7.3.2.7.6. Cmo: Pintar un Area con un Dibujo
7.3.2.7.7. Cmo: Pintar un Area con una Imagen
7.3.2.7.8. Cmo: Pintar un Area con un Degradado Lineal
7.3.2.7.9. Cmo: Pintar un Area con un Degradado Radial
7.3.2.7.10. Cmo: Pintar un Area con un Color Slido
7.3.2.7.11. Cmo: Pintar un Area con un Pincel del Sistema
7.3.2.7.12. Cmo: Pintar un Area con un Vdeo

MCT: Luis Dueas

Pag 4 de 473

Manual de Windows Presentation Foundation


7.3.2.7.13. Cmo: Pintar un Area con un Objeto Visual
7.3.2.7.14. Cmo: Conservar la Relacin de Aspecto de una Imagen
utilizada como Fondo
7.3.2.7.15. Cmo: Establecer la Alineacin Horizontal y Vertical de
TileBrush
7.3.2.7.16. Cmo: Establecer el Tamao del Mosaico de un TileBrush
7.3.2.7.17. Cmo: Transformar un Pincel
7.3.2.7.18. Cmo: Usar Colores del Sistema en un Degradado
7.3.3. Dibujos
7.3.3.1. Informacin General sobre Objetos Drawing
7.3.3.2. Temas Cmo de Dibujo
7.3.3.2.1. Cmo: Aplicar un Objeto BitmapEffect a un Dibujo
7.3.3.2.2. Cmo: Aplicar un Objeto GuidelineSet a un Dibujo
7.3.3.2.3. Cmo: Aplicar una Mscara de Opacidad a un Dibujo
7.3.3.2.4. Cmo: Aplicar una Transformacin a un Dibujo
7.3.3.2.5. Cmo: Recortar un Dibujo
7.3.3.2.6. Cmo: Controlar la Opacidad de un Dibujo
7.3.3.2.7. Cmo: Crear un Dibujo Compuesto
7.3.3.2.8. Cmo: Crear un Objeto GeometryDrawing
7.3.3.2.9. Cmo: Dibujar una Imagen usando Objeto ImageDrawing
7.3.3.2.10. Cmo: Reproducir Elementos Multimedia con un Objeto
VideoDrawing
7.3.3.2.11. Cmo: Usar un Dibujo como el Origen de una Imagen
7.3.4. Geometras
7.3.4.1. Sintaxis de Marcado de Trazados
7.3.4.2. Informacin General sobre Geometra
7.3.4.3. Temas Cmo de Objetos de Geometra
7.3.4.3.1. Cmo: Animar una Regin de Recorte
7.3.4.3.2. Cmo: Animar un EllipseGeometry
7.3.4.3.3. Cmo: Animar el Tamao de un Objeto ArcSegment
7.3.4.3.4. Cmo: Controlar el Relleno de una Forma Compuesta
7.3.4.3.5. Cmo: Crear una Regin de Recorte
7.3.4.3.6. Cmo: Crear una Geometra Combinada
7.3.4.3.7. Cmo: Crear una Forma Compuesta
7.3.4.3.8. Cmo: Crear una Curva Bzier Cbica
7.3.4.3.9. Cmo: Crear una Lnea mediante la Clase LineGeometry
7.3.4.3.10. Cmo: Crear un Segmento de Lnea en una Clase
PathGeometry
7.3.4.3.11. Cmo: Crear una Forma mediante una Clase
PathGeometry
7.3.4.3.12. Cmo: Crear una Forma utilizando StreamGeometry
7.3.4.3.13. Cmo: Crear una curva Bzier Cuadrtica
7.3.4.3.14. Cmo: Crear un Arco Elptico
7.3.4.3.15. Cmo: Crear Varios Subtrazados en un PathGeometry
7.3.4.3.16. Cmo: Definir un Rectngulo usando una Clase
RectangleGeometry
7.3.4.3.17. Cmo: Redondear las Esquinas de un RectangleGeometry

MCT: Luis Dueas

Pag 5 de 473

Manual de Windows Presentation Foundation

4. Controles
Windows Presentation Foundation (WPF) proporciona una completa biblioteca de controles que permiten el
desarrollo de interfaz de usuario (UI), la visualizacin de documentos y la serializacin de entrada manuscrita
digital.

4.1. Informacin General sobre Controles


Windows Presentation Foundation (WPF) se distribuye con muchos de los componentes de interfaz de usuario
comunes que se utilizan en casi todas las aplicaciones para Windows, como Button, Label, TextBox, Menu y
ListBox. Histricamente, estos objetos se denominan controles. Aunque en el WPF SDK se contina utilizando el
trmino "control" para denominar genricamente cualquier clase que representa un objeto visible de una
aplicacin, es importante tener en cuenta que una clase no necesita heredar de la clase Control para tener una
presencia visible. Las clases que heredan de la clase Control contienen una ControlTemplate, que permite al
consumidor de un control cambiar radicalmente el aspecto de ste sin tener que crear una nueva subclase. En
este tema se describen los usos ms comunes de los controles (tanto los que heredan de la clase Control como
los que no) en WPF.
Crear una instancia de un control
Puede agregar un control a una aplicacin utilizando Lenguaje de marcado de aplicaciones extensible (XAML) o
cdigo. En el ejemplo siguiente se muestra cmo crear una aplicacin simple que pide al usuario su nombre y
su apellido. En este ejemplo se crean seis controles: dos etiquetas, dos cuadros de texto y dos botones, en
XAML. Todos los controles se pueden crear de igual forma.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label>
Enter your first name:
</Label>
<TextBox Grid.Row="0" Grid.Column="1" Name="firstName" Margin="0,5,10,5"/>
<Label Grid.Row="1" >
Enter your last name:
</Label>
<TextBox Grid.Row="1" Grid.Column="1" Name="lastName" Margin="0,5,10,5"/>
<Button Grid.Row="2" Grid.Column="0" Name="submit" Margin="2">
View message
</Button>
<Button Grid.Row="2" Grid.Column="1" Name="Clear" Margin="2">
Clear Name
</Button>
</Grid>
En el ejemplo siguiente se crea la misma aplicacin mediante cdigo. Para mayor brevedad, la creacin de Grid,
grid1 se ha excluido del ejemplo. grid1 tiene las mismas definiciones de columnas y filas que en el ejemplo
XAML anterior.
Private firstNameLabel As Label
Private lastNameLabel As Label
Private firstName As TextBox
Private lastName As TextBox
Private submit As Button
Private clear As Button
Sub CreateControls()
firstNameLabel = New Label()
firstNameLabel.Content = "Enter your first name:"
grid1.Children.Add(firstNameLabel)
firstName = New TextBox()
firstName.Margin = New Thickness(0, 5, 10, 5)
Grid.SetColumn(firstName, 1)
grid1.Children.Add(firstName)
lastNameLabel = New Label()
lastNameLabel.Content = "Enter your last name:"
Grid.SetRow(lastNameLabel, 1)
grid1.Children.Add(lastNameLabel)
lastName = New TextBox()

MCT: Luis Dueas

Pag 6 de 473

Manual de Windows Presentation Foundation


lastName.Margin = New Thickness(0, 5, 10, 5)
Grid.SetColumn(lastName, 1)
Grid.SetRow(lastName, 1)
grid1.Children.Add(lastName)
submit = New Button()
submit.Content = "View message"
Grid.SetRow(submit, 2)
grid1.Children.Add(submit)
clear = New Button()
clear.Content = "Clear Name"
Grid.SetRow(clear, 2)
Grid.SetColumn(clear, 1)
grid1.Children.Add(clear)
End Sub 'CreateControls
Cambiar el aspecto de un control
Es comn cambiar el aspecto de un control para ajustarlo a la apariencia y funcionamiento de la aplicacin.
Puede cambiar el aspecto de un control realizando una de las acciones siguientes, dependiendo de lo que desea
lograr:

Cambiar el valor de una propiedad del control.


Crear un Style para el control.
Crear una nueva ControlTemplate para el control.

Cambiar el valor de una propiedad de un control


Muchos controles tienen propiedades que permiten cambiar el aspecto del control, como el fondo (Background)
de un botn (Button). Puede establecer las propiedades de valor en XAML y mediante cdigo. En el ejemplo
siguiente se establecen las propiedades Background, FontSize y FontWeight de un Button en XAML.
<Button FontSize="14" FontWeight="Bold">
<!--Set the Background property of the Button to
a LinearGradientBrush.-->
<Button.Background>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
<GradientStop Color="Green" Offset="0.0" />
<GradientStop Color="White" Offset="0.9" />
</LinearGradientBrush>
</Button.Background>
View message
</Button>
En el ejemplo siguiente se establecen las mismas propiedades mediante cdigo.
Dim buttonBrush As New LinearGradientBrush()
buttonBrush.StartPoint = New Point(0, 0.5)
buttonBrush.EndPoint = New Point(1, 0.5)
buttonBrush.GradientStops.Add(New GradientStop(Colors.Green, 0))
buttonBrush.GradientStops.Add(New GradientStop(Colors.White, 0.9))
submit.Background = buttonBrush
submit.FontSize = 14
submit.FontWeight = FontWeights.Bold
Crear un estilo para un control
WPF permite especificar el aspecto de los controles de manera global, en lugar de establecer las propiedades en
cada instancia en la aplicacin, creando un Style. En el ejemplo siguiente se crea un Style que se aplica a
Button en la aplicacin. Las definiciones de Style se suelen definir en XAML en un ResourceDictionary, como la
propiedad Resources de FrameworkElement.
<Style TargetType="Button">
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
<GradientStop Color="Green" Offset="0.0" />
<GradientStop Color="White" Offset="0.9" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
Tambin puede aplicar un estilo nicamente a determinados controles de un tipo especfico asignando una clave
al estilo y especificando esa la clave en la propiedad Style del control.
Crear una plantilla de control
Style permite establecer las propiedades en varios controles a la vez, pero en ocasiones puede ser conveniente
personalizar el aspecto de un Control ms all de lo que se puede hacer creando un Style. Las clases que
heredan de la clase Control tienen una ControlTemplate, que define la estructura y el aspecto de un Control. La

MCT: Luis Dueas

Pag 7 de 473

Manual de Windows Presentation Foundation


propiedad Template de Control es pblica, por lo que puede dar una ControlTemplate a un Control diferente de
la predeterminada. Con frecuencia, se puede especificar una ControlTemplate nueva para un Control en lugar
de heredar de un control para personalizar el aspecto de Control.
Tomemos uno de los controles comunes, Button. El comportamiento primario de Button es permitir que una
aplicacin realice una accin cuando el usuario hace clic en l. De manera predeterminada, Button aparece en
WPF

como un rectngulo elevado. Al programar

una aplicacin, puede que quiera aprovechar el

comportamiento de Button, es decir, controlar el evento de hacer clic en el botn, pero que desee cambiar el
aspecto del botn ms all de lo que es posible cambiando las propiedades del mismo. En este caso, puede
crear una nueva ControlTemplate.
En el ejemplo siguiente se crea un objeto ControlTemplate para un control Button. ControlTemplate crea Button
con esquinas redondeadas y fondo degradado. ControlTemplate contiene un Border cuyo Background es
LinearGradientBrush con dos objetos GradientStop. El primer GradientStop utiliza el enlace de datos para
enlazar la propiedad Color de GradientStop al color de fondo del botn. Al establecer la propiedad Background
de Button, el color de ese valor se utilizar como primer GradientStop. En el ejemplo tambin se crea un
Trigger que cambia el aspecto de Button cuando IsPressed es true.
<!--Define a template that creates a gradient-colored button.-->
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Border" CornerRadius="20" BorderThickness="1" BorderBrush="Black">
<Border.Background>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
<GradientStop Color="{Binding Background.Color,
RelativeSource={RelativeSource TemplatedParent}}" Offset="0.0" />
<GradientStop Color="White" Offset="0.9" />
</LinearGradientBrush>
</Border.Background>
<ContentPresenter Margin="2" HorizontalAlignment="Center"
VerticalAlignment="Center" RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<!--Change the appearance of the button when the user clicks it.-->
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
<GradientStop Color="{Binding Background.Color,
RelativeSource={RelativeSource TemplatedParent}}" Offset="0.0" />
<GradientStop Color="DarkSlateGray" Offset="0.9" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
...
<Button Grid.Row="2" Grid.ColumnSpan="2" Name="submitName" Background="Green">
View message</Button>
Nota:
La propiedad Background de Button debe estar establecida en SolidColorBrush para que ejemplo funcione
correctamente.
Suscribirse a eventos
Puede suscribirse a un evento de un control utilizando XAML o cdigo, pero los eventos nicamente se pueden
controlar mediante cdigo. En el ejemplo siguiente se muestra cmo suscribirse al evento Click de un Button.
<Button Grid.Row="2" Grid.ColumnSpan="2" Name="submitName" Click="submit_Click"
Background="Green">View message</Button>
AddHandler submit.Click, AddressOf submit_Click
En el ejemplo siguiente se controla el evento Click de un control Button.
Private Sub submit_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
MessageBox.Show("Hello, " + firstName.Text + " " + lastName.Text)
End Sub 'submit_Click
Contenido enriquecido en los controles

MCT: Luis Dueas

Pag 8 de 473

Manual de Windows Presentation Foundation


La mayora de las clases que heredan de la clase Control tienen la capacidad de incluir contenido enriquecido.
Por ejemplo, un control Label puede contener cualquier objeto, como una cadena, una Image o un Panel. Las
clases siguientes proporcionan compatibilidad con el contenido enriquecido y actan como clases base para la
mayora de los controles en WPF.

ContentControl: algunos ejemplos de clases que heredan de esta clase son Label, Button y ToolTip.
ItemsControl: algunos ejemplos de clases que heredan de esta clase son ListBox, Menu y StatusBar.
HeaderedContentControl: algunos ejemplos de clases que heredan de esta clase son TabItem,
GroupBox y Expander.

HeaderedItemsControl: algunos ejemplos de clases que heredan de esta clase son MenuItem,
TreeViewItem y ToolBar.

4.2. Tutorial: Crear un Botn Animado mediante el uso de XAML


El objetivo de este tutorial es aprender a crear un botn animado para usarlo en una aplicacin Windows
Presentation Foundation (WPF). Este tutorial utiliza estilos y una plantilla para crear un recurso de botn
personalizado que permite la reutilizacin de cdigo y la separacin de la lgica del botn de la declaracin del
botn. Este tutorial est escrito completamente en Lenguaje de marcado de aplicaciones extensible (XAML).
Nota importante:
Este tutorial sirve como gua en los pasos necesarios para crear la aplicacin escribiendo, o copiando y
pegando Lenguaje de marcado de aplicaciones extensible (XAML) en Microsoft Visual Studio. Si prefiere
aprender a usar una herramienta de diseo (Microsoft Expression Blend) para crear la misma aplicacin,
La figura siguiente muestra los botones acabados.

Crear botones bsicos


Empecemos por crear un nuevo proyecto y agregar unos botones a la ventana.

Para crear un nuevo proyecto de WPF y agregar botones a la ventana


1.

Inicie Visual Studio.

2.

Cree un nuevo proyecto de WPF: En el men Archivo, seale a Nuevo y, a continuacin, haga clic en
Proyecto. Busque la plantilla Aplicacin para Windows (WPF) y asigne al proyecto el nombre
"AnimatedButton". Esto crear el esqueleto para la aplicacin.

3.

Agregue los botones predeterminados bsicos: la plantilla proporciona todos los archivos que necesita
para este tutorial. Abra el archivo Window1.xaml haciendo doble clic en l en el Explorador de soluciones.
De forma predeterminada, hay un elemento Grid en Window1.xaml. Quite el elemento Grid y agregue
unos botones a la pgina Lenguaje de marcado de aplicaciones extensible (XAML) escribiendo o copiando
y pegando el siguiente cdigo resaltado en Window1.xaml:
<Window x:Class="AnimatedButton.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="AnimatedButton" Height="300" Width="300"
Background="Black">
<!-- Buttons arranged vertically inside a StackPanel. -->
<StackPanel HorizontalAlignment="Left">

MCT: Luis Dueas

Pag 9 de 473

Manual de Windows Presentation Foundation

4.

<Button>Button 1</Button>
<Button>Button 2</Button>
<Button>Button 3</Button>
</StackPanel>
</Window>
Presione F5 para ejecutar la aplicacin; debera ver un conjunto de botones parecido a la ilustracin
siguiente.

5.

Ahora que ha creado los botones bsicos, ha terminado de trabajar en el archivo Window1.xaml. El
resto del tutorial se centra en el archivo app.xaml, definiendo estilos y una plantilla para los botones.
Establecer propiedades bsicas

A continuacin, establezcamos algunas propiedades de estos botones para controlar su aspecto y su diseo. En
lugar de establecer individualmente las propiedades de los botones, utilizar recursos para definir las
propiedades de botn de toda la aplicacin. Los recursos de aplicacin son conceptualmente similares a Hojas
de estilos en cascada (CSS) externas para pginas web; sin embargo, los recursos son mucho ms eficaces que
Hojas de estilos en cascada (CSS), como ver al final de este tutorial.

Para utilizar estilos para establecer propiedades bsicas en los botones


1.

2.

Defina un bloque Application.Resources: abra app.xaml y agregue el marcado resaltado siguiente si


an no est all:
<Application x:Class="AnimatedButton.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml">
<Application.Resources>
<!-- Resources for the entire application can be defined here. -->
</Application.Resources>
</Application>
El mbito del recurso lo determina dnde se defina el recurso. Definir

recursos

en

Application.Resoureses en el archivo app.xaml permite utilizar el recurso en cualquier parte de la


aplicacin.
3.

Cree un estilo y defina los valores de las propiedades bsicas con l: agregue el marcado siguiente al
bloque Application.Resources. Este marcado crea un objeto Style que se aplica a todos los botones de la

4.

aplicacin, estableciendo la propiedad Width de los botones en 90 y Margin en 10:


<Application.Resources>
<Style TargetType="Button">
<Setter Property="Width" Value="90" />
<Setter Property="Margin" Value="10" />
</Style>
</Application.Resources>
La propiedad TargetType especifica que el estilo se aplica a todos los objetos de tipo Button. Cada
Setter establece un valor de la propiedad diferente para el objeto Style. Por consiguiente, en este punto
cada botn de la aplicacin tiene un ancho de 90 y un margen de 10. Si presiona F5 para ejecutar la
aplicacin, ver la ventana siguiente.

MCT: Luis Dueas

Pag 10 de 473

Manual de Windows Presentation Foundation

5.

Hay mucho ms que puede hacer con estilos, incluidas diversas maneras de ajustar con precisin a
qu objetos se destinan, especificar valores de propiedad complejos e incluso utilizar estilos como entrada
para otros estilos.

6.

Establezca un valor de propiedad de estilo en un recurso: los recursos permiten reutilizar fcilmente
objetos y valores que se definen con frecuencia. Es especialmente til definir valores complejos utilizando

7.

recursos, para hacer el cdigo ms modular. Agregue el marcado resaltado siguiente a app.xaml.
<Application.Resources>
<LinearGradientBrush x:Key="GrayBlueGradientBrush" StartPoint="0,0"
EndPoint="1,1">
<GradientStop Color="DarkGray" Offset="0" />
<GradientStop Color="#CCCCFF" Offset="0.5" />
<GradientStop Color="DarkGray" Offset="1" />
</LinearGradientBrush>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="{StaticResource GrayBlueGradientBrush}"/>
<Setter Property="Width" Value="80" />
<Setter Property="Margin" Value="10" />
</Style>
</Application.Resources>
Directamente bajo el bloque Application.Resources, cre un recurso llamado "GrayBlueGradientBrush".
Este recurso define un degradado horizontal. Este recurso se puede utilizar en cualquier parte de la
aplicacin como un valor de propiedad, incluso dentro del establecedor de estilo del botn para la
propiedad Background. Ahora, todos los botones tienen un valor de propiedad Background de este
degradado.

8.

Presione F5 para ejecutar la aplicacin. Debe tener este aspecto:

Crear una plantilla que define la apariencia del botn


En esta seccin crear una plantilla que personaliza la apariencia (presentacin) del botn. La presentacin del
botn se compone de varios objetos, que incluyen rectngulos y otros componentes para dar una apariencia
nica al botn.
Hasta ahora, el control de la apariencia de los botones de la aplicacin se ha confinado a cambiar propiedades
del botn. Qu ocurre si desea realizar cambios ms radicales en la apariencia del botn? Las plantillas
permiten un control eficaz sobre la presentacin de un objeto. Dado que las plantillas se pueden utilizar dentro
de estilos, puede aplicar una plantilla a todos los objetos a los que se aplica el estilo (en este tutorial, el botn).

MCT: Luis Dueas

Pag 11 de 473

Manual de Windows Presentation Foundation


Para utilizar la plantilla para definir la apariencia del botn
1.

Configure la plantilla: dado que los controles como Button tienen una propiedad Template, puede
definir el valor de la propiedad de la plantilla igual que los dems valores de propiedades que hemos
establecido en un objeto Style, utilizando un objeto Setter. Agregue el marcado resaltado siguiente al

2.

estilo de botn.
<Application.Resources>
<LinearGradientBrush x:Key="GrayBlueGradientBrush"
StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="DarkGray" Offset="0" />
<GradientStop Color="#CCCCFF" Offset="0.5" />
<GradientStop Color="DarkGray" Offset="1" />
</LinearGradientBrush>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="{StaticResource GrayBlueGradientBrush}" />
<Setter Property="Width" Value="80" />
<Setter Property="Margin" Value="10" />
<Setter Property="Template">
<Setter.Value>
<!-- The button template is defined here. -->
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
Modifique la presentacin del botn: en este punto, debe definir la plantilla. Agregue el siguiente
marcado resaltado: Este marcado especifica dos elementos Rectangle con bordes redondeados, seguidos
por un control DockPanel. El control DockPanel se utiliza para hospedar el objeto ContentPresenter del
botn. Un objeto ContentPresenter muestra el contenido del botn. En este tutorial, el contenido es texto
("Button 1", "Button 2", "Button 3"). Todos los componentes de la plantilla (los rectngulos y el control

3.

4.

DockPanel) se disponen dentro de un control Grid.


<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"
ClipToBounds="True">
<!-- Outer Rectangle with rounded corners. -->
<Rectangle x:Name="outerRectangle" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Stroke="{TemplateBinding Background}" RadiusX="20"
RadiusY="20" StrokeThickness="5" Fill="Transparent" />
<!-- Inner Rectangle with rounded corners. -->
<Rectangle x:Name="innerRectangle" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Stroke="Transparent" StrokeThickness="20"
Fill="{TemplateBinding Background}" RadiusX="20" RadiusY="20" />
<!-- Present Content (text) of the button. -->
<DockPanel Name="myContentPresenterDockPanel">
<ContentPresenter x:Name="myContentPresenter" Margin="20"
Content="{TemplateBinding Content}" TextBlock.Foreground="Black" />
</DockPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
Presione F5 para ejecutar la aplicacin. Debe tener este aspecto:

Agregue un efecto de vidrio a la plantilla: a continuacin agregar el vidrio. Primero cree algunos
recursos que creen un efecto de degradado de vidrio. Agregue estos recursos de degradado en cualquier
punto del bloque Application.Resources:
<Application.Resources>
<GradientStopCollection x:Key="MyGlassGradientStopsResource">
<GradientStop Color="WhiteSmoke" Offset="0.2" />
<GradientStop Color="Transparent" Offset="0.4" />
<GradientStop Color="WhiteSmoke" Offset="0.5" />
<GradientStop Color="Transparent" Offset="0.75" />

MCT: Luis Dueas

Pag 12 de 473

Manual de Windows Presentation Foundation

5.

<GradientStop Color="WhiteSmoke" Offset="0.9" />


<GradientStop Color="Transparent" Offset="1" />
</GradientStopCollection>
<LinearGradientBrush x:Key="MyGlassBrushResource" StartPoint="0,0" EndPoint="1,1"
Opacity="0.75" GradientStops="{StaticResource MyGlassGradientStopsResource}" />
<!-- Styles and other resources below here. -->
Estos recursos se utilizan como propiedad Fill para un rectngulo que insertamos en el control Grid de

6.

la plantilla de botn. Agregue el siguiente marcado resaltado a la plantilla.


<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"
ClipToBounds="True">
<!-- Outer Rectangle with rounded corners. -->
<Rectangle x:Name="outerRectangle" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Stroke="{TemplateBinding Background}"
RadiusX="20" RadiusY="20" StrokeThickness="5" Fill="Transparent" />
<!-- Inner Rectangle with rounded corners. -->
<Rectangle x:Name="innerRectangle" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Stroke="Transparent" StrokeThickness="20"
Fill="{TemplateBinding Background}" RadiusX="20" RadiusY="20" />
<!-- Glass Rectangle -->
<Rectangle x:Name="glassCube" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" StrokeThickness="2" RadiusX="10" RadiusY="10"
Opacity="0" Fill="{StaticResource MyGlassBrushResource}"
RenderTransformOrigin="0.5,0.5">
<Rectangle.Stroke>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.0" Color="LightBlue" />
<GradientStop Offset="1.0" Color="Gray" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Rectangle.Stroke>
<!-- These transforms have no effect as they are declared here. The reason the
transforms are included is to be targets for animation (see later). -->
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform />
<RotateTransform />
</TransformGroup>
</Rectangle.RenderTransform>
<!-- A BevelBitmapEffect is applied to give the button a "Beveled" look. -->
<Rectangle.BitmapEffect>
<BevelBitmapEffect />
</Rectangle.BitmapEffect>
</Rectangle>
<!-- Present Text of the button. -->
<DockPanel Name="myContentPresenterDockPanel">
<ContentPresenter x:Name="myContentPresenter" Margin="20"
Content="{TemplateBinding Content}" TextBlock.Foreground="Black" />
</DockPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
Observe que la propiedad Opacity del rectngulo con la propiedad x:Name de "glassCube" es 0, por lo
que al ejecutar el ejemplo no ver el rectngulo de vidrio superpuesto. Esto se debe a que agregaremos
despus a la plantilla los desencadenadores para cuando el usuario interacte con el botn. Sin embargo,
puede ver la apariencia que tiene ahora el botn cambiando el valor Opacity a 1 y ejecutando la
aplicacin. Vea la ilustracin siguiente. Antes de continuar con el paso siguiente, vuelva a establecer
Opacity en 0.

Crear la interactividad del botn

MCT: Luis Dueas

Pag 13 de 473

Manual de Windows Presentation Foundation


En esta seccin, crear desencadenadores de propiedad y desencadenadores de evento para cambiar valores de
propiedad y ejecutar animaciones en respuesta a acciones del usuario tales como mover el puntero del mouse
sobre el botn y hacer clic.
Una manera fcil de agregar interactividad (entrada de mouse, salida de mouse, clic, etc.) es definir los
desencadenadores dentro de la plantilla o del estilo. Para crear un objeto Trigger, defina una propiedad
"condicin" tal como: el valor de la propiedad IsMouseOver del botn es igual a true. A continuacin, defina
establecedores (acciones) que tengan lugar cuando la condicin de activacin sea verdadera.

Para crear la interactividad del botn


1.

2.

Agregue desencadenadores de plantilla: agregue el marcado resaltado a la plantilla.


<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}" ClipToBounds="True">
<!-- Outer Rectangle with rounded corners. -->
<Rectangle x:Name="outerRectangle" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Stroke="{TemplateBinding Background}"
RadiusX="20" RadiusY="20" StrokeThickness="5" Fill="Transparent" />
<!-- Inner Rectangle with rounded corners. -->
<Rectangle x:Name="innerRectangle" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Stroke="Transparent"
StrokeThickness="20"
Fill="{TemplateBinding Background}" RadiusX="20" RadiusY="20" />
<!-- Glass Rectangle -->
<Rectangle x:Name="glassCube" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
StrokeThickness="2" RadiusX="10" RadiusY="10" Opacity="0"
Fill="{StaticResource MyGlassBrushResource}"
RenderTransformOrigin="0.5,0.5">
<Rectangle.Stroke>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.0" Color="LightBlue" />
<GradientStop Offset="1.0" Color="Gray" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Rectangle.Stroke>
<!-- These transforms have no effect as they
are declared here.
The reason the transforms are included is to be targets
for animation (see later). -->
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform />
<RotateTransform />
</TransformGroup>
</Rectangle.RenderTransform>
<!-- A BevelBitmapEffect is applied to give the button a
"Beveled" look. -->
<Rectangle.BitmapEffect>
<BevelBitmapEffect />
</Rectangle.BitmapEffect>
</Rectangle>
<!-- Present Text of the button. -->
<DockPanel Name="myContentPresenterDockPanel">
<ContentPresenter x:Name="myContentPresenter" Margin="20"
Content="{TemplateBinding Content}" TextBlock.Foreground="Black" />
</DockPanel>
</Grid>
<ControlTemplate.Triggers>
<!-- Set action triggers for the buttons and define what the button does in
response to those triggers. -->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
Agregue desencadenadores de propiedad: agregue el marcado resaltado al bloque ControlTemplate.
Triggers:
<ControlTemplate.Triggers>
<!-- Set properties when mouse pointer is over the button. -->
<Trigger Property="IsMouseOver" Value="True">
<!-- Below are three property settings that occur when the condition is met (user
mouses over button). -->
<!-- Change the color of the outer rectangle when user mouses over it. -->
<Setter Property ="Rectangle.Stroke" TargetName="outerRectangle"
Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
<!-- Sets the glass opacity to 1, therefore, the glass "appears" when user mouses
over it. -->

MCT: Luis Dueas

Pag 14 de 473

Manual de Windows Presentation Foundation

3.

<Setter Property="Rectangle.Opacity" Value="1" TargetName="glassCube" />


<!-- Makes the text slightly blurry as though you were looking at it through
blurry glass. -->
<Setter Property="ContentPresenter.BitmapEffect" TargetName="myContentPresenter">
<Setter.Value>
<BlurBitmapEffect Radius="1" />
</Setter.Value>
</Setter>
</Trigger>
<ControlTemplate.Triggers/>
Presione F5 para ejecutar la aplicacin y ver el efecto al pasar el puntero del mouse sobre los botones.

4.

Agregue un desencadenador de foco: a continuacin, agregaremos algunos establecedores similares


para administrar el caso en el que el botn tenga el foco (por ejemplo, despus de que el usuario haga

5.

clic en l).
<ControlTemplate.Triggers>
<!-- Set properties when mouse pointer is over the button. -->
<Trigger Property="IsMouseOver" Value="True">
<!-- Below are three property settings that occur when the
condition is met (user mouses over button). -->
<!-- Change the color of the outer rectangle when user mouses over it. -->
<Setter Property ="Rectangle.Stroke" TargetName="outerRectangle"
Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
<!-- Sets the glass opacity to 1, therefore, the glass "appears" when user mouses
over it. -->
<Setter Property="Rectangle.Opacity" Value="1" TargetName="glassCube" />
<!-- Makes the text slightly blurry as though you were looking at it through
blurry glass. -->
<Setter Property="ContentPresenter.BitmapEffect" TargetName="myContentPresenter">
<Setter.Value>
<BlurBitmapEffect Radius="1" />
</Setter.Value>
</Setter>
</Trigger>
<!-- Set properties when button has focus. -->
<Trigger Property="IsFocused"
Value="true">
<Setter Property="Rectangle.Opacity" Value="1" TargetName="glassCube" />
<Setter Property="Rectangle.Stroke" TargetName="outerRectangle"
Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
<Setter Property="Rectangle.Opacity" Value="1" TargetName="glassCube" />
</Trigger>
</ControlTemplate.Triggers>
Presione F5 para ejecutar la aplicacin y haga clic en uno de los botones. Observa que el botn
permanece resaltado despus de hacer clic en l, porque todava tiene el foco. Si hace clic en otro botn,
el nuevo botn obtendr el foco mientras el ltimo lo pierde.

6.

Agregue

animaciones

para

MouseEnter

MouseLeave:

continuacin,

agregamos

algunas

animaciones a los desencadenadores. Agregue el marcado siguiente en cualquier punto del bloque

7.

ControlTemplate.Triggers.
<!-- Animations that start when mouse enters and leaves button. -->
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard Name="mouseEnterBeginStoryboard">
<Storyboard>
<!-- This animation makes the glass rectangle shrink in the X direction. -->
<DoubleAnimation Storyboard.TargetName="glassCube"
Storyboard.TargetProperty= "(Rectangle.RenderTransform).
(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"
By="-0.1" Duration="0:0:0.5" />
<!-- This animation makes the glass rectangle shrink in the Y direction. -->
<DoubleAnimation Storyboard.TargetName="glassCube"
Storyboard.TargetProperty="(Rectangle.RenderTransform).
(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
By="-0.1" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<EventTrigger.Actions>
<!-- Stopping the storyboard sets all animated properties back to default. -->
<StopStoryboard BeginStoryboardName="mouseEnterBeginStoryboard" />
</EventTrigger.Actions>
</EventTrigger>
El rectngulo de vidrio se reduce cuando el puntero del mouse pasa sobre el botn y vuelve al tamao
normal cuando el puntero sale.

MCT: Luis Dueas

Pag 15 de 473

Manual de Windows Presentation Foundation


8.

Hay dos animaciones que se desencadenan cuando el puntero pasa sobre el botn (cuando se provoca
el evento MouseEnter). Estas animaciones reducen el rectngulo de vidrio a lo largo de los ejes X e Y.
Observe las propiedades de los elementos DoubleAnimation, Duration y By. La propiedad Duration
especifica que la animacin se produce durante medio segundo, y By especifica que el vidrio se reduce un
10%.

9.

El segundo desencadenador de eventos (MouseLeave) detiene simplemente el primero. Al detener un


objeto Storyboard, todas las propiedades animadas vuelven a sus valores predeterminados. Por
consiguiente, cuando el usuario saca el puntero del botn, el botn vuelve a ser como era antes de que el
puntero del mouse pasara sobre el botn.

10.

Agregue una animacin para cuando se haga clic en el botn: el paso final es agregar un
desencadenador para cuando el usuario haga clic en el botn. Agregue el marcado siguiente en cualquier

punto del bloque ControlTemplate.Triggers.


<!-- Animation fires when button is clicked, causing glass to spin. -->
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="glassCube"
Storyboard.TargetProperty="(Rectangle.RenderTransform).
(TransformGroup.Children)[1].(RotateTransform.Angle)"
By="360" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
11.
Presione F5 para ejecutar la aplicacin y haga clic en uno de los botones. Al hacer clic en un botn, el
rectngulo de vidrio gira sobre s mismo.
Resumen
En este tutorial, ha realizado los siguientes ejercicios:

Ha destinado un objeto Style a un tipo de objeto (Button).


Ha controlado propiedades bsicas de los botones en toda la aplicacin utilizando el objeto Style.
Ha creado recursos tales como degradados para utilizarlos para valores de propiedades de los
establecedores de Style.

Ha personalizado la apariencia de botones de toda la aplicacin aplicando una plantilla a los botones.
Ha personalizado el comportamiento de los botones en respuesta a acciones del usuario (tales como
MouseEnter, MouseLeave y Click) incluyendo efectos de animacin.

4.3. Personalizacin de Controles


Esta categora abarca las distintas clases base, las interfaces y otros elementos y conceptos utilizados en la
creacin de un control de Windows Presentation Foundation (WPF) totalmente funcional.

4.3.1. Informacin General sobre la Creacin de Controles


La extensibilidad del modelo de control Windows Presentation Foundation (WPF) reduce en gran medida la
necesidad de crear un nuevo control. Sin embargo, en ciertos casos es posible que an necesite crear un
control personalizado. En este tema se explican las caractersticas que minimizan la necesidad de crear un
control personalizado y los diferentes modelos de creacin de controles de Windows Presentation Foundation
(WPF). En este tema tambin se muestra cmo crear un nuevo control.
Alternativas a la escritura de un nuevo control
Histricamente, si se deseaba obtener una experiencia personalizada de un control existente, las posibilidades
estaban limitadas a cambiar las propiedades estndar del control, tales como el color de fondo, el ancho del
borde y el tamao de fuente. Si se deseaba extender la apariencia o comportamiento de un control ms all de
estos parmetros predefinidos, necesitaba crear un nuevo control, normalmente heredado de uno existente, e
invalidar el mtodo responsable de dibujarlo. Aunque esto sigue siendo posible, WPF permite personalizar los

MCT: Luis Dueas

Pag 16 de 473

Manual de Windows Presentation Foundation


controles existentes gracias a su modelo

de

contenido enriquecido, as como estilos, plantillas

desencadenadores. La lista siguiente proporciona ejemplos de cmo utilizar estas caractersticas para crear
experiencias personalizadas y coherentes sin tener que crear un nuevo control.

Contenido enriquecido. Muchos de los controles estndar de WPF son compatibles con contenido
enriquecido. Por ejemplo, la propiedad de contenido de un control Button es del tipo Object, de modo
que, en teora, en un control Button se puede mostrar cualquier elemento. Para que un botn muestre
una imagen y texto, puede agregar una imagen y un control TextBlock a StackPanel y asignar
StackPanel a la propiedad Content. Debido a que estos controles permiten mostrar elementos visuales
de WPF y datos arbitrarios, se reduce la necesidad de crear un nuevo control o modificar un control
existente para permitir una visualizacin compleja.

Estilos. Un objeto Style es una coleccin de valores que representan propiedades para un control.
Utilizando estilos, puede crear una representacin reutilizable del aspecto y el comportamiento
deseados para un control sin necesidad de escribir un nuevo control. Por ejemplo, suponga que desea
que la fuente de todos los controles TextBlock sea Arial de color rojo con un tamao de 14. Puede
crear un estilo como un recurso y establecer las propiedades adecuadas en consecuencia. A
continuacin, todos los controles TextBlock que agregue a la aplicacin tendrn la misma apariencia.

Plantillas de datos.DataTemplate permite personalizar cmo se muestran los datos en un control. Por
ejemplo, DataTemplate se puede utilizar para especificar cmo se muestran los datos en ListBox.
Adems de personalizar la apariencia de los datos, un objeto DataTemplate puede incluir elementos de
interfaz de usuario, lo que aporta gran flexibilidad en las interfaces de usuario personalizadas. Por
ejemplo, con DataTemplate se puede crear un control ComboBox en el que cada elemento contenga
una casilla.

Plantillas de control. Muchos controles de WPF utilizan ControlTemplate para definir la estructura y
apariencia del control, a fin de independizar su apariencia de su funcionalidad. Es posible cambiar
drsticamente la apariencia de un control si se redefine su ControlTemplate. Por ejemplo, supongamos
que desea un control semejante a un semforo. La interfaz de usuario y la funcionalidad de este
control son sencillas. El control est compuesto de tres crculos y slo uno de ellos puede estar
encendido en un momento dado. Despus de reflexionar, puede que se d cuenta de que RadioButton
proporciona la funcionalidad de permitir la seleccin de una sola opcin a la vez, aunque la apariencia
predeterminada de RadioButton no se parece nada a un semforo. Dado que RadioButton utiliza una
plantilla de control para definir su apariencia, resulta fcil redefinir ControlTemplate para adaptarlo a
los requisitos del control y utilizar los botones de opcin para crear el semforo.
Nota:
Aunque RadioButton puede utilizar un objeto DataTemplate, en este ejemplo no basta con
DataTemplate. DataTemplate define la apariencia del contenido de un control. En el caso de un botn
de opcin (RadioButton), su contenido es aquello que aparece a la derecha del crculo, que indica si
RadioButton est seleccionado. En el ejemplo del semforo, el botn de opcin tiene que ser slo un
crculo capaz de "encenderse". Dado que el requisito de apariencia del semforo es tan distinto de la
apariencia predeterminada de RadioButton, es necesario redefinir ControlTemplate. En general,
DataTemplate se utiliza para definir el contenido (o los datos) de un control y ControlTemplate se
utiliza para definir la estructura del control.

Desencadenadores.Trigger permite cambiar dinmicamente la apariencia y el comportamiento de un


control sin crear uno nuevo. Por ejemplo, suponga que tiene varios controles ListBox en una aplicacin
y desea que todos los elementos de ListBox se muestren en negrita y en rojo cuando estn
seleccionados. Su primer impulso podra ser crear una clase que herede de ListBox e invalidar el
mtodo OnSelectionChanged para cambiar la apariencia del elemento seleccionado; sin embargo, sera
ms apropiado agregar a un estilo de ListBoxItem un desencadenador que cambie la apariencia del
elemento seleccionado. Un desencadenador permite cambiar los valores de las propiedades o realizar

MCT: Luis Dueas

Pag 17 de 473

Manual de Windows Presentation Foundation


acciones basadas en el valor de una propiedad. Un objeto EventTrigger permite realizar acciones
cuando se produce un evento.
En general, si el control tiene la misma funcionalidad que un control existente, pero desea modificar su
apariencia, antes de nada debe estudiar si puede utilizar cualquiera de los mtodos descritos en esta seccin a
fin de cambiar la apariencia del control existente.
Modelos para la creacin de controles
El modelo de contenido enriquecido, los estilos, las plantillas y los desencadenadores minimizan la necesidad de
crear controles nuevos. Sin embargo, si se ve obligado a crear uno nuevo, es importante comprender los
distintos modelos de creacin de controles de WPF. WPF proporciona tres modelos generales para crear
controles, cada uno de los cuales ofrece un conjunto diferente de caractersticas y un nivel de flexibilidad
distinto. Las clases base de los tres modelos son UserControl, Control y FrameworkElement.
Derivar de UserControl
La manera ms sencilla de crear un control en WPF es derivar de UserControl. Cuando se genera un control que
hereda de UserControl, se agregan los componentes existentes a UserControl, se les da un nombre y se hace
referencia los controladores de eventos en Lenguaje de marcado de aplicaciones extensible (XAML). A
continuacin, puede hacer referencia a los elementos con nombre y definir los controladores de eventos en el
cdigo. Este modelo de programacin es muy similar al utilizado para la programacin de aplicaciones en WPF.
Si est generado correctamente, un control UserControl puede aprovechar las ventajas del contenido
enriquecido, los estilos y los desencadenadores. Sin embargo, si el control hereda de UserControl, las personas
que lo utilicen no podrn usar DataTemplate o ControlTemplate para personalizar su apariencia. Es necesario
derivar de la clase Control o de una de sus clases derivadas (excepto UserControl) para crear un control
personalizado que admita plantillas.
Ventajas de derivar de UserControl
Considere la posibilidad de derivar de UserControl si se cumplen todas las condiciones siguientes:

Desea generar un control de forma similar a como se genera una aplicacin.


El control est compuesto solamente de componentes existentes.
No necesita permitir una personalizacin compleja.

Derivar de Control
Derivar de la clase Control es el modelo utilizado por la mayora de los controles WPF existentes. Cuando se
crea un control que hereda de la clase Control, su apariencia se define mediante plantillas. Al hacerlo, se
independiza la lgica de funcionamiento de la representacin visual. Tambin se puede garantizar la
independencia entre la interfaz de usuario y la lgica si se usan comandos y enlaces en lugar de eventos, y se
evita en lo posible hacer referencia a los elementos de ControlTemplate. Si la interfaz de usuario y la lgica del
control estn debidamente desconectadas, un usuario del control podr redefinir el objeto ControlTemplate del
control para personalizar su apariencia. Aunque generar un objeto Control personalizado no es tan sencillo
como generar un objeto UserControl, un objeto Control personalizado proporciona mayor flexibilidad.
Ventajas de derivar de Control
Considere la posibilidad de derivar de Control en lugar de utilizar la clase UserControl si se cumple cualquiera
de las condiciones siguientes:

Desea que la apariencia del control sea personalizable a travs del objeto ControlTemplate.
Desea que el control admita temas diferentes.

Derivar de FrameworkElement
Los controles derivados de UserControl o Control se basan en la composicin de elementos existentes. Para
muchos escenarios, sta es una solucin aceptable, porque cualquier objeto que hereda de FrameworkElement
puede estar en un objeto ControlTemplate. Sin embargo, en ocasiones la apariencia de un control requiere una

MCT: Luis Dueas

Pag 18 de 473

Manual de Windows Presentation Foundation


funcionalidad que va ms all de la simple composicin de elementos. Para estos escenarios, basar un
componente en FrameworkElement es la opcin correcta.
Hay dos mtodos estndar para generar componentes basados en FrameworkElement: la representacin
directa y la composicin de elementos personalizada. La representacin directa implica invalidar el mtodo
OnRender de FrameworkElement y proporcionar operaciones DrawingContext que definan explcitamente el
aspecto visual del componente. ste es el mtodo utilizado por Image y Border. La composicin de elementos
personalizada implica utilizar objetos de tipo Visual para crear la apariencia del componente. Tambin es
posible mezclar la representacin directa y la composicin de elementos personalizada en el mismo control.
Ventajas de derivar de FrameworkElement
Considere la posibilidad de derivar de FrameworkElement si se cumple cualquiera de las condiciones siguientes:

Desea tener un control preciso sobre la apariencia del control ms all de lo que proporciona la simple
composicin de elementos.

Desea definir el aspecto del control definiendo una lgica de representacin propia.
Desea componer elementos existentes de maneras nuevas que excedan lo posible con UserControl y
Control.

Fundamentos de creacin de controles


Como se coment anteriormente, una de las caractersticas ms eficaces de WPF es la posibilidad de no tener
que limitarse a establecer las propiedades bsicas de un control para modificar su apariencia y
comportamiento, sin estar obligado a crear un control personalizado. Las caractersticas de estilo, enlace de
datos y desencadenadores son posibles gracias al sistema de propiedades de WPF y al sistema de eventos de
WPF. Si implementa propiedades de dependencia y eventos enrutados en un control personalizado, los usuarios
podrn

utilizar

estas

caractersticas

exactamente

igual

que

para

un

control

distribuido

con

WPF,

independientemente del modelo utilizado para crearlo.


Utilizar propiedades de dependencia
Cuando una propiedad es de dependencia, es posible realizar las acciones siguientes:

Establecer la propiedad en un estilo.


Enlazar la propiedad a un origen de datos.
Utilizar un recurso dinmico como valor de la propiedad.
Animar la propiedad.

Si desea que una propiedad del control admita cualquiera de estas funcionalidades, debe implementarla como
propiedad de dependencia. En el ejemplo siguiente se define una propiedad de dependencia denominada Value
mediante este procedimiento:

Defina

un

identificador

de

DependencyProperty

denominado

ValueProperty

como

campo

public static readonly.

Registre el nombre de la propiedad en el sistema de propiedades, mediante una llamada a


DependencyProperty.Register, para especificar lo siguiente:

El nombre de la propiedad.
El tipo de la propiedad.
El tipo que posee la propiedad.
Los metadatos de la propiedad. Los metadatos contienen el valor predeterminado de la
propiedad, CoerceValueCallback y PropertyChangedCallback.

Defina una propiedad de "contenedor" de CLR denominada Value (que es el mismo nombre que se
utiliza para registrar la propiedad de dependencia) mediante la implementacin de los descriptores de
acceso get y set de la propiedad. Observe que los descriptores de acceso get y set llaman nicamente
a GetValue y SetValue, respectivamente. Se recomienda que los descriptores de acceso de las
propiedades de dependencia no contengan lgica adicional, porque los clientes y WPF pueden omitir

MCT: Luis Dueas

Pag 19 de 473

Manual de Windows Presentation Foundation


dichos descriptores y llamar a GetValue y SetValue directamente. Por ejemplo, cuando una propiedad
est enlazada a un origen de datos, no se llama al descriptor de acceso set de la propiedad. En lugar
de agregar lgica adicional a los descriptores de acceso get y set, utilice los delegados
ValidateValueCallback, PropertyChangedCallback y CoerceValueCallback para responder a los cambios
del valor o comprobar si el valor ha cambiado.

Defina un mtodo para CoerceValueCallback denominado CoerceValue. CoerceValue garantiza que


Value sea mayor o igual que MinValue y menor o igual que MaxValue.

Defina un mtodo para PropertyChangedCallback, denominado OnValueChanged. OnValueChanged


crea un objeto RoutedPropertyChangedEventArgs<(Of <(T>)>) y se prepara para provocar el evento

enrutado ValueChanged. Los eventos enrutados se abordan en la seccin siguiente.


/// <summary>
/// Identifies the Value dependency property.
/// </summary>
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(decimal), typeof(NumericUpDown),
new FrameworkPropertyMetadata(MinValue,
new PropertyChangedCallback(OnValueChanged),
new CoerceValueCallback(CoerceValue)));
/// <summary>
/// Gets or sets the value assigned to the control.
/// </summary>
public decimal Value
{
get { return (decimal)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
private static object CoerceValue(DependencyObject element, object value)
{
decimal newValue = (decimal)value;
NumericUpDown control = (NumericUpDown)element;
newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue));
return newValue;
}
private static void OnValueChanged(DependencyObject obj,
DependencyPropertyChangedEventArgs args)
{
NumericUpDown control = (NumericUpDown)obj;
RoutedPropertyChangedEventArgs<decimal> e = _
new RoutedPropertyChangedEventArgs<decimal>(
(decimal)args.OldValue, (decimal)args.NewValue, ValueChangedEvent);
control.OnValueChanged(e);
}
Utilizar eventos enrutados
Al igual que las propiedades de dependencia extienden la nocin de propiedades CLR con funciones adicionales,
los eventos enrutados extienden la nocin de eventos CLR estndar. Al crear un nuevo control WPF, tambin es
conveniente implementar el evento como enrutado, porque un evento enrutado admite el comportamiento
siguiente:

Los eventos se pueden controlar en un elemento primario de varios controles. Si un evento es de


propagacin, puede suscribirse a l un elemento primario nico del rbol de elementos. A
continuacin, los autores de la aplicacin pueden utilizar un mismo controlador para responder al
evento de varios controles. Por ejemplo, si el control forma parte de cada uno de los elementos de un
control ListBox (por estar incluido en DataTemplate), el programador de la aplicacin puede definir el
controlador del evento del control en ListBox. Cada vez que se produzca el evento en cualquiera de los
controles, se llamar al controlador.

Los eventos enrutados se pueden utilizar en EventSetter, lo que permite a los programadores de
aplicaciones especificar el controlador de un evento en un estilo.

Los eventos enrutados se pueden utilizar en EventTrigger, lo que resulta til para animar propiedades
mediante XAML.

En el ejemplo siguiente se define un evento enrutado mediante este procedimiento:

MCT: Luis Dueas

Pag 20 de 473

Manual de Windows Presentation Foundation

Defina

un

identificador

de

RoutedEvent

denominado

ValueChangedEvent

como

campo

public static readonly.

Registre el evento enrutado mediante una llamada al mtodo EventManager.RegisterRoutedEvent. En


el ejemplo se especifica la informacin siguiente al llamar a RegisterRoutedEvent:

El nombre del evento es ValueChanged.


La estrategia del enrutamiento es Bubble. Esto significa que primero se llama a un
controlador de eventos en el origen (el objeto que provoca el evento) y, a continuacin, se llama
sucesivamente a los controladores de eventos en los elementos primarios del origen, empezando
por el controlador de eventos del elemento primario ms cercano.

El tipo del controlador de eventos es RoutedPropertyChangedEventHandler<(Of <(T>)>),


construido con un tipo Decimal.

El tipo propietario del evento es NumericUpDown.

Declare un evento pblico denominado ValueChanged e incluya declaraciones de descriptores de


acceso del evento. En el ejemplo se llama a AddHandler en la declaracin del descriptor de acceso add
y a RemoveHandler en la declaracin del descriptor de acceso remove a fin de utilizar los servicios de
eventos de WPF.

Cree un mtodo virtual protegido denominado OnValueChanged que provoque el evento ValueChanged
/// <summary>
/// Identifies the ValueChanged routed event.
/// </summary>
public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent(
"ValueChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler
<decimal>), typeof(NumericUpDown));
/// <summary>
/// Occurs when the Value property changes.
/// </summary>
public event RoutedPropertyChangedEventHandler<decimal> ValueChanged
{
add { AddHandler(ValueChangedEvent, value); }
remove { RemoveHandler(ValueChangedEvent, value); }
}
/// <summary>
/// Raises the ValueChanged event.
/// </summary>
/// <param name="args">Arguments associated with the ValueChanged event.</param>
protected virtual void OnValueChanged(RoutedPropertyChangedEventArgs<decimal> args)
{
RaiseEvent(args);
}
Utilizar el enlace
Para desacoplar la interfaz de usuario del control de su lgica, puede ser conveniente utilizar el enlace de datos.
Esto resulta particularmente importante si la apariencia del control se define mediante ControlTemplate. Al
utilizar el enlace de datos, puede que consiga eliminar la necesidad de hacer referencia a partes concretas de la
interfaz de usuario desde el cdigo. Es conveniente evitar hacer referencia a elementos incluidos en
ControlTemplate, ya que cuando el cdigo hace referencia a elementos incluidos en ControlTemplate y se
modifica ControlTemplate, el elemento al que se hace referencia debe incluirse en el nuevo objeto
ControlTemplate.
En el ejemplo siguiente se actualiza el control TextBlock del control NumericUpDown, para ello se le asigna un
nombre y se hace referencia al cuadro de texto por su nombre en el cdigo.
<Border BorderThickness="1" BorderBrush="Gray" Margin="2"
Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">
<TextBlock Name="valueText" Width="60" TextAlignment="Right" Padding="5"/>
</Border>
private void UpdateTextBlock()
{
valueText.Text = Value.ToString();
}
En el ejemplo siguiente se utiliza el enlace para lograr lo mismo.
<Border BorderThickness="1" BorderBrush="Gray" Margin="2"

MCT: Luis Dueas

Pag 21 de 473

Manual de Windows Presentation Foundation


Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">
<!--Bind the TextBlock to the Value property-->
<TextBlock Width="60" TextAlignment="Right" Padding="5"
Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:NumericUpDown}}, Path=Value}"/>
</Border>
Definir y utilizar comandos
Considere la posibilidad de definir y utilizar comandos para proporcionar funcionalidad en lugar de controlar
eventos. Cuando utiliza controladores de eventos en el control, las aplicaciones no pueden obtener acceso a la
accin realizada dentro del controlador de eventos. Si implementa comandos en el control, las aplicaciones
pueden tener acceso a la funcionalidad que, de otro modo, no estara disponible.
El ejemplo siguiente muestra la parte de un control que controla el evento Click para que dos botones cambien
el valor del control NumericUpDown. Independientemente de que el control sea UserControl o Control con
ControlTemplate, la interfaz de usuario y la lgica estn estrechamente unidas, ya que el control utiliza
controladores de eventos.
<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>
private void upButton_Click(object sender, EventArgs e)
{
Value++;
}
private void downButton_Click(object sender, EventArgs e)
{
Value--;
}
En el ejemplo siguiente se definen dos comandos que cambian el valor del control NumericUpDown.
public static RoutedCommand IncreaseCommand
{
get
{
return _increaseCommand;
}
}
public static RoutedCommand DecreaseCommand
{
get
{
return _decreaseCommand;
}
}
private static void InitializeCommands()
{
_increaseCommand = new RoutedCommand("IncreaseCommand", typeof(NumericUpDown));
CommandManager.RegisterClassCommandBinding(typeof(NumericUpDown),
new CommandBinding(_increaseCommand, OnIncreaseCommand));
CommandManager.RegisterClassInputBinding(typeof(NumericUpDown),
new InputBinding(_increaseCommand, new KeyGesture(Key.Up)));
_decreaseCommand = new RoutedCommand("DecreaseCommand", typeof(NumericUpDown));
CommandManager.RegisterClassCommandBinding(typeof(NumericUpDown),
new CommandBinding(_decreaseCommand, OnDecreaseCommand));
CommandManager.RegisterClassInputBinding(typeof(NumericUpDown),
new InputBinding(_decreaseCommand,
new KeyGesture(Key.Down)));
}
private static void OnIncreaseCommand(object sender, ExecutedRoutedEventArgs e)
{
NumericUpDown control = sender as NumericUpDown;
if (control != null)
{
control.OnIncrease();
}
}
private static void OnDecreaseCommand(object sender, ExecutedRoutedEventArgs e)
{
NumericUpDown control = sender as NumericUpDown;
if (control != null)
{
control.OnDecrease();
}
}
protected virtual void OnIncrease()
{
Value++;
}

MCT: Luis Dueas

Pag 22 de 473

Manual de Windows Presentation Foundation


protected virtual void OnDecrease()
{
Value--;
}
private static RoutedCommand _increaseCommand;
private static RoutedCommand _decreaseCommand;
Los elementos de la plantilla pueden hacer referencia, entonces, a los comandos, como se muestra en el
ejemplo siguiente.
<RepeatButton
Command="{x:Static local:NumericUpDown.IncreaseCommand}"
Grid.Column="1" Grid.Row="0">Up</RepeatButton>
<RepeatButton
Command="{x:Static local:NumericUpDown.DecreaseCommand}"
Grid.Column="1" Grid.Row="1">Down</RepeatButton>
Ahora las aplicaciones pueden hacer referencia a los enlaces para tener acceso a la funcionalidad que no era
accesible cuando el control utilizaba controladores de eventos.
Especificar que un elemento es necesario en ControlTemplate
En las secciones anteriores se explica cmo utilizar el enlace de datos y los comandos para que un control no
haga referencia a elementos de su ControlTemplate desde el cdigo. Sin embargo, puede haber ocasiones en
las que hacer referencia a un elemento sea inevitable. Si se produce esta situacin, debe aplicar el atributo
TemplatePartAttribute al control. Este atributo informa a los autores de la plantilla de los tipos y los nombres de
los elementos incluidos en ControlTemplate. No es necesario nombrar cada uno de los elementos de
ControlTemplate en TemplatePartAttribute. De hecho, cuantos menos elementos se nombren, mejor. Sin
embargo, si hace referencia al elemento en el cdigo, debe utilizar TemplatePartAttribute.
Disear para diseadores
Para recibir soporte tcnico para controles personalizados de WPF en Windows Presentation Foundation (WPF)
Designer for Visual Studio (por ejemplo, la edicin de propiedades con la ventana Propiedades), siga estas
instrucciones.
Propiedades de dependencia
Es importante implementar los descriptores de acceso CLR get y set como se describi anteriormente en
"Utilizar propiedades de dependencia". Los diseadores pueden utilizar el contenedor para detectar la presencia
de una propiedad de dependencia, pero no se les exige, al igual que a WPF y a los clientes del control, llamar a
los descriptores de acceso al obtener o establecer la propiedad.
Propiedades asociadas
Para implementar propiedades asociadas en controles personalizados, es recomendable que utilice las
siguientes instrucciones:

Consideremos

una

clase

public static readonly

DependencyProperty

con

el

formato

NombreDePropiedadProperty creada mediante el mtodo RegisterAttached. El nombre de propiedad


que se pasa a RegisterAttached debe coincidir con NombreDePropiedad.

Implemente

un

par

de

mtodos

publicstatic

CLR

denominados

SetNombreDePropiedad

GetNombreDePropiedad. Ambos mtodos deben aceptar una clase derivada de DependencyProperty


como su primer argumento. El mtodo SetNombreDePropiedad tambin acepta un argumento cuyo
tipo

coincida

con

NombreDePropiedad

el

tipo
debe

de

datos

devolver

registrado
un

valor

para

la

propiedad.

del

mismo

tipo.

El

mtodo

Si

falta

Getmtodo
el

mtodo

SetNombreDePropiedad, la propiedad se marca como de slo lectura.

SetNombreDePropiedad y GetNombreDePropiedad deben enrutar directamente a los mtodos GetValue


y los mtodos SetValue del objeto de dependencia de destino, respectivamente. Los diseadores
pueden tener acceso a la propiedad asociada mediante una llamada a travs del contenedor de mtodo
o una llamada directa al objeto de dependencia de destino.

Definir y utilizar recursos compartidos para el control

MCT: Luis Dueas

Pag 23 de 473

Manual de Windows Presentation Foundation


Puede incluir el control en el mismo ensamblado que la aplicacin o bien empaquetarlo en un ensamblado
independiente que se pueda utilizar en varias aplicaciones. En general, la informacin analizada en este tema
es aplicable independientemente del mtodo que se utilice. Sin embargo, es necesario destacar una diferencia.
Al incluir un control en el mismo ensamblado que una aplicacin, puede agregar recursos globales al archivo
app.xaml libremente. Sin embargo, un ensamblado que slo contiene controles no tiene asociado un objeto
Application, por lo que no hay disponible un archivo app.xaml.
Cuando una aplicacin busca un recurso, la bsqueda se realiza en tres niveles en el orden que se indica a
continuacin:
1.

Nivel de elemento: el sistema empieza por el elemento que hace referencia al recurso y, a
continuacin, busca en los recursos del elemento primario lgico, y as sucesivamente, hasta que se
alcanza el elemento raz.

2.
3.

Nivel de aplicacin: recursos definidos por el objeto Application.


Nivel de tema: los diccionarios del nivel de tema se almacenan en una subcarpeta denominada Temas.
Los

archivos

de

la

carpeta

Temas

corresponden

los

temas.

Por

ejemplo,

puede

tener

Aero.NormalColor.xaml, Luna.NormalColor.xaml, Royale.NormalColor.xaml, etc. Tambin puede tener un


archivo denominado generic.xaml. Cuando el sistema busca un recurso en el nivel de temas, primero
busca en el archivo especfico del tema y, despus, en generic.xaml.
Cuando el control se encuentra en un ensamblado independiente de la aplicacin, debe colocar los recursos
globales en el nivel de elemento o el nivel de tema. Ambos mtodos tienen sus ventajas.
Definir recursos en el nivel de elemento
Para definir recursos compartidos en el nivel de elemento, cree un diccionario de recursos personalizado y
combnelo con el diccionario de recursos del control. Cuando utilice este mtodo, puede asignar al archivo de
recursos el nombre que desee e incluir dicho archivo en la misma carpeta que los controles. Los recursos del
nivel de elemento tambin pueden utilizar cadenas simples como claves. En el ejemplo siguiente se crea un
archivo de recursos de LinearGradientBrush llamado Dictionary1.XAML.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<LinearGradientBrush
x:Key="myBrush"
StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="Red" Offset="0.25" />
<GradientStop Color="Blue" Offset="0.75" />
</LinearGradientBrush>
</ResourceDictionary>
Una vez definido el diccionario, deber combinarlo con el diccionario de recursos del control. Para ello, utilice
XAML o el cdigo.
En el ejemplo siguiente se combina un diccionario de recursos mediante XAML.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
El inconveniente de este enfoque es que se crea un objeto ResourceDictionary cada vez que se hace referencia
a l. Por ejemplo, si tiene 10 controles personalizados en la biblioteca y se combinan los diccionarios de
recursos compartidos de cada control mediante XAML, se crean 10 objetos ResourceDictionary idnticos. Para
evitarlo, cree una clase esttica que combine los recursos del cdigo y devuelva el elemento ResourceDictionary
resultante.
En el ejemplo siguiente se crea una clase que devuelve un elemento ResourceDictionary compartido.
internal static class SharedDictionaryManager
{
internal static ResourceDictionary SharedDictionary

MCT: Luis Dueas

Pag 24 de 473

Manual de Windows Presentation Foundation


{
get
{
if (_sharedDictionary == null)
{
System.Uri resourceLocater =
new System.Uri("/ElementResourcesCustomControlLibrary;component/Dictionary1.xaml",
System.UriKind.Relative);
_sharedDictionary = (ResourceDictionary)Application.LoadComponent(resourceLocater);
}
return _sharedDictionary;
}
}
private static ResourceDictionary _sharedDictionary;

}
En el ejemplo siguiente se combina el recurso compartido con los recursos de un control personalizado en el
constructor

del

control

antes

de

llamar

InitilizeComponent.

Dado

que

SharedDictionaryManager.SharedDictionary es una propiedad esttica, ResourceDictionary slo se crea una vez.


Dado que el diccionario de recursos se combin antes de llamar a InitializeComponent, los recursos estn
disponibles para el control en su archivo de XAML.
public NumericUpDown()
{
this.Resources.MergedDictionaries.Add(SharedDictionaryManager.SharedDictionary);
InitializeComponent();
}
Definir recursos en el nivel de tema
WPF permite crear recursos para distintos temas de Windows. Como autor del control, puede definir un recurso
para un tema concreto a fin de cambiar la apariencia del control en funcin del tema que se utilice. Por
ejemplo, la apariencia de un elemento Button en el tema de Windows clsico (tema predeterminado de
Windows 2000) es distinta de la de un elemento Button en el tema Luna de Windows (tema predeterminado de
Windows XP) porque Button utiliza un elemento ControlTemplate distinto para cada tema.
Los recursos especficos de un tema se mantienen en un diccionario de recursos con un nombre de archivo
concreto. Estos archivos deben estar en una carpeta llamada Temas, que es una subcarpeta de la carpeta que
contiene el control. En la tabla siguiente se enumeran los archivos de diccionario de recursos y el tema asociado
a cada archivo:
Nombre del archivo de diccionario de
recursos

Tema de Windows

Classic.xaml

Aspecto clsico de Windows 9x/2000 en Windows XP

Luna.NormalColor.xaml

Tema azul predeterminado en Windows XP

Luna.Homestead.xaml

Tema verde olivo en Windows XP

Luna.Metallic.xaml

Tema plateado en Windows XP

Royale.NormalColor.xaml

Tema predeterminado en Windows XP Media Center


Edition

Aero.NormalColor.xaml

Tema predeterminado en Windows Vista

No es necesario definir un recurso para cada tema. Si no se ha definido un recurso para un tema concreto, el
control utiliza el recurso genrico, que se encuentra en un archivo de diccionario de recursos llamado
generic.xaml en la misma carpeta que los archivos de diccionario de recursos especficos de un tema. Aunque
generic.xaml no corresponde a un tema concreto de Windows, no deja de ser un diccionario del nivel de tema.
Ejemplo NumericUpDown Custom Control with Theme and UI Automation Support contiene dos diccionarios de
recursos para el control NumericUpDown: uno est en generic.xaml y el otro en Luna.NormalColor.xaml. Puede
ejecutar la aplicacin y cambiar entre el tema plateado de Windows XP y otro tema a fin de ver la diferencia
entre las dos plantillas de control. (Si est ejecutandoWindows Vista, puede cambiar el nombre de
Luna.NormalColor.xaml a Aero.NormalColor.xaml y cambiar entre dos temas, como el de Windows clsico y el
tema predeterminado de Windows Vista.)

MCT: Luis Dueas

Pag 25 de 473

Manual de Windows Presentation Foundation


Al colocar un elemento ControlTemplate en cualquiera de los archivos de diccionario de recursos especficos de
un tema, debe crear un constructor esttico para el control y llamar al mtodo OverrideMetadata(Type,
PropertyMetadata) en DefaultStyleKey, como se muestra en el ejemplo siguiente.
static NumericUpDown()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown),
new FrameworkPropertyMetadata(typeof(NumericUpDown)));
}
Definir y hacer referencia a claves para los recursos de tema
Al definir un recurso en el nivel de elemento, puede asignar una cadena como clave de ste y obtener acceso al
recurso a travs de la cadena. Al definir un recurso en el nivel de tema, debe utilizar un elemento
ComponentResourceKey como clave. En el ejemplo siguiente se define un recurso en generic.xaml.
En el ejemplo siguiente se hace referencia al recurso mediante la especificacin de ComponentResourceKey
como clave.
Especificar la ubicacin de los recursos de tema
Para buscar los recursos de un control, la aplicacin host debe saber que el ensamblado contiene recursos
especficos del control. Para ello, agregue ThemeInfoAttribute al ensamblado que contiene el control.
ThemeInfoAttribute tiene una propiedad GenericDictionaryLocation que especifica la ubicacin de los recursos
genricos y una propiedad ThemeDictionaryLocation que especifica la ubicacin de los recursos especficos de
tema.
En el ejemplo siguiente se establecen las propiedades ThemeDictionaryLocation y GenericDictionaryLocation en
SourceAssembly, para especificar que los recursos genricos y especficos de tema estn en el mismo
ensamblado que el control.
[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly,
ResourceDictionaryLocation.SourceAssembly)]
Heredar de UserControl frente a usar ControlTemplate
Varios ejemplos muestran distintos mtodos para escribir y empaquetar el control NumericUpDown. En Ejemplo
NumericUpDown

UserControl

with

DependencyProperty

and

RoutedEvent,

NumericUpDown

hereda de

UserControl; en Ejemplo NumericUpDown Custom Control with Theme and UI Automation Support,
NumericUpDown hereda de Control y utiliza un elemento ControlTemplate. En esta seccin se describen
brevemente algunas de las diferencias entre ambos y se explica por qu el control que utiliza ControlTemplate
es ms extensible.
La diferencia ms importante es que el control NumericUpDown que hereda de UserControl no utiliza un
elemento ControlTemplate y el control que hereda directamente de Control s lo utiliza. En el ejemplo siguiente
se muestra XAML del control que hereda de UserControl. Como puede observar, XAML es muy similar al
resultado que podra obtener al crear una aplicacin y comenzar con Window o Page.
<!--XAML for NumericUpDown that inherits from UserControl.-->
<UserControl x:Class="MyUserControl.NumericUpDown"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyUserControl">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border BorderThickness="1" BorderBrush="Gray" Margin="2" Grid.RowSpan="2"
VerticalAlignment="Center" HorizontalAlignment="Stretch">
<!--Bind the TextBlock to the Value property-->
<TextBlock
Width="60" TextAlignment="Right" Padding="5"
Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:NumericUpDown}}, Path=Value}"/>
</Border>
<RepeatButton Name="upButton" Click="upButton_Click"
Grid.Column="1" Grid.Row="0">Up</RepeatButton>
<RepeatButton Name="downButton" Click="downButton_Click"

MCT: Luis Dueas

Pag 26 de 473

Manual de Windows Presentation Foundation


Grid.Column="1" Grid.Row="1">Down</RepeatButton>
</Grid>
</UserControl>
En el ejemplo siguiente se muestra ControlTemplate del control que hereda de Control. Observe que
ControlTemplate es similar a XAML en UserControl, con tan slo algunas diferencias de sintaxis.
<!--ControlTemplate for NumericUpDown that inherits from Control.-->
<Style TargetType="{x:Type local:NumericUpDown}">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:NumericUpDown}">
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border BorderThickness="1" BorderBrush="Gray" Margin="2" Grid.RowSpan="2"
VerticalAlignment="Center" HorizontalAlignment="Stretch">
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value}"
Width="60" TextAlignment="Right" Padding="5"/>
</Border>
<RepeatButton Command="{x:Static local:NumericUpDown.IncreaseCommand}"
Grid.Column="1" Grid.Row="0">Up</RepeatButton>
<RepeatButton Command="{x:Static local:NumericUpDown.DecreaseCommand}"
Grid.Column="1" Grid.Row="1">Down</RepeatButton>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
La mayor diferencia entre los dos ejemplos anteriores es que aquel en el que se utiliza ControlTemplate tiene
una apariencia personalizable y no as el que hereda de UserControl. En el caso en el que NumericUpDown
hereda de UserControl, los desarrolladores de aplicaciones no pueden hacer nada para cambiar la apariencia del
control. De hecho, aunque NumericUPDown tiene una propiedad ControlTemplate (dado que UserControl hereda
de Control), si alguien intenta establecerla, se producir una excepcin en tiempo de ejecucin. Por otra parte,
un desarrollador de aplicaciones que utiliza el control NumericUpDown que hereda de Control puede crear
libremente un nuevo elemento ControlTemplate para el control. Por ejemplo, se podra crear un elemento
ControlTemplate que colocara los botones a izquierda y derecha de TextBlock en lugar de encima y debajo.
La diferencia entre los dos enfoques es evidente en la diferencia sintctica de los ejemplos anteriores. El control
que utiliza ControlTemplate establece la propiedad Template en un elemento Style para NumericUpDown. ste
es un mtodo habitual de crear plantillas de control. Al establecer la propiedad Template en un estilo, se
especifica que todas las instancias del control utilizarn ese elemento ControlTemplate. Los desarrolladores de
aplicaciones pueden cambiar libremente la propiedad Template de un control NumericcUpDown para
personalizar su apariencia. En cambio, el elemento XAML del control que hereda de UserControl rellena la
propiedad Content de NumericUpDown (<UserControl.Content> est implcito en XAML). Si un desarrollador de
aplicaciones no puede cambiar la propiedad Content, no se puede utilizar NumericUpDown.
Otra diferencia entre los ejemplos es la forma en que los controles responden a los botones Arriba y Abajo. El
control que hereda de UserControl controla el evento Click y el control que utiliza ControlTemplate implementa
comandos y se enlaza a stos en el elemento ControlTemplate. Como resultado, un desarrollador de
aplicaciones que crea un nuevo elemento ControlTemplate para NumericUpDown puede establecer tambin un
enlace a los comandos y conservar la funcionalidad del control. Si ControlTemplate controla el evento Click en
lugar de enlazarse a los comandos, el desarrollador de aplicaciones debera implementar controladores de
eventos al crear un nuevo elemento ControlTemplate e interrumpir de esa forma la encapsulacin de
NumericUpDown.
Otra diferencia es la sintaxis del enlace entre la propiedad Text de TextBlock y la propiedad Value. En el caso de
UserControl, el enlace especifica que RelativeSource es el control NumericUpDown primario y se enlaza a la
propiedad Value. En el caso de ControlTemplate, RelativeSource es el control al que pertenece la plantilla. El

MCT: Luis Dueas

Pag 27 de 473

Manual de Windows Presentation Foundation


resultado obtenido es el mismo, pero vale la pena mencionar que la sintaxis de enlace difiere entre ambos
ejemplos.
En

Ejemplo

NumericUpDown

Custom

Control

with

Theme

and

UI

Automation

Support,

el

control

NumericUpDown se encuentra en un ensamblado independiente de la aplicacin y define y utiliza recursos de


nivel de tema, pero este el control NumericUpDown est en el mismo ensamblado que la aplicacin y no define
ni utiliza recursos de nivel de tema.

4.3.2. Instrucciones para el Diseo de Controles con Estilos


Este documento resume un conjunto de procedimientos recomendados que se debe tener en cuenta al disear
un control con estilos y plantillas de fcil uso. Los vimos a travs de muchas pruebas y errores al trabajar en
los estilos del control de tema del conjunto del control integrado de WPF. Aprendimos que un estilo correcto
est formado tanto por una funcin de un modelo de objetos bien diseado como por el propio estilo. El lector
al que est dirigido este documento es el autor del control, y no el autor del estilo.
Terminologa
Diseo de estilos y plantillas hace referencia al conjunto de tecnologas que permiten al autor de un control
transferir los aspectos visuales de ste a su estilo y plantilla. Este conjunto de tecnologas incluye los siguientes
elementos:

Estilos (incluidos los establecedores de propiedades, los desencadenadores y los guiones grficos).
Recursos.
Plantillas de control.
Plantillas de datos.

Antes de empezar: introduccin al control


Antes de pasar a las instrucciones, es importante entender el control y haber definido su uso comn. El diseo
de estilos expone un conjunto de posibilidades que suele ser difcil de controlar. Los controles que se escriben
para usarlos mucho (en muchas aplicaciones, por muchos programadores) se enfrentan al desafo de que el
diseo de estilos se puede utilizar para efectuar cambios difciles de conseguir en el aspecto visual del control.
De hecho, puede que el control con estilos no se parezca en absoluto a la idea inicial de su autor. Puesto que la
flexibilidad proporcionada por los estilos es bsicamente ilimitada, puede usar la idea de uso comn para
ayudarle a delimitar el mbito de sus decisiones.
Para entender el uso comn del control, es prctico pensar en la finalidad del mismo. Qu aporta el control a
la tabla que no pueda aportar ningn otro control? El uso comn no implica ningn aspecto visual concreto,
sino ms bien la filosofa del control y un conjunto razonable de expectativas de uso. Esta comprensin permite
plantearse algunas suposiciones sobre el modelo de la composicin y los comportamientos definidos por estilo
del control en el caso comn. En el caso de ComboBox, por ejemplo, entender el uso comn no le ayudar a
formarse una idea sobre si un objeto ComboBox concreto tiene las esquinas redondeadas, pero s lo har sobre
el hecho de que ComboBox, probablemente, necesite una ventana emergente y algo de alternancia si est
abierto.
Instrucciones generales

No aplique el proceso de desarrollo de la plantilla de manera estricta. El proceso de desarrollo de la


plantilla de un control podra estar formado por elementos, comandos, enlaces, desencadenadores o
incluso los valores de propiedades necesarios o esperados de un control para que funcione
correctamente.

Simplifique el desarrollo al mximo posible.


El diseo basado en las expectativas del tiempo de diseo (es decir, al usar una herramienta
de diseo) tiene como resultado una plantilla de control cuyo estado es incompleto. WPF no
proporciona ninguna infraestructura de estado de "creacin"; por tanto, los controles se deben
crear con la expectativa de que tal estado pudiera ser vlido.

MCT: Luis Dueas

Pag 28 de 473

Manual de Windows Presentation Foundation

No produzca excepciones cuando no se siga un aspecto cualquiera de un diseo de plantilla.


En estas lneas, los paneles no deben producir excepciones si tienen demasiados elementos
secundarios o muy pocos.

Tenga en cuenta la funcionalidad perifrica de los elementos de la aplicacin auxiliar de la plantilla. Los
controles se deben basar en su funcionalidad bsica y en propuestas reales, y definir mediante el uso
comn del control. Para ello, utilice los elementos de creacin y de la aplicacin auxiliar en la plantilla
para habilitar comportamientos y visualizaciones perifricos; es decir, los comportamientos y
visualizaciones que no contribuyen a la funcionalidad bsica del control. Los elementos de la aplicacin
auxiliar se clasifican en tres categoras:

Los tipos de la aplicacin auxiliar independientes son controles pblicos y reutilizables o


primitivos que se usan "annimamente" en una plantilla; es decir, ni el elemento de la aplicacin
auxiliar ni el control con estilo se dan cuenta de la existencia del otro. Tcnicamente, el tipo de
cualquier elemento puede ser annimo pero, en este contexto, el trmino describe los tipos que
encapsulan la funcionalidad especializada para habilitar escenarios concretos.

Los elementos de aplicacin auxiliar basados en tipo son nuevos tipos que encapsulan la
funcionalidad especializada. Estos elementos se suelen disear con un intervalo de funcionalidad
menor que los controles comunes o primitivos. A diferencia de los elementos de la aplicacin
auxiliar independientes, los elementos de aplicacin auxiliar basados en tipo son conscientes del
contexto en el que se usan y, normalmente, deben compartir los datos con el control a cuya
plantilla pertenecen.

Los elementos de aplicacin auxiliar con nombre son los controles comunes o primitivos que
espera un control para buscar por nombre en su plantilla. A estos elementos se les asigna un
nombre conocido en la plantilla para que, de esta forma, el control busque el elemento e
interacte con l mediante programacin. Puede haber slo uno elemento con un nombre concreto
en una plantilla cualquiera.

En la tabla siguiente, se muestran los elementos de aplicacin auxiliar usados por los estilos de control
actuales (esta lista no es una lista completa):

Elemento

Tipo

Utilizado por

ContentPresenter

Basado en
tipo

Button, CheckBox, RadioButton, Frame, etc. (todos los


tipos de ContentControl)

ItemsPresenter

Basado en
tipo

ListBox, ComboBox, Menu, etc. (todos los tipos de


ItemsControl)

ToolBarOverflowPanel

Con nombre

ToolBar

Popup

Independiente

ComboBox, ToolBar, Menu, ToolTip, etc.

RepeatButton

Con nombre

Slider, ScrollBar, etc.

ScrollBar

Con nombre

ScrollViewer

ScrollViewer

Independiente

ListBox, ComboBox, Menu, Frame, etc.

TabPanel

Independiente

TabControl

TextBox

Con nombre

ComboBox

TickBar

Basado en
tipo

Slider

Reduzca el nmero necesario de enlaces especificados por el usuario o los valores de las propiedades
de los elementos de la aplicacin auxiliar. Es normal que un elemento de aplicacin auxiliar requiera
algunos valores de enlaces o de propiedades para funcionar correctamente en la plantilla del control. El
elemento de la aplicacin auxiliar y el control con plantilla deben establecer estos valores en la medida

MCT: Luis Dueas

Pag 29 de 473

Manual de Windows Presentation Foundation


de lo posible. Al establecer propiedades o enlaces, cuidado, se debe tener cuidado para no invalidar los
valores establecidos por el usuario. Los procedimientos recomendados especficos son los siguientes:

Los elementos de la aplicacin auxiliar con nombre los debe identificar el elemento primario y
ste debe establecer cualquier valor necesario del elemento de la aplicacin auxiliar.

Los elementos de la aplicacin auxiliar basados en tipo deben establecer los valores
necesarios directamente en s mismos. De esta forma, quiz el elemento de la aplicacin auxiliar
deba consultar el contexto de la informacin en la que se usa, incluido su TemplatedParent (el tipo
de control de la plantilla en la que se utiliza). Por ejemplo, ContentPresenter enlaza
automticamente la propiedad Content de su TemplatedParent a su propiedad Content cuando se
usa en un tipo derivado de ContentControl.

Los elementos de la aplicacin auxiliar independientes no se pueden optimizar de esta manera


porque, por definicin, ni el elemento de aplicacin auxiliar ni el primario saben que existe el otro.

Use la propiedad Name para marcar los elementos de una plantilla. Un control que necesite buscar un
elemento en su estilo para tener acceso a l mediante programacin debe hacerlo con la propiedad
Name y el ejemplo de FindName. Un control no debe producir ninguna excepcin cuando no se
encuentre un elemento, sino deshabilitar correctamente la funcionalidad que requera ese elemento.

Utilice los procedimientos recomendados para expresar el estado del control y el comportamiento de
un estilo. A continuacin, se muestra una lista ordenada de los procedimientos recomendados para
expresar los cambios del estado del control y de comportamiento de un estilo. Debe utilizar el primer
elemento de la lista que habilita su escenario.
1. Enlace

de

Propiedad.

Ejemplo:

enlace

entre

ComboBox.IsDropDownOpen

ToggleButton.IsChecked.
2. Cambios de propiedad desencadenados o animaciones de propiedad. Ejemplo: estado de
desplazamiento del puntero de Button.
3. Comando. Ejemplo: LineUpCommand / LineDownCommand de ScrollBar.
4. Elementos independientes de la aplicacin auxiliar. Ejemplo: TabPanel de TabControl.
5. Tipos de aplicacin auxiliar basados en tipo. Ejemplo: ContentPresenter de Button, TickBar de
Slider.
6. Elementos con nombre de la aplicacin auxiliar. Ejemplo: TextBox de ComboBox.
7. Eventos traspasados desde los tipos con nombre de la aplicacin auxiliar. Si realiza escuchas
de eventos traspasados desde un elemento de estilo, es necesario que se pueda identificar
exclusivamente el elemento que genera el evento. Ejemplo: Thumb de ToolBar.
8. Personalice el comportamiento de OnRender. Ejemplo: ButtonChrome de Button.

Utilice desencadenadores de estilo (en contraposicin a los desencadenadores de plantilla) con


moderacin. Los desencadenadores que afectan las propiedades de los elementos de la plantilla se
deben declarar en la plantilla. Los desencadenadores que afectan las propiedades del control (sin
TargetName) se pueden declarar en el estilo a menos que sepa que al cambiar la plantilla, tambin se
destruye el desencadenador.

Sea coherente con los modelos de estilo existentes. Muchas veces existen varias formas de resolver un
problema. Tenga en cuenta y sea coherente con los modelos de los estilos de un control existente en
la medida de lo posible. Esto es especialmente importante para los controles que se derivan del mismo
tipo base (por ejemplo, ContentControl, ItemsControl, RangeBase, etc.).

MCT: Luis Dueas

Pag 30 de 473

Manual de Windows Presentation Foundation

Exponga las propiedades para habilitar los escenarios de personalizacin comunes sin volver a crear
plantillas. WPF no admite las partes que se pueden conectar o personalizar; por tanto, al usuario de un
control le quedan slo dos mtodos de personalizacin: establecer las propiedades directamente o
mediante estilos. Teniendo esto presente, es correcto exponer un nmero limitado de propiedades
cuyo destino sean escenarios de personalizacin muy comunes y prioritarios que, de otro modo,
requeriran volver a crear las plantillas. A continuacin se detallan los procedimientos recomendados
para el momento en el que se habilitan los escenarios de personalizacin y cmo se habilitan:

Las personalizaciones muy comunes se deben exponer como propiedades del control y las
debe usar la plantilla.

Las personalizaciones menos comunes (aunque no raras) se deben exponer como


propiedades adjuntas y las debe usar la plantilla.

Es aceptable que las personalizaciones conocidas pero raras requieran que se vuelva a crear
la plantilla.

Consideraciones del tema

Los estilos del tema deben intentar ser coherentes con la semntica de la propiedad en todos los
temas, pero sin garantizarlo. Como parte de su documentacin, el control debe tener un documento
que describa la semntica de la propiedad del control; es decir, el "significado" de una propiedad de un
control. Por ejemplo, el control ComboBox debe definir el significado de la propiedad Background
dentro de ComboBox. Los estilos predeterminados del control deben intentar seguir la semntica
definida en ese documento en todos los temas. Los usuarios del control, por otra parte, deben tener en
cuenta que la semntica de la propiedad puede cambiar de un tema a otro. En algunos casos, una
propiedad especificada no se puede expresar en las restricciones visuales requeridas por un tema
concreto. (Por ejemplo, el tema Clsico no tiene ni un solo borde al que se pueda aplicar Thickness
para muchos controles.)

Los estilos del tema no tienen que ser coherentes con la semntica del desencadenador en todos los
temas. El comportamiento expuesto por un estilo de control a travs de los desencadenadores o
animaciones puede variar de un tema a otro. Los usuarios del control deben tener en cuenta que un
control no usar necesariamente el mismo mecanismo para lograr un comportamiento concreto en
todos los temas. Por ejemplo, un tema puede usar una animacin para expresar el comportamiento de
desplazamiento del puntero mientras que otro usa un desencadenador. De esta forma, se pueden
producir incoherencias en la conservacin del comportamiento en los controles personalizados. (Si se
cambia la propiedad de segundo plano, por ejemplo, puede que no afecte el estado de desplazamiento
del puntero del control si dicho estado se expresa con un desencadenador. Sin embargo, si este estado
se implementa utilizando una animacin, al cambiar a segundo plano, se podra interrumpir
irremediablemente la animacin y, por consiguiente, la transicin de estado.)

Los estilos del tema no tiene que ser coherentes con la semntica del "diseo" en todos los temas. Por
ejemplo, el estilo predeterminado no tiene que garantizar que un control vaya a tener el mismo
tamao en todos los temas ni que un control vaya a tener los mismos mrgenes o relleno de contenido
en todos los temas.

4.3.3. Adornos
En esta seccin se proporciona informacin sobre adornos y el marco de trabajo de adornos de Windows
Presentation Foundation (WPF).

4.3.3.1. Informacin General sobre Adornos


Las etiquetas contextuales son un tipo especial de FrameworkElement, que se utiliza para proporcionar
indicaciones visuales a un usuario. Entre otros usos se pueden utilizar para agregar controladores funcionales a
los elementos o proporcionar informacin de estado sobre un control.

MCT: Luis Dueas

Pag 31 de 473

Manual de Windows Presentation Foundation


Etiquetas contextuales
Un Adorner es un elemento de marco (FrameworkElement) personalizado que se enlaza a un elemento de
interfaz de usuario (UIElement). Las etiquetas contextuales se representan en una capa de AdornerLayer), que
es una superficie de representacin que siempre se encuentra encima del elemento con etiqueta o de una
coleccin de elementos con etiqueta. La representacin de una etiqueta contextual es independiente de la
representacin del UIElement al que est enlazado la etiqueta contextual. Una etiqueta contextual se suele
colocar con relacin al elemento al que se enlaza, utilizando el origen de coordenadas 2D estndar situado en la
parte superior izquierda del elemento adornado.
Algunas aplicaciones comunes de las etiquetas contextuales son:

Agregar controladores funcionales a un UIElement, que permiten al usuario manipular el elemento de


alguna manera (cambiar su tamao o posicin, girarlo, etc.).

Proporcionar comentarios visuales para indicar diversos estados o en respuesta a distintos eventos.
Superponer etiquetas contextuales visuales en un UIElement.
Invalidar o enmascarar visualmente la totalidad o parte de un UIElement.

Windows Presentation Foundation (WPF) proporciona un marco de trabajo bsico para etiquetar elementos
visuales. En la tabla siguiente se muestra una lista de los tipos principales utilizados al etiquetar objetos y su
finalidad. A continuacin, se presentan varios ejemplos de uso.
Adorner

Una clase base abstracta de la que heredan todas las implementaciones de etiquetas
contextuales concretas.

AdornerLayer

Una clase que representa una capa de representacin para la o las etiquetas
contextuales de uno o ms elementos.

AdornerDecorator

Una clase que permite asociar una capa de etiquetas contextuales a una coleccin de
elementos.

Implementacin de una etiqueta contextual personalizada


El marco de trabajo de etiquetas contextuales proporcionado por Windows Presentation Foundation (WPF) est
diseado principalmente para admitir la creacin de etiquetas contextuales personalizadas. Una etiqueta
contextual personalizada se crea implementando una clase que hereda de la clase Adorner abstracta.
Nota:
El elemento primario de un objeto Adorner es AdornerLayer, que representa el Adorner, no el elemento
etiquetado.
En el ejemplo siguiente se muestra una clase que implementa una etiqueta contextual simple. La etiqueta
contextual del ejemplo se limita a etiquetar las esquinas de un UIElement con crculos.
Public Class SimpleCircleAdorner
Inherits Adorner
Sub New(ByVal adornedElement As UIElement)
MyBase.New(adornedElement)
End Sub
Protected Overrides Sub OnRender(ByVal drawingContext As
System.Windows.Media.DrawingContext)
MyBase.OnRender(drawingContext)
Dim adornedElementRect As New Rect(AdornedElement.DesiredSize)
Dim renderBrush As New SolidColorBrush(Colors.Green)
renderBrush.Opacity = 0.2
Dim renderPen As New Pen(New SolidColorBrush(Colors.Navy), 1.5)
Dim renderRadius As Double
renderRadius = 5.0
'Draw a circle at each corner.
drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopLeft,
renderRadius, renderRadius)
drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopRight,
renderRadius, renderRadius)
drawingContext.DrawEllipse(renderBrush, renderPen,
adornedElementRect.BottomLeft, renderRadius, renderRadius)
drawingContext.DrawEllipse(renderBrush, renderPen,
adornedElementRect.BottomRight, renderRadius, renderRadius)
End Sub

MCT: Luis Dueas

Pag 32 de 473

Manual de Windows Presentation Foundation


End Class
En la ilustracin siguiente se muestra la etiqueta contextual SimpleCircleAdorner aplicada a un control TextBox.

Comportamiento de representacin de las etiquetas contextuales


Es importante tener en cuenta que las etiquetas contextuales no incluyen ningn comportamiento de
representacin inherente; asegurarse de que una etiqueta contextual se representa es responsabilidad del
implementador de la etiqueta contextual. Una manera comn de implementar el comportamiento de
representacin es invalidar el mtodo OnRenderSizeChanged y utilizar uno o ms objetos DrawingContext para
representar los elementos visuales de la etiqueta contextual, segn sea necesario (como se muestra en el
ejemplo anterior).
Nota:
Todo aquello que se coloca en la capa de etiquetas contextuales se representa encima del resto de estilos
que se han establecido. En otras palabras, las etiquetas contextuales siempre estn visualmente encima y
no pueden invalidar utilizando el orden z.
Eventos y pruebas de posicionamiento
Las etiquetas contextuales reciben los eventos de entrada exactamente igual que cualquier otro elemento
FrameworkElement. Dado que una etiqueta contextual siempre tiene un orden z ms alto que el elemento que
adorna, la etiqueta contextual recibe los eventos de entrada (como Drop o MouseMove) que podran estar
dirigidos al elemento adornado subyacente. Una etiqueta contextual puede realizar escuchas para ciertos
eventos de entrada y pasrselos al elemento etiquetado subyacente volviendo a provocar el evento.
Para habilitar las pruebas de posicionamiento indirectas de los elementos que se encuentran debajo de una
etiqueta contextual, establezca la propiedad IsHitTestVisible de la etiqueta contextual en false.
Agregar una etiqueta contextual a un solo elemento de interfaz de usuario
Para enlazar una etiqueta contextual a un elemento UIElement determinado, siga estos pasos:
1.

Llame al mtodo esttico GetAdornerLayer para obtener un objeto AdornerLayer del elemento
UIElement que se va a etiquetar. GetAdornerLayer recorre el rbol visual en sentido ascendente,
empezando por el elemento UIElement especificado, y devuelve la primera capa de etiquetas
contextuales que encuentra. (Si no se encuentra ninguna capa, el mtodo devuelve null.)

2.

Llame al mtodo Add para enlazar la etiqueta contextual al UIElement de destino.

En el ejemplo siguiente se enlaza la etiqueta contextual SimpleCircleAdorner (mostrada anteriormente) a un


control TextBox denominado myTextBox.
myAdornerLayer = AdornerLayer.GetAdornerLayer(myTextBox)
myAdornerLayer.Add(New SimpleCircleAdorner(myTextBox))
Nota:
En la actualidad, no se admite el uso de Lenguaje de marcado de aplicaciones extensible (XAML) para
enlazar una etiqueta contextual a otro elemento.
Agregar etiquetas contextuales a elementos secundarios de un panel
Para enlazar una etiqueta contextual a los elementos secundarios de un control Panel, siga estos pasos:
1.

Llame al mtodo static GetAdornerLayer para buscar una capa de etiquetas contextuales del elemento
cuyos elementos secundarios desea etiquetar.

2.

Enumere los elementos secundarios del elemento primario y llame al mtodo Add para enlazar una
etiqueta contextual a cada elemento secundario.

MCT: Luis Dueas

Pag 33 de 473

Manual de Windows Presentation Foundation


En el ejemplo siguiente se enlaza la etiqueta contextual SimpleCircleAdorner (mostrada anteriormente) a los
elementos secundarios de un control StackPanel denominado myStackPanel.
For Each toAdorn As UIElement In myStackPanel.Children
myAdornerLayer.Add(New SimpleCircleAdorner(toAdorn))
Next

4.3.3.2. Temas Cmo de Adornos


En los ejemplos siguientes se muestra cmo realizar tareas comunes mediante el marco de trabajo de adornos
de Windows Presentation Foundation (WPF).

4.3.3.2.1. Cmo: Implementar un Adorno


En este ejemplo se muestra una implementacin del adorno mnima.
Notas para los implementadores
Es importante tener en cuenta que los adornos no incluyen ningn comportamiento de representacin
inherente; asegurarse de que un adorno se representa es responsabilidad del implementador del adorno. Una
manera

comn

de

implementar

el

comportamiento

de

representacin

es

invalidar

el

mtodo

OnRenderSizeChanged y utilizar uno o ms objetos DrawingContext para representar los elementos visuales del
adorno, segn sea necesario (como se muestra en este ejemplo).
Ejemplo
Un adorno personalizado se crea implementando una clase que hereda de la clase Adorner abstracta. El adorno
del ejemplo simplemente adorna las esquinas de un elemento UIElement con crculos invalidando el mtodo
OnRender.
Public Class SimpleCircleAdorner
Inherits Adorner
Sub New(ByVal adornedElement As UIElement)
MyBase.New(adornedElement)
End Sub
Protected Overrides Sub OnRender(ByVal drawingContext As
System.Windows.Media.DrawingContext)
MyBase.OnRender(drawingContext)
Dim adornedElementRect As New Rect(AdornedElement.DesiredSize)
Dim renderBrush As New SolidColorBrush(Colors.Green)
renderBrush.Opacity = 0.2
Dim renderPen As New Pen(New SolidColorBrush(Colors.Navy), 1.5)
Dim renderRadius As Double
renderRadius = 5.0
'Draw a circle at each corner.
drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopLeft,
renderRadius, renderRadius)
drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopRight,
renderRadius, renderRadius)
drawingContext.DrawEllipse(renderBrush, renderPen,
adornedElementRect.BottomLeft, renderRadius, renderRadius)
drawingContext.DrawEllipse(renderBrush, renderPen,
adornedElementRect.BottomRight, renderRadius, renderRadius)
End Sub
End Class

4.3.3.2.2 Cmo: Enlazar un Adorno a un Elemento


En este ejemplo se muestra cmo enlazar mediante programacin un adorno a un elemento UIElement
especificado.
Ejemplo
Para enlazar un adorno a un elemento UIElement determinado, siga estos pasos:
1.

Llame al mtodo GetAdornerLayer de tipo static para obtener un objeto AdornerLayer del elemento
UIElement que se va a etiquetar. El mtodo GetAdornerLayer recorre el rbol visual en sentido
ascendente, empezando por el elemento UIElement especificado, y devuelve la primera capa de adornos
que encuentra. (Si no se encuentra ninguna capa, el mtodo devuelve null.)

2.

Llame al mtodo Add para enlazar el adorno al elemento UIElement de destino.

MCT: Luis Dueas

Pag 34 de 473

Manual de Windows Presentation Foundation


En el ejemplo siguiente se enlaza el adorno SimpleCircleAdorner (mostrado anteriormente) a un control
TextBox denominado myTextBox.
myAdornerLayer = AdornerLayer.GetAdornerLayer(myTextBox)
myAdornerLayer.Add(New SimpleCircleAdorner(myTextBox))
Nota:
En la actualidad, no se admite el uso de Lenguaje de marcado de aplicaciones extensible (XAML) para
enlazar un adorno a otro elemento.

4.3.3.2.3. Cmo: Incluir Adornos en los Elementos Secundarios de un Panel


En este ejemplo se muestra cmo enlazar mediante programacin un adorno a los elementos secundarios del
control Panel especificado.
Ejemplo
Para enlazar un adorno contextual a los elementos secundarios de un control Panel, siga estos pasos:
1.

Declare un nuevo objeto AdornerLayer y llame al mtodo static GetAdornerLayer para encontrar una
capa de adornos para el elemento cuyos elementos secundarios se van a etiquetar.

2.

Enumere los elementos secundarios del elemento primario y llame al mtodo Add para enlazar un
adorno a cada elemento secundario.

En el ejemplo siguiente se enlaza el adorno SimpleCircleAdorner (mostrada anteriormente) a los elementos


secundarios de un control StackPanel denominado myStackPanel.
For Each toAdorn As UIElement In myStackPanel.Children
myAdornerLayer.Add(New SimpleCircleAdorner(toAdorn))
Next
Nota:
En la actualidad, no se admite el uso de Lenguaje de marcado de aplicaciones extensible (XAML) para
enlazar un adorno a otro elemento.

4.3.3.2.4. Cmo: Quitar un Adorno de un Elemento


En este ejemplo se muestra cmo quitar mediante programacin un adorno concreto del elemento UIElement
especificado.
Ejemplo
En este ejemplo de cdigo detallado se quita el primer adorno en la matriz de adornos devuelta por
GetAdorners. Este recupera los adornos de un elemento UIElement denominado myTextBox. Si el elemento
especificado en la llamada a GetAdorners no tiene ningn adorno, se devuelve null. En este cdigo se
comprueba explcitamente si la matriz es null y resulta especialmente adecuado para aplicaciones en que cabe
esperar que una matriz sea null con relativa frecuencia.
Adorner[] toRemoveArray = myAdornerLayer.GetAdorners(myTextBox);
Adorner toRemove;
if (toRemoveArray != null)
{
toRemove = toRemoveArray[0];
myAdornerLayer.Remove(toRemove);
}
Este

ejemplo

de

cdigo

comprimido

es

funcionalmente

equivalente

al

ejemplo

detallado

mostrado

anteriormente. En este cdigo no se comprueba explcitamente si la matriz es null, por lo que es posible que se
inicie una excepcin NullReferenceException. Este cdigo es ms apropiado para aplicaciones en que no sea
frecuente que una matriz sea null.
try { myAdornerLayer.Remove((myAdornerLayer.GetAdorners(myTextBox))[0]); } catch { }

4.3.3.2.5. Cmo: Quitar Todos los Adornos de un Elemento

MCT: Luis Dueas

Pag 35 de 473

Manual de Windows Presentation Foundation


En este ejemplo se muestra cmo quitar mediante programacin todos los adornos de un elemento UIElement
especificado.
Ejemplo
En este ejemplo de cdigo detallado se quitan todos los adornos en la matriz de adornos devuelta por
GetAdorners. Este recupera los adornos de un elemento UIElement denominado myTextBox. Si el elemento
especificado en la llamada a GetAdorners no tiene ningn adorno, se devuelve null. En este cdigo se
comprueba explcitamente si la matriz es null y resulta especialmente adecuado para aplicaciones en que cabe
esperar que una matriz sea null con relativa frecuencia.
Adorner[] toRemoveArray = myAdornerLayer.GetAdorners(myTextBox);
if (toRemoveArray != null)
{
for (int x = 0; x < toRemoveArray.Length; x++)
{
myAdornerLayer.Remove(toRemoveArray[x]);
}
}
Este

ejemplo

de

cdigo

comprimido

es

funcionalmente

equivalente

al

ejemplo

detallado

mostrado

anteriormente. En este cdigo no se comprueba explcitamente si la matriz es null, por lo que es posible que se
inicie una excepcin NullReferenceException. Este cdigo es ms apropiado para aplicaciones en que no sea
frecuente que una matriz sea null.
try { foreach (Adorner toRemove in myAdornerLayer.GetAdorners(myTextBox))
myAdornerLayer.Remove(toRemove); } catch { }

4.3.4. Ejemplos de ControlTemplate


4.3.4.1. Ejemplo de ControlTemplate de Button
Los controles de Windows Presentation Foundation (WPF) tienen un objeto ControlTemplate que contiene el
rbol visual de ese control. Puede modificar el aspecto de la estructura y la apariencia de un control
modificando el objeto ControlTemplate de ese control. No existe ningn modo de reemplazar nicamente una
parte del rbol visual de un control; para modificar el rbol visual de un control, debe establecer la propiedad
Template del control en su nuevo y completo objeto ControlTemplate.
En este tema se muestra el objeto ControlTemplate del control Button de WPF.
Ejemplo de ControlTemplate de Button
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto Button
de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style TargetType="Button">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
<Setter Property="MinHeight" Value="23"/>
<Setter Property="MinWidth" Value="75"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
x:Name="Border"
CornerRadius="2"
BorderThickness="1"
Background="{StaticResource NormalBrush}"
BorderBrush="{StaticResource NormalBorderBrush}">
<ContentPresenter
Margin="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter TargetName="Border" Property="BorderBrush"
Value="{StaticResource DefaultedBorderBrush}" />
</Trigger>
<Trigger Property="IsDefaulted" Value="true">

MCT: Luis Dueas

Pag 36 de 473

Manual de Windows Presentation Foundation


<Setter TargetName="Border" Property="BorderBrush"
Value="{StaticResource DefaultedBorderBrush}" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource DarkBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource PressedBrush}" />
<Setter TargetName="Border" Property="BorderBrush"
Value="{StaticResource PressedBorderBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource DisabledBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush"
Value="{StaticResource DisabledBorderBrush}" />
<Setter Property="Foreground"
Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
El ejemplo anterior utiliza uno o ms de los siguientes recursos.
<Style x:Key="ButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle
Margin="2"
StrokeThickness="1"
Stroke="#60000000"
StrokeDashArray="1 2"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Fill Brushes -->
<LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>

MCT: Luis Dueas

Pag 37 de 473

Manual de Windows Presentation Foundation


<GradientStopCollection>
<GradientStop Color="#BBB" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="0.1"/>
<GradientStop Color="#EEE" Offset="0.9"/>
<GradientStop Color="#FFF" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<!-- Border Brushes -->
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0"
EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#777" Offset="0.0"/>
<GradientStop Color="#000" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#444" Offset="0.0"/>
<GradientStop Color="#888" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
<SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />
<!-- Miscellaneous Brushes -->
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />

4.3.4.2. Ejemplo de ControlTemplate de CheckBox


En este tema se muestra el objeto ControlTemplate del control CheckBox de WPF.
Ejemplo de ControlTemplate de CheckBox
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto
CheckBox de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Type CheckBox}" TargetType="CheckBox">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="FocusVisualStyle"
Value="{StaticResource CheckBoxFocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<BulletDecorator Background="Transparent">
<BulletDecorator.Bullet>
<Border x:Name="Border"
Width="13"
Height="13"
CornerRadius="0"
Background="{StaticResource NormalBrush}"
BorderThickness="1"
BorderBrush="{StaticResource NormalBorderBrush}">
<Path
Width="7" Height="7"
x:Name="CheckMark"
SnapsToDevicePixels="False"
Stroke="{StaticResource GlyphBrush}"

MCT: Luis Dueas

Pag 38 de 473

Manual de Windows Presentation Foundation


StrokeThickness="2"
Data="M 0 0 L 7 7 M 0 7 L 7 0" />
</Border>
</BulletDecorator.Bullet>
<ContentPresenter Margin="4,0,0,0"
VerticalAlignment="Center"
HorizontalAlignment="Left"
RecognizesAccessKey="True"/>
</BulletDecorator>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="false">
<Setter TargetName="CheckMark" Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsChecked" Value="{x:Null}">
<Setter TargetName="CheckMark" Property="Data" Value="M 0 7 L 7 0" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource DarkBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource PressedBrush}" />
<Setter TargetName="Border" Property="BorderBrush"
Value="{StaticResource PressedBorderBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource DisabledBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush"
Value="{StaticResource DisabledBorderBrush}" />
<Setter Property="Foreground"
Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
El ejemplo anterior utiliza uno o ms de los siguientes recursos.
<Style x:Key="CheckBoxFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle
Margin="15,0,0,0"
StrokeThickness="1"
Stroke="#60000000"
StrokeDashArray="1 2"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Fill Brushes -->
<LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>

MCT: Luis Dueas

Pag 39 de 473

Manual de Windows Presentation Foundation


</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#BBB" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="0.1"/>
<GradientStop Color="#EEE" Offset="0.9"/>
<GradientStop Color="#FFF" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<!-- Border Brushes -->
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0"
EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#777" Offset="0.0"/>
<GradientStop Color="#000" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#444" Offset="0.0"/>
<GradientStop Color="#888" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
<SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />
<!-- Miscellaneous Brushes -->
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />

4.3.4.3. Ejemplo de ControlTemplate de ComboBox


En este tema se muestra el objeto ControlTemplate del control ComboBox de WPF.
Ejemplo de ControlTemplate de ComboBox
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto
ComboBox de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="ToggleButton">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Border
x:Name="Border"

MCT: Luis Dueas

Pag 40 de 473

Manual de Windows Presentation Foundation


Grid.ColumnSpan="2"
CornerRadius="2"
Background="{StaticResource NormalBrush}"
BorderBrush="{StaticResource NormalBorderBrush}"
BorderThickness="1" />
<Border
Grid.Column="0"
CornerRadius="2,0,0,2"
Margin="1"
Background="{StaticResource WindowBackgroundBrush}"
BorderBrush="{StaticResource NormalBorderBrush}"
BorderThickness="0,0,1,0" />
<Path
x:Name="Arrow"
Grid.Column="1"
Fill="{StaticResource GlyphBrush}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 0 L 4 4 L 8 0 Z"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource DarkBrush}" />
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="true">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource PressedBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource DisabledBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush"
Value="{StaticResource DisabledBorderBrush}" />
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
<Setter TargetName="Arrow" Property="Fill"
Value="{StaticResource DisabledForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="ComboBoxTextBox" TargetType="TextBox">
<Border x:Name="PART_ContentHost" Focusable="False"
Background="{TemplateBinding Background}" />
</ControlTemplate>
<Style x:Key="{x:Type ComboBox}" TargetType="ComboBox">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="MinWidth" Value="120"/>
<Setter Property="MinHeight" Value="20"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<ToggleButton
Name="ToggleButton"
Template="{StaticResource ComboBoxToggleButton}"
Grid.Column="2"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
</ToggleButton>
<ContentPresenter
Name="ContentSite"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Margin="3,3,23,3"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
<TextBox x:Name="PART_EditableTextBox"
Style="{x:Null}"
Template="{StaticResource ComboBoxTextBox}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3,3,23,3"
Focusable="True"
Background="Transparent"
Visibility="Hidden"
IsReadOnly="{TemplateBinding IsReadOnly}"/>
<Popup
Name="Popup"

MCT: Luis Dueas

Pag 41 de 473

Manual de Windows Presentation Foundation


Placement="Bottom"
IsOpen="{TemplateBinding IsDropDownOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Slide">
<Grid
Name="DropDown"
SnapsToDevicePixels="True"
MinWidth="{TemplateBinding ActualWidth}"
MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border
x:Name="DropDownBorder"
Background="{StaticResource WindowBackgroundBrush}"
BorderThickness="1"
BorderBrush="{StaticResource SolidBorderBrush}"/>
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True"
KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground"
Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true">
<Setter TargetName="DropDownBorder" Property="CornerRadius" Value="4"/>
<Setter TargetName="DropDownBorder" Property="Margin" Value="0,2,0,0"/>
</Trigger>
<Trigger Property="IsEditable"
Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter TargetName="PART_EditableTextBox" Property="Visibility"
Value="Visible"/>
<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
</Style.Triggers>
</Style>
En el ejemplo anterior se usan uno o ms de los siguientes recursos.
<!-- Fill Brushes -->
<LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>

MCT: Luis Dueas

Pag 42 de 473

Manual de Windows Presentation Foundation


<LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#BBB" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="0.1"/>
<GradientStop Color="#EEE" Offset="0.9"/>
<GradientStop Color="#FFF" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<!-- Border Brushes -->
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0"
EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#777" Offset="0.0"/>
<GradientStop Color="#000" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#444" Offset="0.0"/>
<GradientStop Color="#888" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
<SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />
<!-- Miscellaneous Brushes -->
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />

4.3.4.4. Ejemplo de ControlTemplate de ComboBoxItem


En este tema se muestra el objeto ControlTemplate del control StatusBar de WPF.
Ejemplo de ControlTemplate de ComboBoxItem
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto
ComboBoxItem de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Type ComboBoxItem}" TargetType="ComboBoxItem">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border
Name="Border"
Padding="2"
SnapsToDevicePixels="true">
<ContentPresenter />

MCT: Luis Dueas

Pag 43 de 473

Manual de Windows Presentation Foundation


</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource SelectedBackgroundBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground"
Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se utilizan los siguientes recursos.
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
...
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />

4.3.4.5. Ejemplo de ControlTemplate de ContextMenu


En este tema se muestra el objeto ControlTemplate del control ContextMenu de WPF.
Ejemplo de ControlTemplate de ContextMenu
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto
ContextMenu de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style TargetType="{x:Type ContextMenu}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Grid.IsSharedSizeScope" Value="true"/>
<Setter Property="HasDropShadow" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContextMenu}">
<Border
Name="Border"
Background="{StaticResource WindowBackgroundBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1" >
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="HasDropShadow" Value="true">
<Setter TargetName="Border" Property="Padding" Value="0,3,0,3"/>
<Setter TargetName="Border" Property="CornerRadius" Value="4"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se utilizan los siguientes recursos.
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
...
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />

4.3.4.6. Ejemplo de ControlTemplate para DocumentViewer


En este tema se muestra el objeto ControlTemplate del control DocumentViewer de WPF.
Ejemplo de ControlTemplate para DocumentViewer
Aunque este ejemplo contiene todos los elementos que se definen en la ControlTemplate de un objeto
DocumentViewer de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Type DocumentViewer}" TargetType="DocumentViewer">
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
<Setter Property="Background"
Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="ContextMenu"
Value="{DynamicResource {ComponentResourceKey
TypeInTargetAssembly={x:Type ui:PresentationUIStyleResources},
ResourceId=PUIDocumentViewerContextMenu}}"/>

MCT: Luis Dueas

Pag 44 de 473

Manual de Windows Presentation Foundation


<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DocumentViewer">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}" Focusable="False">
<Grid Background="{StaticResource LightBrush}"
KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ToolBar
ToolBarTray.IsLocked="True"
KeyboardNavigation.TabNavigation="Continue">
<Button Command="ApplicationCommands.Print"
CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Content="Print"/>
<Button Command="ApplicationCommands.Copy"
CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Content="Copy"/>
<Separator />
<Button Command="NavigationCommands.IncreaseZoom"
CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Content="Zoom In"/>
<Button Command="NavigationCommands.DecreaseZoom"
CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Content="Zoom Out"/>
<Separator />
<Button Command="NavigationCommands.Zoom"
CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
CommandParameter="100.0"
Content="Actual Size" />
<Button Command="DocumentViewer.FitToWidthCommand"
CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Content="Fit to Width" />
<Button Command="DocumentViewer.FitToMaxPagesAcrossCommand"
CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
CommandParameter="1"
Content="Whole Page"/>
<Button Command="DocumentViewer.FitToMaxPagesAcrossCommand"
CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
CommandParameter="2"
Content="Two Pages"/>
</ToolBar>
<ScrollViewer Grid.Row="1"
CanContentScroll="true"
HorizontalScrollBarVisibility="Auto"
x:Name="PART_ContentHost"
IsTabStop="true"/>
<ContentControl Grid.Row="2"
x:Name="PART_FindToolBarHost"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se usa el recurso siguiente:
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>

4.3.4.7. Ejemplo de ControlTemplate de Expander


En este tema se muestra el objeto ControlTemplate del control Expander de WPF.
Ejemplo de ControlTemplate de Expander
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto
Expander de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<ControlTemplate x:Key="ExpanderToggleButton" TargetType="ToggleButton">
<Border
Name="Border"
CornerRadius="2,0,0,0"
Background="Transparent"

MCT: Luis Dueas

Pag 45 de 473

Manual de Windows Presentation Foundation


BorderBrush="{StaticResource NormalBorderBrush}"
BorderThickness="0,0,1,0">
<Path
Name="Arrow"
Fill="{StaticResource GlyphBrush}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 0 L 4 4 L 8 0 Z"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource DarkBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource PressedBrush}" />
</Trigger>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="Arrow" Property="Data"
Value="M 0 4 L 4 0 L 8 4 Z" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource DisabledBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush"
Value="{StaticResource DisabledBorderBrush}" />
<Setter Property="Foreground"
Value="{StaticResource DisabledForegroundBrush}"/>
<Setter TargetName="Arrow" Property="Fill"
Value="{StaticResource DisabledForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style TargetType="Expander">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Expander">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Name="ContentRow" Height="0"/>
</Grid.RowDefinitions>
<Border
Name="Border"
Grid.Row="0"
Background="{StaticResource LightBrush}"
BorderBrush="{StaticResource NormalBorderBrush}"
BorderThickness="1"
CornerRadius="2,2,0,0" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ToggleButton
IsChecked="{Binding Path=IsExpanded,Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}"
OverridesDefaultStyle="True"
Template="{StaticResource ExpanderToggleButton}"
Background="{StaticResource NormalBrush}" />
<ContentPresenter
Grid.Column="1"
Margin="4"
ContentSource="Header"
RecognizesAccessKey="True" />
</Grid>
</Border>
<Border
Name="Content"
Grid.Row="1"
Background="{StaticResource WindowBackgroundBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1,0,1,1"
CornerRadius="0,0,2,2" >
<ContentPresenter Margin="4" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="ContentRow" Property="Height"
Value="{Binding ElementName=Content,Path=DesiredHeight}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource DisabledBackgroundBrush}" />

MCT: Luis Dueas

Pag 46 de 473

Manual de Windows Presentation Foundation


<Setter TargetName="Border" Property="BorderBrush"
Value="{StaticResource DisabledBorderBrush}" />
<Setter Property="Foreground"
Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
El ejemplo anterior utiliza uno o ms de los siguientes recursos.
<!-- Fill Brushes -->
<LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#BBB" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="0.1"/>
<GradientStop Color="#EEE" Offset="0.9"/>
<GradientStop Color="#FFF" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<!-- Border Brushes -->
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0"
EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">

MCT: Luis Dueas

Pag 47 de 473

Manual de Windows Presentation Foundation


<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#777" Offset="0.0"/>
<GradientStop Color="#000" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#444" Offset="0.0"/>
<GradientStop Color="#888" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
<SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />
<!-- Miscellaneous Brushes -->
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />

4.3.4.8. Ejemplo de ControlTemplate de Frame


En este tema se muestra el objeto ControlTemplate del control Frame de WPF.
Ejemplo de ControlTemplate de Frame
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto Frame
de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<!-- Back/Forward Button Style -->
<Style x:Key="FrameButtonStyle" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Command" Value="NavigationCommands.BrowseBack"/>
<Setter Property="Focusable" Value="false"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse
Name="Ellipse"
Fill="{StaticResource NormalBrush}"
Stroke="{StaticResource NormalBorderBrush}"
StrokeThickness="1"
Width="16"
Height="16"/>
<Path
x:Name="Arrow"
Margin="0,0,2,0"
Fill="{StaticResource GlyphBrush}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 4 0 L 0 4 L 4 8 Z"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Command"
Value="{x:Static NavigationCommands.BrowseForward}">
<Setter TargetName="Arrow" Property="Data"
Value="M 0 0 L 4 4 L 0 8 z"/>
<Setter TargetName="Arrow" Property="Margin" Value="2,0,0,0"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Ellipse" Property="Fill"
Value="{StaticResource DarkBrush}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Ellipse" Property="Fill"
Value="{StaticResource PressedBrush}" />
<Setter TargetName="Ellipse" Property="Stroke"
Value="{StaticResource PressedBorderBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Ellipse" Property="Fill"
Value="{StaticResource DisabledBackgroundBrush}"/>
<Setter TargetName="Arrow" Property="Fill"
Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

MCT: Luis Dueas

Pag 48 de 473

Manual de Windows Presentation Foundation


<!-- Frame Menu Style -->
<Style x:Key="FrameMenu" TargetType="Menu">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="IsMainMenu" Value="false"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Menu">
<DockPanel IsItemsHost="true"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Frame Menu Header Style -->
<Style x:Key="FrameHeaderMenuItem" TargetType="MenuItem">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="MenuItem">
<Grid>
<Popup x:Name="PART_Popup"
Placement="Bottom"
VerticalOffset="2"
IsOpen="{TemplateBinding IsSubmenuOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Fade">
<Border Name="SubMenuBorder"
Background="{StaticResource WindowBackgroundBrush}"
BorderThickness="1"
BorderBrush="{StaticResource SolidBorderBrush}">
<StackPanel IsItemsHost="true"
Margin="2"
KeyboardNavigation.TabNavigation="Cycle"
KeyboardNavigation.DirectionalNavigation="Cycle"/>
</Border>
</Popup>
<Grid x:Name="Panel"
Width="24"
Background="Transparent"
HorizontalAlignment="Right" >
<Border Visibility="Hidden"
Name="HighlightBorder"
BorderThickness="1"
BorderBrush="{StaticResource NormalBorderBrush}"
Background="{StaticResource NormalBrush}"
CornerRadius="2" />
<Path x:Name="Arrow"
SnapsToDevicePixels="false"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Margin="0,2,4,0"
Fill="{StaticResource GlyphBrush}"
StrokeLineJoin="Round"
Data="M 0 0 L 4 4 L 8 0 Z"/>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="HighlightBorder"
Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="IsSubmenuOpen" Value="true">
<Setter TargetName="HighlightBorder"
Property="Background" Value="{StaticResource PressedBrush}" />
<Setter TargetName="HighlightBorder"
Property="BorderBrush" Value="{StaticResource PressedBorderBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Arrow"
Property="Fill" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Frame Menu Item Style -->
<Style x:Key="FrameSubmenuItem" TargetType="MenuItem">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Header" Value="{Binding Path=(JournalEntry.Name)}"/>
<Setter Property="Command" Value="NavigationCommands.NavigateJournal"/>
<Setter Property="CommandTarget" Value="{Binding RelativeSource={RelativeSource
AncestorType={x:Type Menu}}, Path=TemplatedParent}"/>
<Setter Property="CommandParameter"
Value="{Binding RelativeSource={RelativeSource Self}}"/>

MCT: Luis Dueas

Pag 49 de 473

Manual de Windows Presentation Foundation


<Setter Property="JournalEntryUnifiedViewConverter.JournalEntryPosition"
Value="{Binding (JournalEntryUnifiedViewConverter.JournalEntryPosition)}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="MenuItem">
<Grid Name="Panel"
Background="Transparent"
SnapsToDevicePixels="true">
<Path Name="Glyph"
SnapsToDevicePixels="false"
Margin="7,5"
Width="10"
Height="10"
HorizontalAlignment="Left"
StrokeStartLineCap="Triangle"
StrokeEndLineCap="Triangle"
StrokeThickness="2"
Stroke="{StaticResource GlyphBrush}" />
<ContentPresenter ContentSource="Header"
Margin="24,5,50,5"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="JournalEntryUnifiedViewConverter.JournalEntryPosition"
Value="Current">
<Setter TargetName="Glyph"
Property="Data" Value="M 0,5 L 2.5,8 L 7,3 "/>
</Trigger>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Panel" Property="Background"
Value="{StaticResource SelectedBackgroundBrush}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="MenuItem.IsHighlighted" Value="true"/>
<Condition Property="JournalEntryUnifiedViewConverter.JournalEntryPosition"
Value="Forward"/>
</MultiTrigger.Conditions>
<Setter TargetName="Glyph" Property="Data" Value="M 3 1 L 7 5 L 3 9 z"/>
<Setter TargetName="Glyph" Property="Fill" Value="{StaticResource GlyphBrush}"/>
<Setter TargetName="Glyph" Property="Stroke" Value="{x:Null}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="MenuItem.IsHighlighted" Value="true"/>
<Condition Property="JournalEntryUnifiedViewConverter.JournalEntryPosition"
Value="Back"/>
</MultiTrigger.Conditions>
<Setter TargetName="Glyph" Property="Data" Value="M 7 1 L 3 5 L 7 9 z"/>
<Setter TargetName="Glyph" Property="Fill" Value="{StaticResource GlyphBrush}"/>
<Setter TargetName="Glyph" Property="Stroke" Value="{x:Null}"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Merges Back and Forward Navigation Stacks -->
<JournalEntryUnifiedViewConverter x:Key="JournalEntryUnifiedViewConverter"/>
<!-- SimpleStyles: Frame -->
<Style x:Key="{x:Type Frame}" TargetType="Frame">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Frame">
<DockPanel>
<Grid Background="{StaticResource LightBrush}"
DockPanel.Dock="Top"
Height="22">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="16"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Menu Name="NavMenu"
Grid.ColumnSpan="3"
Height="16"
Margin="1,0,0,0"
VerticalAlignment="Center"
Style="{StaticResource FrameMenu}">
<MenuItem Style="{StaticResource FrameHeaderMenuItem}"
ItemContainerStyle="{StaticResource FrameSubmenuItem}"
IsSubmenuOpen="{Binding Path=(MenuItem.IsSubmenuOpen),Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}">
<MenuItem.ItemsSource>
<MultiBinding Converter="{StaticResource JournalEntryUnifiedViewConverter}">

MCT: Luis Dueas

Pag 50 de 473

Manual de Windows Presentation Foundation


<MultiBinding.Bindings>
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="BackStack" />
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="ForwardStack" />
</MultiBinding.Bindings>
</MultiBinding>
</MenuItem.ItemsSource>
</MenuItem>
</Menu>
<Path Grid.Column="0"
SnapsToDevicePixels="false"
IsHitTestVisible="false"
Margin="2,0,0,0"
Grid.ColumnSpan="3"
StrokeThickness="1"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Data="M15,14 Q18,12.9 20.9,14 A8.3,8.3,0,0,0,35.7,8.7 A8.3,8.3,0,0,0,25.2,
0.6 Q18,3.3 10.8,0.6 A8.3,8.3,0,0,0,0.3,8.7 A8.3,8.3,0,0,0,15,14 z"
Fill="{StaticResource PressedBrush}"
Stroke="{StaticResource LightBorderBrush}"/>
<Button Style="{StaticResource FrameButtonStyle}"
Command="NavigationCommands.BrowseBack"
Content="M 4 0 L 0 4 L 4 8 Z"
Margin="2.7,0,1.3,0"
Grid.Column="0"/>
<Button Style="{StaticResource FrameButtonStyle}"
Command="NavigationCommands.BrowseForward"
Content="M 4 0 L 0 4 L 4 8 Z"
Margin="1.3,0,0,0"
Grid.Column="1"/>
</Grid>
<ContentPresenter x:Name="PART_FrameCP"/>
</DockPanel>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="CanGoForward" Value="false"/>
<Condition Property="CanGoBack" Value="false"/>
</MultiTrigger.Conditions>
<Setter TargetName="NavMenu" Property="IsEnabled" Value="false"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se usan uno o ms de los siguientes recursos.
<!-- Fill Brushes -->
<LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>

MCT: Luis Dueas

Pag 51 de 473

Manual de Windows Presentation Foundation


<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#BBB" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="0.1"/>
<GradientStop Color="#EEE" Offset="0.9"/>
<GradientStop Color="#FFF" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<!-- Border Brushes -->
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0"
EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#777" Offset="0.0"/>
<GradientStop Color="#000" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#444" Offset="0.0"/>
<GradientStop Color="#888" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
<SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />
<!-- Miscellaneous Brushes -->
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />

4.3.4.9. Ejemplo de ControlTemplate de GroupBox


En este tema se muestra el objeto ControlTemplate del control GroupBox de WPF.
Ejemplo de ControlTemplate de GroupBox
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto
GroupBox de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style TargetType="GroupBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupBox">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border
Grid.Row="0"
Background="{StaticResource LightBrush}"
BorderBrush="{StaticResource NormalBorderBrush}"
BorderThickness="1"

MCT: Luis Dueas

Pag 52 de 473

Manual de Windows Presentation Foundation


CornerRadius="2,2,0,0" >
<ContentPresenter
Margin="4"
ContentSource="Header"
RecognizesAccessKey="True" />
</Border>
<Border
Grid.Row="1"
Background="{StaticResource WindowBackgroundBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1,0,1,1"
CornerRadius="0,0,2,2" >
<ContentPresenter
Margin="4" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se utilizan los siguientes recursos.
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
...
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
...
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
...
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />

4.3.4.10. Ejemplo de ControlTemplate de Label


En este tema se muestra el objeto ControlTemplate del control Label de WPF.
Ejemplo de ControlTemplate de Label
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto Label
de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Type Label}" TargetType="Label">
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<Border>
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground"
Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
El ejemplo anterior utiliza el recurso siguiente.
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />

4.3.4.11. Ejemplo de ControlTemplate de ListBox

MCT: Luis Dueas

Pag 53 de 473

Manual de Windows Presentation Foundation


En este tema se muestra el objeto ControlTemplate del control ListBox de WPF.
Ejemplo de ControlTemplate de ListBox
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto ListBox
de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Type ListBox}" TargetType="ListBox">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="MinWidth" Value="120"/>
<Setter Property="MinHeight" Value="95"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<Border
Name="Border"
Background="{StaticResource WindowBackgroundBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1"
CornerRadius="2">
<ScrollViewer
Margin="0"
Focusable="false">
<StackPanel Margin="2" IsItemsHost="True" />
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource DisabledBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush"
Value="{StaticResource DisabledBorderBrush}" />
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se utilizan los siguientes recursos.
<SolidColorBrush
...
<SolidColorBrush
...
<SolidColorBrush
...
<SolidColorBrush

x:Key="WindowBackgroundBrush" Color="#FFF" />


x:Key="SolidBorderBrush" Color="#888" />
x:Key="DisabledBackgroundBrush" Color="#EEE" />
x:Key="DisabledBorderBrush" Color="#AAA" />

4.3.4.12. Ejemplo de ControlTemplate de ListBoxItem


En este tema se muestra el objeto ControlTemplate del control ListBoxItem de WPF.
Ejemplo de ControlTemplate de ListBoxItem
Aunque este ejemplo contiene todos los elementos que se definen en el objeto ControlTemplate de un objeto
ListBoxItem de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Type ListBoxItem}" TargetType="ListBoxItem">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border
Name="Border"
Padding="2"
SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource SelectedBackgroundBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">

MCT: Luis Dueas

Pag 54 de 473

Manual de Windows Presentation Foundation


<Setter Property="Foreground"
Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se usan los siguientes recursos.
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
...
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />

4.3.4.13. Ejemplo de ControlTemplate de ListView


En este tema se muestra el objeto ControlTemplate del control ListView de WPF.
Ejemplo de ControlTemplate de ListView
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto ListView
de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Static GridView.GridViewScrollViewerStyleKey}"
TargetType="ScrollViewer">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ScrollViewer">
<Grid Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DockPanel Margin="{TemplateBinding Padding}">
<ScrollViewer DockPanel.Dock="Top"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
Focusable="false">
<GridViewHeaderRowPresenter Margin="2,0,2,0"
Columns="{Binding Path=TemplatedParent.View.Columns,
RelativeSource={RelativeSource TemplatedParent}}"
ColumnHeaderContainerStyle="{Binding
Path=TemplatedParent.View.ColumnHeaderContainerStyle,
RelativeSource={RelativeSource TemplatedParent}}"
ColumnHeaderTemplate="{Binding
Path=TemplatedParent.View.ColumnHeaderTemplate,
RelativeSource={RelativeSource TemplatedParent}}"
ColumnHeaderTemplateSelector="{Binding
Path=TemplatedParent.View.ColumnHeaderTemplateSelector,
RelativeSource={RelativeSource TemplatedParent}}"
AllowsColumnReorder="{Binding
Path=TemplatedParent.View.AllowsColumnReorder,
RelativeSource={RelativeSource TemplatedParent}}"
ColumnHeaderContextMenu="{Binding
Path=TemplatedParent.View.ColumnHeaderContextMenu,
RelativeSource={RelativeSource TemplatedParent}}"
ColumnHeaderToolTip="{Binding
Path=TemplatedParent.View.ColumnHeaderToolTip,
RelativeSource={RelativeSource TemplatedParent}}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
<ScrollContentPresenter Name="PART_ScrollContentPresenter"
KeyboardNavigation.DirectionalNavigation="Local"
CanContentScroll="True" CanHorizontallyScroll="False"
CanVerticallyScroll="False"/>
</DockPanel>
<ScrollBar Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Grid.Row="1"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Value="{TemplateBinding HorizontalOffset}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
<ScrollBar Name="PART_VerticalScrollBar"
Grid.Column="1"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Value="{TemplateBinding VerticalOffset}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>

MCT: Luis Dueas

Pag 55 de 473

Manual de Windows Presentation Foundation


</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="GridViewColumnHeaderGripper" TargetType="Thumb">
<Setter Property="Width" Value="18"/>
<Setter Property="Background" Value="{StaticResource NormalBorderBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border Padding="{TemplateBinding Padding}"
Background="Transparent">
<Rectangle HorizontalAlignment="Center"
Width="1"
Fill="{TemplateBinding Background}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Type GridViewColumnHeader}"
TargetType="GridViewColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewColumnHeader">
<Grid>
<Border Name="HeaderBorder"
BorderThickness="0,1,0,1"
BorderBrush="{StaticResource NormalBorderBrush}"
Background="{StaticResource LightBrush}"
Padding="2,0,2,0">
<ContentPresenter Name="HeaderContent"
Margin="0,0,0,1"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<Thumb x:Name="PART_HeaderGripper"
HorizontalAlignment="Right"
Margin="0,0,-9,0"
Style="{StaticResource GridViewColumnHeaderGripper}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="HeaderBorder"
Property="Background" Value="{StaticResource NormalBrush}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="HeaderBorder"
Property="Background" Value="{StaticResource PressedBrush}"/>
<Setter TargetName="HeaderContent"
Property="Margin" Value="1,1,0,0"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Role" Value="Floating">
<Setter Property="Opacity" Value="0.7"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewColumnHeader">
<Canvas Name="PART_FloatingHeaderCanvas">
<Rectangle Fill="#60000000"
Width="{TemplateBinding ActualWidth}"
Height="{TemplateBinding ActualHeight}"/>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Role" Value="Padding">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewColumnHeader">

MCT: Luis Dueas

Pag 56 de 473

Manual de Windows Presentation Foundation


<Border Name="HeaderBorder"
BorderThickness="0,1,0,1"
BorderBrush="{StaticResource NormalBorderBrush}"
Background="{StaticResource LightBrush}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="{x:Type ListView}" TargetType="ListView">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListView">
<Border Name="Border"
BorderThickness="1"
BorderBrush="{StaticResource SolidBorderBrush}"
Background="{StaticResource WindowBackgroundBrush}">
<ScrollViewer Style="{DynamicResource
{x:Static GridView.GridViewScrollViewerStyleKey}}">
<ItemsPresenter />
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsGrouping"
Value="true">
<Setter Property="ScrollViewer.CanContentScroll"
Value="false"/>
</Trigger>
<Trigger Property="IsEnabled"
Value="false">
<Setter TargetName="Border"
Property="Background"
Value="{StaticResource DisabledBorderBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
El ejemplo anterior utiliza uno o ms de los siguientes recursos.
<!-- Fill Brushes -->
<LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>

MCT: Luis Dueas

Pag 57 de 473

Manual de Windows Presentation Foundation


</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#BBB" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="0.1"/>
<GradientStop Color="#EEE" Offset="0.9"/>
<GradientStop Color="#FFF" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<!-- Border Brushes -->
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0"
EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#777" Offset="0.0"/>
<GradientStop Color="#000" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#444" Offset="0.0"/>
<GradientStop Color="#888" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
<SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />
<!-- Miscellaneous Brushes -->
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />

4.3.4.14. Ejemplo de ControlTemplate de ListViewItem


En este tema se muestra el objeto ControlTemplate del control ListViewItem de WPF.
Ejemplo de ControlTemplate de ListViewItem
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto
ListViewItem de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Type ListViewItem}" TargetType="ListViewItem">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border
Name="Border"
Padding="2"
SnapsToDevicePixels="true"
Background="Transparent">
<GridViewRowPresenter
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">

MCT: Luis Dueas

Pag 58 de 473

Manual de Windows Presentation Foundation


<Setter TargetName="Border"
Property="Background" Value="{StaticResource SelectedBackgroundBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground"
Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se usan los siguientes recursos.
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
...
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />

4.3.4.15. Ejemplo de ControlTemplate de Menu


En este tema se muestra el objeto ControlTemplate del control Menu de WPF.
Ejemplo de ControlTemplate de Menu
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto Menu
de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Type Menu}" TargetType="{x:Type Menu}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Menu}">
<Border
Background="{StaticResource LightBrush}"
BorderBrush="{StaticResource NormalBorderBrush}"
BorderThickness="1">
<StackPanel ClipToBounds="True" Orientation="Horizontal" IsItemsHost="True"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se usan los siguientes recursos.
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
...
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>

4.3.4.16. Ejemplo de ControlTemplate para MenuItem


En este tema se muestra el objeto ControlTemplate del control MenuItem de WPF.
Ejemplo de ControlTemplate para MenuItem
Aunque en este ejemplo se incluyen todos los elementos definidos en la clase ControlTemplate de un control
MenuItem de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Static MenuItem.SeparatorStyleKey}" TargetType="{x:Type Separator}">
<Setter Property="Height" Value="1"/>
<Setter Property="Margin" Value="0,4,0,4"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Separator}">
<Border BorderBrush="{StaticResource SolidBorderBrush}" BorderThickness="1"/>
</ControlTemplate>

MCT: Luis Dueas

Pag 59 de 473

Manual de Windows Presentation Foundation


</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se usa uno o ms de los siguientes recursos.
<!-- Fill Brushes -->
<LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#BBB" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="0.1"/>
<GradientStop Color="#EEE" Offset="0.9"/>
<GradientStop Color="#FFF" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<!-- Border Brushes -->
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0"
EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#777" Offset="0.0"/>
<GradientStop Color="#000" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>

MCT: Luis Dueas

Pag 60 de 473

Manual de Windows Presentation Foundation


<LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#444" Offset="0.0"/>
<GradientStop Color="#888" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
<SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />
<!-- Miscellaneous Brushes -->
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />

4.3.4.17. Ejemplo de ControlTemplate de NavigationWindow


En este tema se muestra el objeto ControlTemplate del control NavigationWindow de WPF.
Ejemplo de ControlTemplate de NavigationWindow
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto
NavigationWindow de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="NavWinButtonStyle" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Command" Value="NavigationCommands.BrowseBack"/>
<Setter Property="Focusable" Value="false"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse
Name="Ellipse"
Fill="{StaticResource NormalBrush}"
Stroke="{StaticResource NormalBorderBrush}"
StrokeThickness="1"
Width="24"
Height="24"/>
<Path
x:Name="Arrow"
Margin="0,0,3,0"
Fill="{StaticResource GlyphBrush}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 6 0 L 0 6 L 6 12 Z"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Command"
Value="{x:Static NavigationCommands.BrowseForward}">
<Setter TargetName="Arrow"
Property="Data" Value="M 0 0 L 6 6 L 0 12 z"/>
<Setter TargetName="Arrow"
Property="Margin" Value="3,0,0,0"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Ellipse"
Property="Fill" Value="{StaticResource DarkBrush}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Ellipse"
Property="Fill" Value="{StaticResource PressedBrush}" />
<Setter TargetName="Ellipse"
Property="Stroke" Value="{StaticResource PressedBorderBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Ellipse"
Property="Fill" Value="{StaticResource DisabledBackgroundBrush}"/>
<Setter TargetName="Arrow"
Property="Fill" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- NavWin Menu Style -->
<Style x:Key="NavWinMenu" TargetType="Menu">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="IsMainMenu" Value="false"/>
<Setter Property="Template">
<Setter.Value>

MCT: Luis Dueas

Pag 61 de 473

Manual de Windows Presentation Foundation


<ControlTemplate TargetType="Menu">
<DockPanel IsItemsHost="true"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- NavWin Menu Header Style -->
<Style x:Key="NavWinHeaderMenuItem" TargetType="MenuItem">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="MenuItem">
<Grid>
<Popup x:Name="PART_Popup"
Placement="Bottom"
VerticalOffset="2"
IsOpen="{TemplateBinding IsSubmenuOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Fade">
<Border Name="SubMenuBorder"
Background="{StaticResource WindowBackgroundBrush}"
BorderThickness="1"
BorderBrush="{StaticResource SolidBorderBrush}">
<StackPanel IsItemsHost="true"
Margin="2"
KeyboardNavigation.TabNavigation="Cycle"
KeyboardNavigation.DirectionalNavigation="Cycle"/>
</Border>
</Popup>
<Grid x:Name="Panel"
Width="24"
Background="Transparent"
HorizontalAlignment="Right" >
<Border Visibility="Hidden"
Name="HighlightBorder"
BorderThickness="1"
BorderBrush="{StaticResource NormalBorderBrush}"
Background="{StaticResource NormalBrush}"
CornerRadius="2" />
<Path x:Name="Arrow"
SnapsToDevicePixels="false"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Margin="0,2,4,0"
Fill="{StaticResource GlyphBrush}"
StrokeLineJoin="Round"
Data="M 0 0 L 4 4 L 8 0 Z"/>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="HighlightBorder" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="IsSubmenuOpen" Value="true">
<Setter TargetName="HighlightBorder" Property="Background"
Value="{StaticResource PressedBrush}" />
<Setter TargetName="HighlightBorder" Property="BorderBrush"
Value="{StaticResource PressedBorderBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Arrow" Property="Fill" Value="{StaticResource
DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- NavWin Menu Item Style -->
<Style x:Key="NavWinSubmenuItem" TargetType="MenuItem">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Header" Value="{Binding Path=(JournalEntry.Name)}"/>
<Setter Property="Command" Value="NavigationCommands.NavigateJournal"/>
<Setter Property="CommandTarget"
Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type Menu}},
Path=TemplatedParent}"/>
<Setter Property="CommandParameter"
Value="{Binding RelativeSource={RelativeSource Self}}"/>
<Setter Property="JournalEntryUnifiedViewConverter.JournalEntryPosition"
Value="{Binding (JournalEntryUnifiedViewConverter.JournalEntryPosition)}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Grid Name="Panel"
Background="Transparent"

MCT: Luis Dueas

Pag 62 de 473

Manual de Windows Presentation Foundation


SnapsToDevicePixels="true">
<Path Name="Glyph"
SnapsToDevicePixels="false"
Margin="7,5"
Width="10"
Height="10"
HorizontalAlignment="Left"
StrokeStartLineCap="Triangle"
StrokeEndLineCap="Triangle"
StrokeThickness="2"
Stroke="{StaticResource GlyphBrush}" />
<ContentPresenter ContentSource="Header"
Margin="24,5,50,5"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Value="Current"
Property="JournalEntryUnifiedViewConverter.JournalEntryPosition" >
<Setter TargetName="Glyph" Property="Data"
Value="M 0,5 L 2.5,8 L 7,3 "/>
</Trigger>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Panel" Property="Background"
Value="{StaticResource SelectedBackgroundBrush}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="MenuItem.IsHighlighted"
Value="true"/>
<Condition Value="Forward"
Property="JournalEntryUnifiedViewConverter.JournalEntryPosition" />
</MultiTrigger.Conditions>
<Setter TargetName="Glyph" Property="Data"
Value="M 3 1 L 7 5 L 3 9 z"/>
<Setter TargetName="Glyph" Property="Fill"
Value="{StaticResource GlyphBrush}"/>
<Setter TargetName="Glyph" Property="Stroke"
Value="{x:Null}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="MenuItem.IsHighlighted"
Value="true"/>
<Condition Value="Back"
Property="JournalEntryUnifiedViewConverter.JournalEntryPosition"/>
</MultiTrigger.Conditions>
<Setter TargetName="Glyph" Property="Data" Value="M 7 1 L 3 5 L 7 9 z"/>
<Setter TargetName="Glyph" Property="Fill" Value="{StaticResource GlyphBrush}"/>
<Setter TargetName="Glyph" Property="Stroke" Value="{x:Null}"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Merges Back and Forward Navigation Stacks -->
<JournalEntryUnifiedViewConverter x:Key="JournalEntryUnifiedViewConverter"/>
<!-- SimpleStyles: NavigationWindow -->
<Style x:Key="{x:Type NavigationWindow}" TargetType="NavigationWindow">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="NavigationWindow">
<DockPanel Background="{StaticResource WindowBackgroundBrush}">
<Grid Background="{StaticResource LightBrush}"
DockPanel.Dock="Top"
Height="30">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="16"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Menu Name="NavMenu"
Grid.ColumnSpan="3"
Height="20"
Margin="1,0,0,0"
VerticalAlignment="Center"
Style="{StaticResource NavWinMenu}">
<MenuItem Style="{StaticResource NavWinHeaderMenuItem}"
ItemContainerStyle="{StaticResource NavWinSubmenuItem}"
IsSubmenuOpen="{Binding Path=(MenuItem.IsSubmenuOpen),
Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}">
<MenuItem.ItemsSource>
<MultiBinding Converter="{StaticResource JournalEntryUnifiedViewConverter}">
<MultiBinding.Bindings>
<Binding RelativeSource="{RelativeSource TemplatedParent}"

MCT: Luis Dueas

Pag 63 de 473

Manual de Windows Presentation Foundation


Path="BackStack" />
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="ForwardStack" />
</MultiBinding.Bindings>
</MultiBinding>
</MenuItem.ItemsSource>
</MenuItem>
</Menu>
<Path Grid.Column="0"
SnapsToDevicePixels="false"
IsHitTestVisible="false"
Margin="2,0,0,0"
Grid.ColumnSpan="3"
StrokeThickness="1"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Data="M22.5767,21.035 Q27,19.37 31.424,21.035 A12.5,12.5,0,0,0,53.5,13
A12.5,12.5,0,0,0,37.765,0.926 Q27,4.93 16.235,0.926 A12.5,12.5,0,0,0,0.5,13
A12.5,12.5,0,0,0,22.5767,21.035 z"
Fill="{StaticResource PressedBrush}"
Stroke="{StaticResource LightBorderBrush}"/>
<Button Style="{StaticResource NavWinButtonStyle}"
Command="NavigationCommands.BrowseBack"
Content="M 4 0 L 0 4 L 4 8 Z"
Margin="3,0,2,0"
Grid.Column="0"/>
<Button Style="{StaticResource NavWinButtonStyle}"
Command="NavigationCommands.BrowseForward"
Content="M 4 0 L 0 4 L 4 8 Z"
Margin="2,0,0,0"
Grid.Column="1"/>
</Grid>
<Grid>
<AdornerDecorator>
<ContentPresenter Name="PART_NavWinCP" ClipToBounds="true"/>
</AdornerDecorator>
<ResizeGrip x:Name="WindowResizeGrip"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Visibility="Collapsed"
IsTabStop="false"/>
</Grid>
</DockPanel>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="CanGoForward" Value="false"/>
<Condition Property="CanGoBack" Value="false"/>
</MultiTrigger.Conditions>
<Setter TargetName="NavMenu" Property="IsEnabled" Value="false"/>
</MultiTrigger>
<Trigger Property="ResizeMode" Value="CanResizeWithGrip">
<Setter TargetName="WindowResizeGrip" Property="Visibility"
Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se utilizan los siguientes recursos.
<Style x:Key="{x:Type ResizeGrip}" TargetType="{x:Type ResizeGrip}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ResizeGrip}">
<Border Background="Transparent"
SnapsToDevicePixels="True"
Width="16"
Height="16">
<Rectangle Margin="2">
<Rectangle.Fill>
<DrawingBrush Viewport="0,0,4,4"
ViewportUnits="Absolute"
Viewbox="0,0,8,8"
ViewboxUnits="Absolute"
TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#AAA"
Geometry="M 4 4 L 4 8 L 8 8 L 8 4 z"/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>

MCT: Luis Dueas

Pag 64 de 473

Manual de Windows Presentation Foundation


</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
...
<!-- Fill Brushes -->
<LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#BBB" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="0.1"/>
<GradientStop Color="#EEE" Offset="0.9"/>
<GradientStop Color="#FFF" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<!-- Border Brushes -->
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0"
EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#777" Offset="0.0"/>

MCT: Luis Dueas

Pag 65 de 473

Manual de Windows Presentation Foundation


<GradientStop Color="#000" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#444" Offset="0.0"/>
<GradientStop Color="#888" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
<SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />
<!-- Miscellaneous Brushes -->
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />

4.3.4.18. Ejemplo de ControlTemplate para ProgressBar


En este tema se muestra el objeto ControlTemplate del control ProgressBar de WPF.
Ejemplo de ControlTemplate para ProgressBar
Aunque este ejemplo contiene todos los elementos que se definen en la ControlTemplate de un objeto
ProgressBar de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Type ProgressBar}"
TargetType="{x:Type ProgressBar}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Grid MinHeight="14" MinWidth="200">
<Border
Name="PART_Track"
CornerRadius="2"
Background="{StaticResource PressedBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1" />
<Border
Name="PART_Indicator"
CornerRadius="2"
Background="{StaticResource DarkBrush}"
BorderBrush="{StaticResource NormalBorderBrush}"
BorderThickness="1"
HorizontalAlignment="Left" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se usan los siguientes recursos.
<LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#BBB" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="0.1"/>
<GradientStop Color="#EEE" Offset="0.9"/>
<GradientStop Color="#FFF" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
...
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
...
<LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
...
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>

MCT: Luis Dueas

Pag 66 de 473

Manual de Windows Presentation Foundation


</LinearGradientBrush>

4.3.4.19. Ejemplo de ControlTemplate de RadioButton


En este tema se muestra el objeto ControlTemplate del control RadioButton de WPF.
Ejemplo de ControlTemplate de RadioButton
Aunque este ejemplo contiene todos los elementos que se definen en la ControlTemplate de un objeto
RadioButton de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Type RadioButton}" TargetType="{x:Type RadioButton}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource RadioButtonFocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<BulletDecorator Background="Transparent">
<BulletDecorator.Bullet>
<Grid Width="13"
Height="13" >
<Ellipse x:Name="Border"
Fill="{StaticResource NormalBrush}"
StrokeThickness="1"
Stroke="{StaticResource NormalBorderBrush}" />
<Ellipse x:Name="CheckMark"
Margin="4"
Fill="{StaticResource GlyphBrush}" />
</Grid>
</BulletDecorator.Bullet>
<ContentPresenter
Margin="4,0,0,0"
VerticalAlignment="Center"
HorizontalAlignment="Left"
RecognizesAccessKey="True"/>
</BulletDecorator>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="false">
<Setter TargetName="CheckMark" Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Fill"
Value="{StaticResource DarkBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Fill"
Value="{StaticResource PressedBrush}" />
<Setter TargetName="Border" Property="Stroke"
Value="{StaticResource GlyphBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Border" Property="Fill"
Value="{StaticResource DisabledBackgroundBrush}" />
<Setter TargetName="Border" Property="Stroke" Value="#40000000" />
<Setter Property="Foreground" Value="#80000000"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se usan uno o ms de los siguientes recursos.
<Style x:Key="RadioButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle
Margin="15,0,0,0"
StrokeThickness="1"
Stroke="#60000000"
StrokeDashArray="1 2"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
...
<!-- Fill Brushes -->
<LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>

MCT: Luis Dueas

Pag 67 de 473

Manual de Windows Presentation Foundation


<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#BBB" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="0.1"/>
<GradientStop Color="#EEE" Offset="0.9"/>
<GradientStop Color="#FFF" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<!-- Border Brushes -->
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0"
EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#777" Offset="0.0"/>
<GradientStop Color="#000" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#444" Offset="0.0"/>
<GradientStop Color="#888" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>

MCT: Luis Dueas

Pag 68 de 473

Manual de Windows Presentation Foundation


<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
<SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />
<!-- Miscellaneous Brushes -->
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />

4.3.4.20. Ejemplo de ControlTemplate de ScrollBar


En este tema se muestra el objeto ControlTemplate del control ScrollBar de WPF.
Ejemplo de ControlTemplate de ScrollBar
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto
ScrollBar de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="ScrollBarLineButton" TargetType="{x:Type RepeatButton}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Focusable" Value="false"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Border
Name="Border"
Margin="1"
CornerRadius="2"
Background="{StaticResource NormalBrush}"
BorderBrush="{StaticResource NormalBorderBrush}"
BorderThickness="1">
<Path
HorizontalAlignment="Center"
VerticalAlignment="Center"
Fill="{StaticResource GlyphBrush}"
Data="{Binding Path=Content,RelativeSource={RelativeSource TemplatedParent}}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource
PressedBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource
DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ScrollBarPageButton" TargetType="{x:Type RepeatButton}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Focusable" Value="false"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Border Background="Transparent" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ScrollBarThumb" TargetType="{x:Type Thumb}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Focusable" Value="false"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border
CornerRadius="2"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="VerticalScrollBar" TargetType="{x:Type ScrollBar}">
<Grid >
<Grid.RowDefinitions>

MCT: Luis Dueas

Pag 69 de 473

Manual de Windows Presentation Foundation


<RowDefinition MaxHeight="18"/>
<RowDefinition Height="0.00001*"/>
<RowDefinition MaxHeight="18"/>
</Grid.RowDefinitions>
<Border
Grid.RowSpan="3"
CornerRadius="2"
Background="#F0F0F0" />
<RepeatButton
Grid.Row="0"
Style="{StaticResource ScrollBarLineButton}"
Height="18"
Command="ScrollBar.LineUpCommand"
Content="M 0 4 L 8 4 L 4 0 Z" />
<Track
Name="PART_Track"
Grid.Row="1"
IsDirectionReversed="true">
<Track.DecreaseRepeatButton>
<RepeatButton
Style="{StaticResource ScrollBarPageButton}"
Command="ScrollBar.PageUpCommand" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb
Style="{StaticResource ScrollBarThumb}"
Margin="1,0,1,0"
Background="{StaticResource HorizontalNormalBrush}"
BorderBrush="{StaticResource HorizontalNormalBorderBrush}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton
Style="{StaticResource ScrollBarPageButton}"
Command="ScrollBar.PageDownCommand" />
</Track.IncreaseRepeatButton>
</Track>
<RepeatButton
Grid.Row="3"
Style="{StaticResource ScrollBarLineButton}"
Height="18"
Command="ScrollBar.LineDownCommand"
Content="M 0 0 L 4 4 L 8 0 Z"/>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="HorizontalScrollBar" TargetType="{x:Type ScrollBar}">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="18"/>
<ColumnDefinition Width="0.00001*"/>
<ColumnDefinition MaxWidth="18"/>
</Grid.ColumnDefinitions>
<Border
Grid.ColumnSpan="3"
CornerRadius="2"
Background="#F0F0F0" />
<RepeatButton
Grid.Column="0"
Style="{StaticResource ScrollBarLineButton}"
Width="18"
Command="ScrollBar.LineLeftCommand"
Content="M 4 0 L 4 8 L 0 4 Z" />
<Track
Name="PART_Track"
Grid.Column="1"
IsDirectionReversed="False">
<Track.DecreaseRepeatButton>
<RepeatButton
Style="{StaticResource ScrollBarPageButton}"
Command="ScrollBar.PageLeftCommand" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb
Style="{StaticResource ScrollBarThumb}"
Margin="0,1,0,1"
Background="{StaticResource NormalBrush}"
BorderBrush="{StaticResource NormalBorderBrush}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton
Style="{StaticResource ScrollBarPageButton}"
Command="ScrollBar.PageRightCommand" />
</Track.IncreaseRepeatButton>
</Track>
<RepeatButton
Grid.Column="3"
Style="{StaticResource ScrollBarLineButton}"

MCT: Luis Dueas

Pag 70 de 473

Manual de Windows Presentation Foundation


Width="18"
Command="ScrollBar.LineRightCommand"
Content="M 0 0 L 4 4 L 0 8 Z"/>
</Grid>
</ControlTemplate>
<Style x:Key="{x:Type ScrollBar}" TargetType="{x:Type ScrollBar}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Style.Triggers>
<Trigger Property="Orientation" Value="Horizontal">
<Setter Property="Width" Value="Auto"/>
<Setter Property="Height" Value="18" />
<Setter Property="Template" Value="{StaticResource HorizontalScrollBar}" />
</Trigger>
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="Width" Value="18"/>
<Setter Property="Height" Value="Auto" />
<Setter Property="Template" Value="{StaticResource VerticalScrollBar}" />
</Trigger>
</Style.Triggers>
</Style>
En el ejemplo anterior se utiliza uno o ms de los siguientes recursos.
<!-- Fill Brushes -->
<LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#BBB" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="0.1"/>
<GradientStop Color="#EEE" Offset="0.9"/>
<GradientStop Color="#FFF" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<!-- Border Brushes -->
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>

MCT: Luis Dueas

Pag 71 de 473

Manual de Windows Presentation Foundation


</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0"
EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#777" Offset="0.0"/>
<GradientStop Color="#000" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#444" Offset="0.0"/>
<GradientStop Color="#888" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
<SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />
<!-- Miscellaneous Brushes -->
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />

4.3.4.21. Ejemplo de ControlTemplate de ScrollViewer


En este tema se muestra el objeto ControlTemplate del control ScrollViewer de WPF.
Ejemplo de ControlTemplate de ScrollViewer
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto
ScrollViewer de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="LeftScrollViewer" TargetType="{x:Type ScrollViewer}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollContentPresenter Grid.Column="1"/>
<ScrollBar Name="PART_VerticalScrollBar"
Value="{TemplateBinding VerticalOffset}"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
<ScrollBar Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Grid.Row="1"
Grid.Column="1"
Value="{TemplateBinding HorizontalOffset}"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

4.3.4.22. Ejemplo de ControlTemplate de Slider


En este tema se muestra el objeto ControlTemplate del control Slider de WPF.
Ejemplo de ControlTemplate de Slider

MCT: Luis Dueas

Pag 72 de 473

Manual de Windows Presentation Foundation


Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto Slider
de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="SliderButtonStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Focusable" Value="false"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Border Background="Transparent" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SliderThumbStyle" TargetType="{x:Type Thumb}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Height" Value="14"/>
<Setter Property="Width" Value="14"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Ellipse
Name="Ellipse"
Fill="{StaticResource NormalBrush}"
Stroke="{StaticResource NormalBorderBrush}"
StrokeThickness="1" />
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Ellipse" Property="Fill"
Value="{StaticResource DarkBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Ellipse" Property="Fill"
Value="{StaticResource DisabledBackgroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="HorizontalSlider" TargetType="{x:Type Slider}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" MinHeight="{TemplateBinding Slider.MinHeight}"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TickBar
Name="TopTick"
SnapsToDevicePixels="True"
Placement="Top"
Fill="{StaticResource GlyphBrush}"
Height="4"
Visibility="Collapsed" />
<Border
Name="TrackBackground"
Margin="0"
CornerRadius="2"
Height="4"
Grid.Row="1"
Background="{StaticResource LightBrush}"
BorderBrush="{StaticResource NormalBorderBrush}"
BorderThickness="1" />
<Track Grid.Row="1" Name="PART_Track">
<Track.DecreaseRepeatButton>
<RepeatButton
Style="{StaticResource SliderButtonStyle}"
Command="Slider.DecreaseLarge" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource SliderThumbStyle}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton
Style="{StaticResource SliderButtonStyle}"
Command="Slider.IncreaseLarge" />
</Track.IncreaseRepeatButton>
</Track>
<TickBar
Name="BottomTick"
SnapsToDevicePixels="True"
Grid.Row="2"
Fill="{TemplateBinding Foreground}"

MCT: Luis Dueas

Pag 73 de 473

Manual de Windows Presentation Foundation


Placement="Bottom"
Height="4"
Visibility="Collapsed" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="TickPlacement" Value="TopLeft">
<Setter TargetName="TopTick" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="TickPlacement" Value="BottomRight">
<Setter TargetName="BottomTick" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="TickPlacement" Value="Both">
<Setter TargetName="TopTick" Property="Visibility" Value="Visible"/>
<Setter TargetName="BottomTick" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="VerticalSlider" TargetType="{x:Type Slider}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" MinWidth="{TemplateBinding Slider.MinWidth}"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TickBar
Name="TopTick"
SnapsToDevicePixels="True"
Placement="Left"
Fill="{StaticResource GlyphBrush}"
Width="4"
Visibility="Collapsed" />
<Border
Name="TrackBackground"
Margin="0"
CornerRadius="2"
Width="4"
Grid.Column="1"
Background="{StaticResource HorizontalLightBrush}"
BorderBrush="{StaticResource HorizontalNormalBorderBrush}"
BorderThickness="1" />
<Track Grid.Column="1" Name="PART_Track">
<Track.DecreaseRepeatButton>
<RepeatButton
Style="{StaticResource SliderButtonStyle}"
Command="Slider.DecreaseLarge" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource SliderThumbStyle}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton
Style="{StaticResource SliderButtonStyle}"
Command="Slider.IncreaseLarge" />
</Track.IncreaseRepeatButton>
</Track>
<TickBar
Name="BottomTick"
SnapsToDevicePixels="True"
Grid.Column="2"
Fill="{TemplateBinding Foreground}"
Placement="Right"
Width="4"
Visibility="Collapsed" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="TickPlacement" Value="TopLeft">
<Setter TargetName="TopTick" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="TickPlacement" Value="BottomRight">
<Setter TargetName="BottomTick" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="TickPlacement" Value="Both">
<Setter TargetName="TopTick" Property="Visibility" Value="Visible"/>
<Setter TargetName="BottomTick" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style TargetType="{x:Type Slider}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Style.Triggers>
<Trigger Property="Orientation" Value="Horizontal">
<Setter Property="MinWidth" Value="104" />
<Setter Property="MinHeight" Value="21" />
<Setter Property="Template" Value="{StaticResource HorizontalSlider}" />
</Trigger>

MCT: Luis Dueas

Pag 74 de 473

Manual de Windows Presentation Foundation


<Trigger Property="Orientation" Value="Vertical">
<Setter Property="MinWidth" Value="21" />
<Setter Property="MinHeight" Value="104" />
<Setter Property="Template" Value="{StaticResource VerticalSlider}" />
</Trigger>
</Style.Triggers>
</Style>
El ejemplo anterior utiliza uno o ms de los siguientes recursos.
<!-- Fill Brushes -->
<LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#BBB" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="0.1"/>
<GradientStop Color="#EEE" Offset="0.9"/>
<GradientStop Color="#FFF" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<!-- Border Brushes -->
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0"
EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#777" Offset="0.0"/>

MCT: Luis Dueas

Pag 75 de 473

Manual de Windows Presentation Foundation


<GradientStop Color="#000" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#444" Offset="0.0"/>
<GradientStop Color="#888" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
<SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />
<!-- Miscellaneous Brushes -->
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />

4.3.4.23. Ejemplo de ControlTemplate de StatusBar


En este tema se muestra el objeto ControlTemplate del control StatusBar de WPF.
Ejemplo de ControlTemplate de StatusBar
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto
StatusBar de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Type StatusBar}" TargetType="{x:Type StatusBar}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type StatusBar}">
<Border Background="{StaticResource LightBrush}"
BorderBrush="{StaticResource NormalBorderBrush}"
Padding="1">
<ItemsPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Static StatusBar.SeparatorStyleKey}" TargetType="{x:Type Separator}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Separator}">
<Rectangle Fill="{StaticResource LightBorderBrush}"
Width="1"
Margin="3"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Type StatusBarItem}" TargetType="{x:Type StatusBarItem}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type StatusBarItem}">
<ContentPresenter Margin="3"/>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se usan los siguientes recursos.
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
...
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">

MCT: Luis Dueas

Pag 76 de 473

Manual de Windows Presentation Foundation


<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
...
<SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />
...
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />

4.3.4.24. Ejemplo de ControlTemplate de TabControl


En este tema se muestra el objeto ControlTemplate del control TabControl de WPF.
Ejemplo de ControlTemplate de TabControl
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto
TabControl de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style TargetType="{x:Type TabControl}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TabPanel
Name="HeaderPanel"
Grid.Row="0"
Panel.ZIndex="1"
Margin="0,0,4,-1"
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"
Background="Transparent" />
<Border
Name="Border"
Grid.Row="1"
Background="{StaticResource WindowBackgroundBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1"
CornerRadius="2"
KeyboardNavigation.TabNavigation="Local"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2" >
<ContentPresenter
Name="PART_SelectedContentHost"
Margin="4"
ContentSource="SelectedContent" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{StaticResource
DisabledForegroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource
DisabledBorderBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se utilizan los siguientes recursos.
<SolidColorBrush
...
<SolidColorBrush
...
<SolidColorBrush
...
<SolidColorBrush

x:Key="WindowBackgroundBrush" Color="#FFF" />


x:Key="SolidBorderBrush" Color="#888" />
x:Key="DisabledForegroundBrush" Color="#888" />
x:Key="DisabledBorderBrush" Color="#AAA" />

4.3.4.25. Ejemplo de ControlTemplate de TabItem


En este tema se muestra el objeto ControlTemplate del control TabItem de WPF.

MCT: Luis Dueas

Pag 77 de 473

Manual de Windows Presentation Foundation


Ejemplo de ControlTemplate de TabItem
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto
TabItem de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border
Name="Border"
Margin="0,0,-4,0"
Background="{StaticResource LightBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1,1,1,1"
CornerRadius="2,12,0,0" >
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="12,2,12,2"
RecognizesAccessKey="True"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Panel.ZIndex" Value="100" />
<Setter TargetName="Border" Property="Background" Value="{StaticResource
WindowBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="{StaticResource
DisabledBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource
DisabledBorderBrush}" />
<Setter Property="Foreground" Value="{StaticResource
DisabledForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se usan los siguientes recursos.
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
...
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
...
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
...
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
...
<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
...
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />

4.3.4.26. Ejemplo de ControlTemplate de TextBox


En este tema se muestra el objeto ControlTemplate del control TextBox de WPF.
Nota importante:
El ControlTemplate de un TextBox debe contener exactamente un elemento etiquetado como elemento que
hospeda el contenido; este elemento se utilizar para representar el contenido de TextBox. Para etiquetar
un elemento como host de contenido, asgnele el nombre especial PART_ContentHost. El elemento que
hospeda el contenido debe ser un ScrollViewer o AdornerDecorator. Dicho elemento no puede hospedar
elementos secundarios.
Ejemplo de ControlTemplate de TextBox

MCT: Luis Dueas

Pag 78 de 473

Manual de Windows Presentation Foundation


Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto TextBox
de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Type TextBox}" TargetType="{x:Type TextBoxBase}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="MinWidth" Value="120"/>
<Setter Property="MinHeight" Value="20"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border
Name="Border"
CornerRadius="2"
Padding="2"
Background="{StaticResource WindowBackgroundBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1" >
<ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="{StaticResource
DisabledBackgroundBrush}"/>
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource
DisabledBackgroundBrush}"/>
<Setter Property="Foreground" Value="{StaticResource
DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- SimpleStyles: RichTextBox -->
<Style x:Key="{x:Type RichTextBox}" BasedOn="{StaticResource {x:Type TextBox}}"
TargetType="{x:Type RichTextBox}">
<Style.Resources>
<Style x:Key="{x:Type FlowDocument}" TargetType="{x:Type FlowDocument}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
</Style>
</Style.Resources>
</Style>
<!-- SimpleStyles: PasswordBox -->
<Style x:Key="{x:Type PasswordBox}" TargetType="{x:Type PasswordBox}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="FontFamily" Value="Verdana"/>
<Setter Property="PasswordChar" Value=""/>
<Setter Property="MinWidth" Value="120"/>
<Setter Property="MinHeight" Value="20"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type PasswordBox}">
<Border
Name="Border"
CornerRadius="2"
Padding="2"
Background="{StaticResource WindowBackgroundBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1" >
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="{StaticResource
DisabledBackgroundBrush}"/>
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource
DisabledBackgroundBrush}"/>
<Setter Property="Foreground" Value="{StaticResource
DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se usan uno o ms de los siguientes recursos.

MCT: Luis Dueas

Pag 79 de 473

Manual de Windows Presentation Foundation


<!-- Fill Brushes -->
<LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#BBB" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="0.1"/>
<GradientStop Color="#EEE" Offset="0.9"/>
<GradientStop Color="#FFF" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<!-- Border Brushes -->
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0"
EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#777" Offset="0.0"/>
<GradientStop Color="#000" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#444" Offset="0.0"/>
<GradientStop Color="#888" Offset="1.0"/>

MCT: Luis Dueas

Pag 80 de 473

Manual de Windows Presentation Foundation


</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
<SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />
<!-- Miscellaneous Brushes -->
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />

4.3.4.27. Ejemplo de ControlTemplate de ToolBar


En este tema se muestra el objeto ControlTemplate del control ToolBar de WPF.
Ejemplo de ControlTemplate de ToolBar
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto ToolBar
de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="ToolBarButtonBaseStyle" TargetType="{x:Type ButtonBase}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ButtonBase}">
<Border
x:Name="Border"
BorderThickness="1"
Background="Transparent"
BorderBrush="Transparent">
<ContentPresenter
Margin="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource
SelectedBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource
SolidBorderBrush}" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource
SelectedBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource
SolidBorderBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource
PressedBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource
PressedBorderBrush}" />
</Trigger>
<Trigger Property="CheckBox.IsChecked" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource
PressedBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource
PressedBorderBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Border" Property="Background" Value="{StaticResource
DisabledBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource
DisabledBorderBrush}" />
<Setter Property="Foreground" Value="{StaticResource
DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Static ToolBar.ButtonStyleKey}"
BasedOn="{StaticResource ToolBarButtonBaseStyle}"
TargetType="{x:Type Button}"/>
<Style x:Key="{x:Static ToolBar.ToggleButtonStyleKey}"
BasedOn="{StaticResource ToolBarButtonBaseStyle}"
TargetType="{x:Type ToggleButton}"/>
<Style x:Key="{x:Static ToolBar.CheckBoxStyleKey}"
BasedOn="{StaticResource ToolBarButtonBaseStyle}"
TargetType="{x:Type CheckBox}"/>

MCT: Luis Dueas

Pag 81 de 473

Manual de Windows Presentation Foundation


<Style x:Key="{x:Static ToolBar.RadioButtonStyleKey}"
BasedOn="{StaticResource ToolBarButtonBaseStyle}"
TargetType="{x:Type RadioButton}"/>
<Style x:Key="{x:Static ToolBar.TextBoxStyleKey}" TargetType="{x:Type TextBox}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border
Name="Border"
Padding="2"
Background="{StaticResource WindowBackgroundBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1" >
<ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="{StaticResource
DisabledBackgroundBrush}"/>
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource
DisabledBackgroundBrush}"/>
<Setter Property="Foreground" Value="{StaticResource
DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ToolBarThumbStyle" TargetType="{x:Type Thumb}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Cursor" Value="SizeAll"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border Background="Transparent"
SnapsToDevicePixels="True">
<Rectangle Margin="0,2">
<Rectangle.Fill>
<DrawingBrush Viewport="0,0,4,4"
ViewportUnits="Absolute"
Viewbox="0,0,8,8"
ViewboxUnits="Absolute"
TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#AAA"
Geometry="M 4 4 L 4 8 L 8 8 L 8 4 z"/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ToolBarOverflowButtonStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Name="Bd"
CornerRadius="0,3,3,0"
Background="Transparent"
SnapsToDevicePixels="true">
<Grid>
<Path Name="Arrow"
Fill="Black"
VerticalAlignment="Bottom"
Margin="2,3"
Data="M -0.5 3 L 5.5 3 L 2.5 6 Z"/>
<ContentPresenter/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Bd" Property="Background" Value="{StaticResource

MCT: Luis Dueas

Pag 82 de 473

Manual de Windows Presentation Foundation


DarkBrush}"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter TargetName="Bd" Property="Background" Value="{StaticResource
DarkBrush}"/>
</Trigger>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="Bd" Property="Background" Value="{StaticResource
DarkBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Arrow" Property="Fill" Value="#AAA"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Type ToolBar}" TargetType="{x:Type ToolBar}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToolBar}">
<Border x:Name="Border"
CornerRadius="2"
BorderThickness="1"
Background="{StaticResource LightBrush}"
BorderBrush="{StaticResource NormalBorderBrush}">
<DockPanel >
<ToggleButton DockPanel.Dock="Right"
IsEnabled="{TemplateBinding HasOverflowItems}"
Style="{StaticResource ToolBarOverflowButtonStyle}"
IsChecked="{Binding Path=IsOverflowOpen,Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
<Popup x:Name="OverflowPopup"
AllowsTransparency="true"
Placement="Bottom"
IsOpen="{Binding Path=IsOverflowOpen,
RelativeSource={RelativeSource TemplatedParent}}"
StaysOpen="false"
Focusable="false"
PopupAnimation="Slide">
<Border x:Name="DropDownBorder"
Background="{StaticResource WindowBackgroundBrush}"
BorderThickness="1"
BorderBrush="{StaticResource SolidBorderBrush}">
<ToolBarOverflowPanel x:Name="PART_ToolBarOverflowPanel"
Margin="2"
WrapWidth="200"
Focusable="true"
FocusVisualStyle="{x:Null}"
KeyboardNavigation.TabNavigation="Cycle"
KeyboardNavigation.DirectionalNavigation="Cycle"/>
</Border>
</Popup>
</ToggleButton>
<Thumb x:Name="ToolBarThumb"
Style="{StaticResource ToolBarThumbStyle}"
Width="10"/>
<ToolBarPanel x:Name="PART_ToolBarPanel"
IsItemsHost="true"
Margin="0,1,2,2"/>
</DockPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsOverflowOpen" Value="true">
<Setter TargetName="ToolBarThumb" Property="IsEnabled" Value="false"/>
</Trigger>
<Trigger Property="ToolBarTray.IsLocked" Value="true">
<Setter TargetName="ToolBarThumb" Property="Visibility" Value="Collapsed"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Type ToolBarTray}" TargetType="{x:Type ToolBarTray}" >
<Setter Property="Background" Value="{StaticResource HorizontalLightBrush}"/>
</Style>
En el ejemplo anterior se usan uno o ms de los siguientes recursos.
<!-- Fill Brushes -->
<LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>

MCT: Luis Dueas

Pag 83 de 473

Manual de Windows Presentation Foundation


<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalLightBrush" StartPoint="0,0" EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#BBB" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="0.1"/>
<GradientStop Color="#EEE" Offset="0.9"/>
<GradientStop Color="#FFF" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<!-- Border Brushes -->
<LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="HorizontalNormalBorderBrush" StartPoint="0,0"
EndPoint="1,0">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="DefaultedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#777" Offset="0.0"/>
<GradientStop Color="#000" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PressedBorderBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#444" Offset="0.0"/>
<GradientStop Color="#888" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>

MCT: Luis Dueas

Pag 84 de 473

Manual de Windows Presentation Foundation


<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
<SolidColorBrush x:Key="LightBorderBrush" Color="#AAA" />
<!-- Miscellaneous Brushes -->
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<SolidColorBrush x:Key="LightColorBrush" Color="#DDD" />

4.3.4.28. Ejemplo de ControlTemplate de ToolTip


En este tema se muestra el objeto ControlTemplate del control ToolTip de WPF.
Ejemplo de ControlTemplate de ToolTip
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto ToolTip
de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Type ToolTip}" TargetType="ToolTip">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="HasDropShadow" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border Name="Border"
Background="{StaticResource LightBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}">
<ContentPresenter
Margin="4"
HorizontalAlignment="Left"
VerticalAlignment="Top" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="HasDropShadow" Value="true">
<Setter TargetName="Border" Property="CornerRadius" Value="4"/>
<Setter TargetName="Border" Property="SnapsToDevicePixels" Value="true"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se usan los siguientes recursos.
<LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
...
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />

4.3.4.29. Ejemplo de ControlTemplate de TreeView


En este tema se muestra el objeto ControlTemplate del control TreeView de WPF.
Ejemplo de ControlTemplate de TreeView
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto
TreeView de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Type TreeView}" TargetType="TreeView">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeView">
<Border
Name="Border"
CornerRadius="1"
Background="{StaticResource WindowBackgroundBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1" >
<ScrollViewer

MCT: Luis Dueas

Pag 85 de 473

Manual de Windows Presentation Foundation


Focusable="False"
CanContentScroll="False"
Padding="4">
<ItemsPresenter/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se usan uno o ms de los siguientes recursos.
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
...
<SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />

4.3.4.30. Ejemplo de ControlTemplate para TreeViewItem


En este tema se muestra el objeto ControlTemplate del control TreeViewItem de WPF.
Ejemplo de ControlTemplate para TreeViewItem
Aunque este ejemplo contiene todos los elementos que se definen en la clase ControlTemplate de un objeto
TreeViewItem de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<!--=================================================================
TreeViewItem
==================================================================-->
<Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid
Width="15"
Height="13"
Background="Transparent">
<Path x:Name="ExpandPath"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="1,1,1,1"
Fill="{StaticResource GlyphBrush}"
Data="M 4 0 L 8 4 L 4 8 Z"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked"
Value="True">
<Setter Property="Data"
TargetName="ExpandPath"
Value="M 0 4 L 8 4 L 4 8 Z"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TreeViewItemFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle Margin="0,0,0,0"
StrokeThickness="5"
Stroke="Black"
StrokeDashArray="1 2"
Opacity="0"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Type TreeViewItem}"
TargetType="{x:Type TreeViewItem}">
<Setter Property="Background"
Value="Transparent"/>
<Setter Property="HorizontalContentAlignment"
Value="{Binding Path=HorizontalContentAlignment,
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment"
Value="{Binding Path=VerticalContentAlignment,
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding"
Value="1,0,0,0"/>

MCT: Luis Dueas

Pag 86 de 473

Manual de Windows Presentation Foundation


<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="FocusVisualStyle"
Value="{StaticResource TreeViewItemFocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="19"
Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ToggleButton x:Name="Expander"
Style="{StaticResource ExpandCollapseToggleStyle}"
IsChecked="{Binding Path=IsExpanded,
RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press"/>
<Border Name="Bd"
Grid.Column="1"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<ContentPresenter x:Name="PART_Header"
ContentSource="Header"
HorizontalAlignment="{TemplateBinding
HorizontalContentAlignment}"/>
</Border>
<ItemsPresenter x:Name="ItemsHost"
Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded"
Value="false">
<Setter TargetName="ItemsHost"
Property="Visibility"
Value="Collapsed"/>
</Trigger>
<Trigger Property="HasItems"
Value="false">
<Setter TargetName="Expander"
Property="Visibility"
Value="Hidden"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="HasHeader"
Value="false"/>
<Condition Property="Width"
Value="Auto"/>
</MultiTrigger.Conditions>
<Setter TargetName="PART_Header"
Property="MinWidth"
Value="75"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="HasHeader"
Value="false"/>
<Condition Property="Height"
Value="Auto"/>
</MultiTrigger.Conditions>
<Setter TargetName="PART_Header"
Property="MinHeight"
Value="19"/>
</MultiTrigger>
<Trigger Property="IsSelected"
Value="true">
<Setter TargetName="Bd"
Property="Background"
Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground"
Value="{DynamicResource {x:Static
SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected"

MCT: Luis Dueas

Pag 87 de 473

Manual de Windows Presentation Foundation


Value="true"/>
<Condition Property="IsSelectionActive"
Value="false"/>
</MultiTrigger.Conditions>
<Setter TargetName="Bd"
Property="Background"
Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se usa el recurso siguiente:
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />

4.3.4.31. Ejemplo de ControlTemplate de Window


En este tema se muestra el objeto ControlTemplate del control Window de WPF.
Ejemplo de ControlTemplate de Window
Aunque este ejemplo contiene todos los elementos definidos en el objeto ControlTemplate de un objeto Window
de manera predeterminada, los valores especficos deben tomarse como ejemplos.
<Style x:Key="{x:Type Window}" TargetType="{x:Type Window}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Grid Background="{StaticResource WindowBackgroundBrush}">
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
<ResizeGrip x:Name="WindowResizeGrip"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Visibility="Collapsed"
IsTabStop="false"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ResizeMode" Value="CanResizeWithGrip">
<Setter TargetName="WindowResizeGrip" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
En el ejemplo anterior se utilizan los siguientes recursos.
<Style x:Key="{x:Type ResizeGrip}" TargetType="{x:Type ResizeGrip}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ResizeGrip}">
<Border Background="Transparent"
SnapsToDevicePixels="True"
Width="16"
Height="16">
<Rectangle Margin="2">
<Rectangle.Fill>
<DrawingBrush Viewport="0,0,4,4"
ViewportUnits="Absolute"
Viewbox="0,0,8,8"
ViewboxUnits="Absolute"
TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#AAA"
Geometry="M 4 4 L 4 8 L 8 8 L 8 4 z"/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>

MCT: Luis Dueas

Pag 88 de 473

Manual de Windows Presentation Foundation


</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
...
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />

4.3.5. Automatizacin de la Interfaz de Usuario de un Control Personalizado de


WPF
Automatizacin de la interfaz de usuario de Microsoft proporciona una nica interfaz generalizada que los
clientes de automatizacin pueden usar para examinar o utilizar las interfaces de usuario de diversas
plataformas y marcos de trabajo. Automatizacin de la interfaz de usuario permite que cdigo de control de
calidad (prueba) y aplicaciones de accesibilidad como lectores de pantalla examinen los elementos de la interfaz
de usuario y simulen la interaccin del usuario con ellos desde otro cdigo.
En este tema se explica cmo implementar un proveedor de automatizacin de la interfaz de usuario de
servidor para un control personalizado que se ejecuta en una aplicacin WPF. WPF admite Automatizacin de la
interfaz de usuario a travs de un rbol de objetos de automatizacin del mismo nivel correspondiente al rbol
de elementos de interfaz de usuario. Las aplicaciones y el cdigo de prueba que proporcionan caractersticas de
accesibilidad pueden usar objetos de automatizacin del mismo nivel directamente (para cdigo en proceso) o a
travs de la interfaz generalizada proporcionada por Automatizacin de la interfaz de usuario.
Clases de automatizacin del mismo nivel
Los controles de WPF admiten Automatizacin de la interfaz de usuario a travs de un rbol de clases del
mismo nivel que se derivan de AutomationPeer. Por convencin, los nombres de las clases del mismo nivel
empiezan

por

el

nombre

de

la

clase

del

control

terminan

por "AutomationPeer".

Por

ejemplo,

ButtonAutomationPeer es la clase del mismo nivel para la clase del control Button. Las clases del mismo nivel
equivalen, en lneas generales, a los tipos de control de Automatizacin de la interfaz de usuario, pero son
especficas de los elementos de WPF. El cdigo de automatizacin que tiene acceso a las aplicaciones WPF a
travs de la interfaz de Automatizacin de la interfaz de usuario no utiliza los elementos de automatizacin del
mismo nivel directamente, pero el cdigo de automatizacin del mismo espacio de proceso s puede hacerlo.
Clases de automatizacin del mismo nivel integradas
Los elementos implementan una clase de automatizacin del mismo nivel si aceptan actividad de la interfaz por
parte del usuario o si contienen informacin necesaria para los usuarios de aplicaciones de lectores de pantalla.
No todos los elementos visuales de WPF tienen objetos de automatizacin del mismo nivel. Algunos ejemplos
de clases que implementan objetos de automatizacin del mismo nivel son Button, TextBox y Label. Ejemplos
de clases que no implementan objetos de automatizacin del mismo nivel son las clases derivadas de
Decorator, como Border, y las clases basadas en Panel, como Grid y Canvas.
La clase base Control no tiene una clase correspondiente del mismo nivel. Si necesita una clase correspondiente
del mismo nivel para un control personalizado derivado de Control, debe derivar la clase personalizada del
mismo nivel de FrameworkElementAutomationPeer.
Consideraciones de seguridad para elementos del mismo nivel derivados
Los elementos de automatizacin del mismo nivel deben ejecutarse en un entorno de confianza parcial. El
cdigo del ensamblado UIAutomationClient no est configurado para ejecutarse en un entorno de confianza
parcial, por lo que el cdigo de automatizacin de los elementos del mismo nivel no debe hacer referencia a
dicho ensamblado. En su lugar, debera utilizar las clases del ensamblado UIAutomationTypes. Por ejemplo,
debera utilizar la clase AutomationElementIdentifiers del ensamblado UIAutomationTypes, que corresponde a

MCT: Luis Dueas

Pag 89 de 473

Manual de Windows Presentation Foundation


la clase AutomationElement del ensamblado UIAutomationClient. Es seguro hacer referencia al ensamblado
UIAutomationTypes en el cdigo de los elementos de automatizacin del mismo nivel.
Navegacin entre elementos del mismo nivel
Despus de localizar los elementos de automatizacin del mismo nivel, el cdigo en proceso puede navegar por
el rbol de elementos del mismo nivel mediante los mtodos GetChildren y GetParent del objeto. La navegacin
por los elementos de WPF dentro de un control se admite mediante la implementacin en los elementos del
mismo nivel del mtodo GetChildrenCore. El sistema de automatizacin de la interfaz de usuario llama a este
mtodo para construir un rbol de elementos secundarios incluidos en un control; por ejemplo, los elementos
de un cuadro de lista. El mtodo UIElementAutomationPeer.GetChildrenCore predeterminado recorre el rbol
visual de elementos para generar el rbol de objetos de automatizacin del mismo nivel. Los controles
personalizados invalidan este mtodo para exponer los elementos secundarios a los clientes de automatizacin
y devuelven los objetos de automatizacin del mismo nivel de los elementos que transmiten informacin o
permiten la interaccin con el usuario.
Personalizaciones de un elemento del mismo nivel derivado
Todas las clases derivadas de UIElement y ContentElement contienen el mtodo virtual protegido
OnCreateAutomationPeer. WPF llama a OnCreateAutomationPeer para obtener el objeto de automatizacin del
mismo nivel correspondiente a cada control. El cdigo de automatizacin puede utilizar los elementos del
mismo nivel para obtener informacin sobre las caractersticas de un control y simular el uso interactivo. Un
control personalizado que admita la automatizacin debe invalidar OnCreateAutomationPeer y devolver una
instancia de una clase derivada de AutomationPeer. Por ejemplo, si un control personalizado se deriva de la
clase

ButtonBase,

el

objeto

devuelto

por

OnCreateAutomationPeer

debera

derivarse

de

ButtonBaseAutomationPeer.
Al implementar un control personalizado, debe invalidar los mtodos bsicos ("Core") de la clase base de
automatizacin del mismo nivel que describen el comportamiento nico y especfico del control personalizado.
Invalidar OnCreateAutomationPeer
Invalide el mtodo OnCreateAutomationPeer para el control personalizado, de modo que devuelva el objeto de
proveedor, que debe derivarse directa o indirectamente de AutomationPeer.
Invalidar GetPattern
Aunque los elementos de automatizacin del mismo nivel simplifican algunos aspectos de la implementacin de
proveedores de Automatizacin de la interfaz de usuario de servidor, los elementos de automatizacin del
mismo nivel de controles personalizados an tienen que controlar las interfaces patrn. Al igual que los
proveedores que no son de WPF, los elementos del mismo nivel admiten patrones de controles mediante
implementaciones de interfaces en el espacio de nombres System.Windows.Automation.Provider, como
IInvokeProvider. Las interfaces de patrones de controles las puede implementar el propio elemento del mismo
nivel u otro objeto. La implementacin en el elemento del mismo nivel de GetPattern devuelve el objeto que
admite el patrn especificado. El cdigo de Automatizacin de la interfaz de usuario llama al mtodo
GetPattern y especifica un valor de enumeracin de PatternInterface. El mtodo que invalida GetPattern debera
devolver el objeto que implementa el patrn especificado. Si el control no tiene ninguna implementacin
personalizada de un patrn, puede llamar a la implementacin del tipo base de GetPattern para recuperar su
implementacin o un valor null si no se admite el patrn para este tipo de control. Por ejemplo, un control
NumericUpDown personalizado puede establecerse en un valor dentro de un intervalo, de forma que su
elemento

de

Automatizacin

de

la

interfaz

de

usuario del

mismo

nivel

implemente

la

interfaz

IRangeValueProvider. En el ejemplo siguiente se muestra cmo se invalida el mtodo GetPattern del elemento
del mismo nivel para responder a un valor PatternInterface.RangeValue.
public override object GetPattern(PatternInterface patternInterface)
{
if (patternInterface == PatternInterface.RangeValue)
{
return this;

MCT: Luis Dueas

Pag 90 de 473

Manual de Windows Presentation Foundation


}
return base.GetPattern(patternInterface);
}
Un mtodo GetPattern tambin puede especificar un subelemento como proveedor del patrn. El siguiente
cdigo muestra cmo ItemsControl transfiere el control del patrn de desplazamiento al elemento del mismo
nivel de su control ScrollViewer interno.
public override object GetPattern(PatternInterface patternInterface)
{
if (patternInterface == PatternInterface.Scroll)
{
ItemsControl owner = (ItemsControl) base.Owner;
// ScrollHost is internal to the ItemsControl class
if (owner.ScrollHost != null)
{
AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(owner.ScrollHost);
if ((peer != null) && (peer is IScrollProvider))
{
peer.EventsSource = this;
return (IScrollProvider) peer;
}
}
}
return base.GetPattern(patternInterface);
}
Para especificar un subelemento para el control del patrn, este cdigo obtiene el objeto del subelemento, crea
un elemento del mismo nivel mediante el mtodo CreatePeerForElement, establece como valor de la propiedad
EventsSource del nuevo elemento del mismo nivel el elemento actual y devuelve el nuevo elemento del mismo
nivel. Al establecer EventsSource en un subelemento se impide que el subelemento aparezca en el rbol de
objetos automatizacin del mismo nivel y se designa que todos los eventos provocados por el subelemento se
originan en el control especificado en EventsSource. El control ScrollViewer no aparece en el rbol de
automatizacin y los eventos de desplazamiento que genera parecern originarse en el objeto ItemsControl.
Invalidar mtodos bsicos ("Core")
El cdigo de automatizacin obtiene informacin sobre el control llamando a los mtodos pblicos de la clase
del mismo nivel. Para proporcionar informacin sobre el control, invalide los mtodos cuyo nombre finalice en
"Core" cuando la implementacin del control difiera de la proporcionada por la clase base de automatizacin del
mismo nivel. Como mnimo, el control debe implementar los mtodos GetClassNameCore y GetAutomation
ControlTypeCore, como se muestra en el ejemplo siguiente.
protected override string GetClassNameCore()
{
return "NumericUpDown";
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Spinner;
}
La implementacin que realice de GetAutomationControlTypeCore describe el control mediante la devolucin de
un valor ControlType. Aunque puede devolver ControlType.Custom, debera devolver un tipo de control ms
especfico (si describe el control con precisin). Un valor devuelto de ControlType.Custom requiere trabajo
adicional para que el proveedor implemente Automatizacin de la interfaz de usuario, y los productos cliente
de Automatizacin de la interfaz de usuario no pueden prever la estructura del control, la interaccin con el
teclado y los posibles patrones del control.
Implemente los mtodos IsControlElementCore y IsContentElementCore para indicar si el control contiene datos
o desempea una funcin de interaccin en la interfaz de usuario (o ambas cosas). De forma predeterminada,
ambos mtodos devuelven true. Esta configuracin aumenta la facilidad de uso de herramientas de
automatizacin como los lectores de pantalla, que pueden utilizar estos mtodos para filtrar el rbol de
automatizacin. Si su mtodo GetPattern transfiere el control del patrn a un subelemento del mismo nivel, el
mtodo IsControlElementCore de dicho subelemento puede devolver el valor false a fin de ocultar el
subelemento del mismo nivel del rbol de automatizacin. Por ejemplo, el desplazamiento por un control
ListBox se controla mediante un elemento ScrollViewer, y los objetos de automatizacin del mismo nivel para

MCT: Luis Dueas

Pag 91 de 473

Manual de Windows Presentation Foundation


PatternInterface.Scroll los devuelve el mtodo GetPattern del elemento ScrollViewerAutomationPeer asociado a
ListBoxAutomationPeer. Por lo tanto, el mtodo IsControlElementCore de ScrollViewerAutomationPeer devuelve
false, de forma que ScrollViewerAutomationPeer no aparece en el rbol de automatizacin.
Los elementos de automatizacin del mismo nivel deben proporcionar los valores predeterminados adecuados
para el control. Tenga en cuenta que el cdigo XAML que hace referencia al control puede invalidar sus
implementaciones

del

mismo

nivel

de

mtodos

bsicos

(core)

mediante

la

inclusin

de

atributos

AutomationProperties. Por ejemplo, el siguiente cdigo XAML crea un botn que tiene dos propiedades
personalizadas de Automatizacin de la interfaz de usuario.
<Button AutomationProperties.Name="Special"
AutomationProperties.HelpText="This is a special button."/>
Implementar proveedores de patrn
Las interfaces implementadas por un proveedor personalizado se declaran explcitamente si el elemento
propietario se deriva directamente de Control. Por ejemplo, en el cdigo siguiente se declara un elemento del
mismo nivel para Control que implementa un valor de intervalo.
public class RangePeer1 : FrameworkElementAutomationPeer, IRangeValueProvider { }
Si el control propietario se deriva de un tipo especfico de control, como RangeBase, el elemento del mismo
nivel puede derivarse de una clase del mismo nivel derivada equivalente. En este caso, el elemento del mismo
nivel

se

derivara

de

RangeBaseAutomationPeer,

que

proporciona

una

implementacin

base

de

IRangeValueProvider. En el cdigo siguiente se muestra la declaracin de este elemento del mismo nivel.
public class RangePeer2 : RangeBaseAutomationPeer { }
Provocar eventos
Los clientes de automatizacin pueden suscribirse a eventos de automatizacin. Los controles personalizados
deben notificar los cambios de estado del control mediante llamadas al mtodo RaiseAutomationEvent. Del
mismo modo, cuando cambie el valor de una propiedad, llame al mtodo RaisePropertyChangedEvent. El cdigo
siguiente muestra cmo obtener el objeto del mismo nivel a partir del cdigo del control y cmo llamar a un
mtodo para provocar un evento. Como medida de optimizacin, el cdigo determina si hay agentes de escucha
para este tipo de evento. Provocar el evento solamente cuando haya agentes de escucha evita una sobrecarga
innecesaria y ayuda a que el control siga respondiendo.
if (AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged))
{
NumericUpDownAutomationPeer peer =
UIElementAutomationPeer.FromElement(nudCtrl) as NumericUpDownAutomationPeer;
if (peer != null)
{
peer.RaisePropertyChangedEvent(RangeValuePatternIdentifiers.ValueProperty,
(double)oldValue,(double)newValue);
}
}

4.4. Agrupar Controles por Categora


Los controles de Windows Presentation Foundation (WPF) se pueden agrupar lgicamente en varias categoras.
Estas categoras se pueden utilizar para seleccionar el control adecuado para cada escenario ayudndolo a ver
qu controles tienen modelos del uso o funcionalidades similares.
Presentacin
Los controles de diseo se utilizan para administrar el tamao, las dimensiones, la posicin y la organizacin de
los elementos secundarios.

Border
BulletDecorator
Canvas
DockPanel
Expander
Grid
GridSplitter

MCT: Luis Dueas

Pag 92 de 473

Manual de Windows Presentation Foundation

GroupBox
Panel
ResizeGrip
Separator
ScrollBar
ScrollViewer
StackPanel
Thumb
Viewbox
VirtualizingStackPanel
Window
WrapPanel

Botones
El botn es uno de los controles de interfaz de usuario ms bsicos. Las aplicaciones suelen realizar algn tipo
de tarea en el evento Click cuando un usuario hace clic en un botn.

Button
RepeatButton

Mens
Los mens se utilizan para agrupar acciones relacionadas o proporcionar ayuda contextual.

ContextMenu
Menu
ToolBar

Seleccin
Los controles de seleccin se utilizan para permitir al usuario seleccionar una o ms opciones.

CheckBox
ComboBox
ListBox
ListView
TreeView
RadioButton
Slider

Navegacin
Los controles de exploracin mejoran o extienden la experiencia de navegacin en la aplicacin, creando
marcos de destino o el aspecto de una aplicacin con fichas.

Frame
Hyperlink
Page
NavigationWindow
TabControl

Cuadros de dilogo
Los cuadros de dilogo proporcionan compatibilidad concreta para los escenarios de interaccin con el usuario
ms comunes, como la impresin.

OpenFileDialog
PrintDialog
SaveFileDialog

Informacin del usuario

MCT: Luis Dueas

Pag 93 de 473

Manual de Windows Presentation Foundation


Los controles de informacin del usuario proporcionan comentarios contextuales o aclaraciones sobre la interfaz
de usuario de una aplicacin. Normalmente, el usuario no puede interactuar con estos controles.

AccessText
Label
Popup
ProgressBar
StatusBar
TextBlock
ToolTip

Documentos
WPF incluye varios controles especializados para ver documentos. Estos controles optimizan la experiencia de
lectura, basndose en el escenario de destino del usuario.

DocumentViewer
FlowDocumentPageViewer
FlowDocumentReader
FlowDocumentScrollViewer
StickyNoteControl

Entrada
Los controles de entrada permiten al usuario escribir texto y otros contenidos.

TextBox
RichTextBox
PasswordBox

Multimedia
WPF incluye compatibilidad integrada para hospedar contenido de audio y vdeo, as como cdecs para la
mayora de los formatos de imagen ms populares.

Image
MediaElement
SoundPlayerAction

Entrada manuscrita digital


Los controles de entrada manuscrita digital proporcionan compatibilidad integrada con las caractersticas de
Tablet PC, como la lectura y escritura de entradas manuscritas.

InkCanvas
InkPresenter

4.5. Modelos de Contenido


En esta seccin se describen los diversos modelos de contenido de Windows Presentation Foundation (WPF).

4.5.1. Modelo de Contenido de WPF


Windows Presentation Foundation (WPF) es una plataforma de presentacin que proporciona muchos tipos de
controles y objetos parecidos cuyo propsito primario es mostrar contenido. Los modelos de contenido de estos
tipos pueden ser muy flexibles y, a veces, muy rgidos. Por ejemplo, algunos tipos pueden contener
exclusivamente una sola parte de contenido, pero algunos pueden contener varios elementos. Adems, el
contenido puede ser texto, otros controles o un conjunto concreto de elementos.
En este tema se organizan los tipos de controles y de objetos parecidos basndose en la jerarqua de herencia y
se proporciona una referencia rpida para los modelos de contenido de estas familias de tipos. Una propiedad

MCT: Luis Dueas

Pag 94 de 473

Manual de Windows Presentation Foundation


de contenido es aqulla que se utiliza para almacenar el contenido del objeto. En este tema se incluyen
nicamente los tipos distribuidos con Windows SDK.
Clases ContentControl
Descripcin

ContentControl es un tipo de Control que contiene una sola parte de contenido.

Propiedades de
contenido

Content

Tipos que pertenecen


a esta familia de tipos

Button, ButtonBase, CheckBox, ComboBoxItem, ContentControl, Frame,


GridViewColumnHeader,
GroupItem,
Label,
ListBoxItem,
ListViewItem,
NavigationWindow, RadioButton, RepeatButton, ScrollViewer, StatusBarItem,
ToggleButton, ToolTip, UserControl, Window

Tipos que pueden


contener tipos
ContentControl

Clases ContentControl, clases HeaderedContentControl, clases ItemsControl,


clases HeaderedItemsControl, clases Panel, clases Decorator, clases Adorner

Subcategoras

Clases HeaderedContentControl

Clases HeaderedContentControl
Descripcin

HeaderedContentControl es un tipo de ContentControl que contiene


una sola parte de contenido y tambin tiene un Header.

Propiedades de contenido

Content, Header

Tipos que pertenecen a esta familia


de tipos

Expander, GroupBox, HeaderedContentControl, TabItem

Tipos que pueden contener tipos


HeaderedContentControl

Clases ContentControl, clases HeaderedContentControl, clases


ItemsControl, clases HeaderedItemsControl, clases Panel, clases
Decorator, clases Adorner

Clases ItemsControl
Descripcin

ItemsControl es un tipo de Control que puede contener varios elementos,


tales como cadenas, objetos u otros elementos.

Propiedades de contenido

Items, ItemsSource

Tipo de contenido principal

Varios elementos que pueden ser cadenas, objetos u otros elementos.

Tipos que pertenecen a


esta familia de tipos

Menu, MenuBase, ContextMenu, ComboBox, ItemsControl, ListBox, ListView,


TabControl, TreeView, Selector, StatusBar

Tipos que pueden contener


tipos ItemsControl

Clases ContentControl, clases HeaderedContentControl, clases ItemsControl,


clases HeaderedItemsControl, clases Panel, clases Decorator, clases Adorner

Subcategoras

Clases HeaderedItemsControl

Clases HeaderedItemsControl
Descripcin

HeaderedItemsControl es un tipo de ItemsControl que puede contener


varios elementos, tales como cadenas, objetos u otros elementos, y
tambin tiene un encabezado.

Propiedades de contenido

Header, Items, ItemsSource

Tipos que pertenecen a esta


familia de tipos

HeaderedItemsControl, MenuItem, TreeViewItem, ToolBar

MCT: Luis Dueas

Pag 95 de 473

Manual de Windows Presentation Foundation

Tipos que pueden contener


tipos HeaderedItemsControl

Normalmente, los objetos MenuItem se utilizan como elementos


secundarios de un elemento Menu; los objetos TreeViewItem se utilizan
como elementos secundarios de un elemento TreeView; y los objetos
ToolBar se utilizan como elementos secundarios de ToolBarTray.

Clases Decorator
Descripcin

Decorator es un tipo de FrameworkElement que aplica efectos a un UIElement


secundario nico o alrededor de l.

Propiedades de
contenido

Child

Tipo de contenido
principal

Un solo UIElement

Tipos que pertenecen a


esta familia de tipos

ButtonChrome, ClassicBorderDecorator, ListBoxChrome, SystemDropShadow


Chrome, Border, InkPresenter, BulletDecorator, Viewbox, AdornerDecorator

Tipos que pueden


contener tipos Decorator

Clases ContentControl, clases HeaderedContentControl, clases ItemsControl,


clases HeaderedItemsControl, clases Panel, clases Decorator, clases Adorner

Clases Panel
Descripcin

Panel es un tipo de FrameworkElement que coloca y organiza objetos


secundarios.

Propiedades de
contenido

Children

Tipo de contenido
principal

Uno o varios objetos UIElement.

Tipos que pertenecen a


esta familia de tipos

Canvas, DockPanel, Grid, TabPanel, ToolBarOverflowPanel, StackPanel,


ToolBarPanel,
UniformGrid,
VirtualizingPanel,
VirtualizingStackPanel,
WrapPanel

Tipos que pueden


contener tipos Panel

Clases ContentControl, clases HeaderedContentControl, clases ItemsControl,


clases HeaderedItemsControl, clases Panel, clases Decorator, clases Adorner

Clases Adorner
Descripcin

Un Adorner es un FrameworkElement que se enlaza a ("adorna") un solo


UIElement.

Propiedades de
contenido

Ninguna.

Tipo de contenido
principal

Ninguno.

Tipos que pertenecen a


esta familia de tipos

La clase Adorner se proporciona como marco de trabajo para la creacin de sus


propios adornos; Windows Presentation Foundation (WPF) no proporciona
ningn adorno implementado.

Clases de texto dinmico


Descripcin

TextElement es un tipo de FrameworkContentElement que contiene texto u


otros objetos TextElement. TextElement y sus clases derivadas se utilizan para
constituir el contenido dinmico.

Tipo de contenido
principal

Varios elementos que pueden ser cadenas, objetos u otros elementos.

MCT: Luis Dueas

Pag 96 de 473

Manual de Windows Presentation Foundation

Tipos que pertenecen a


esta familia de tipos

AnchoredBlock, Block, BlockUIContainer, Bold, Figure, Floater, Hyperlink,


Inline, InlineUIContainer, Italic, LineBreak, List, ListItem, Paragraph, Run,
Section, Span, Table, Underline

Tipos que pueden


contener tipos de texto
dinmico

Clase FlowDocument

Clase TextBox
Descripcin

TextBox es un control que se puede utilizar para mostrar o editar texto sin
formato. TextBox nicamente admite texto sin formato. Para aplicaciones que
requieran compatibilidad con contenido ms complejo, consulte RichTextBox.

Propiedades de
contenido

Text

Tipo de contenido
principal

Cadenas

Tipos que
pertenecen a esta
familia de tipos

TextBox

Tipos que pueden


contener clases
TextBox

Clases ContentControl, clases HeaderedContentControl, clases ItemsControl, clases


HeaderedItemsControl, clases Panel, clases Decorator, clases Adorner, clases de
texto dinmico

Clase TextBlock
Descripcin

TextBlock es un control ligero que permite mostrar pequeos fragmentos de


contenido dinmico.

Propiedades de
contenido

Inlines

Tipos que pertenecen a


esta familia de tipos

TextBlock

Tipos que pueden


contener clases
TextBlock

Clases ContentControl, clases HeaderedContentControl, clases ItemsControl,


clases HeaderedItemsControl, clases Panel, clases Decorator, clases Adorner,
clases de texto dinmico

Clases Shape
Descripcin

Un Shape es un tipo de FrameworkElement que muestra una forma


geomtrica.

Propiedades de
contenido

Ninguna.

Tipo de contenido
principal

Ninguno.

Tipos que pertenecen a


esta familia de tipos

Consulte la clase Shape para obtener una lista de tipos que se derivan de
Shape.

Tipos que pueden


contener tipos Shape

Clases ContentControl, clases HeaderedContentControl, clases ItemsControl,


clases HeaderedItemsControl, clases Panel, clases Decorator, clases Adorner

4.5.2. Informacin General sobre el Modelo de Contenido de Controles

MCT: Luis Dueas

Pag 97 de 473

Manual de Windows Presentation Foundation


En este tema se explican los modelos de contenido utilizados por las clases que heredan de Control. El modelo
de contenido especifica los tipos de objetos que un control puede contener. En este tema, el trmino "control"
se limita a una clase que tiene la clase Control en algn punto de su jerarqua de clases. Las cuatro clases
siguientes que heredan de Control definen los cuatro modelos de contenido descritos en este tema:

ContentControl contiene un solo elemento.


HeaderedContentControl contiene un encabezado y un solo elemento.
ItemsControl contiene una coleccin de elementos.
HeaderedItemsControl contiene un encabezado y una coleccin de elementos.

Estas cuatro clases actan como clases base para la mayora de los controles de WPF. Las clases que usan
estos modelos de contenido pueden contener los mismos tipos de contenido y tratar el contenido de la misma
manera; cualquier tipo de objeto que se puede colocar en ContentControl (o en una clase que hereda de
ContentControl) se puede colocar en un control que tenga cualquiera de los otros tres modelos de contenido. En
la ilustracin siguiente se muestra un control de cada modelo de contenido que contiene una imagen y texto.

ContentControl
El modelo de contenido ms simple de los cuatro es

ContentControl, que tiene una propiedad Content. La

propiedad Content es del tipo Object, as que no hay ninguna restriccin sobre lo que se puede colocar en
ContentControl. Puede utilizar Lenguaje de marcado de aplicaciones extensible (XAML) o cdigo para establecer
Content.
Los controles siguientes utilizan el modelo de contenido ContentControl:

Button
ButtonBase
CheckBox
ComboBoxItem
ContentControl
Frame
GridViewColumnHeader
GroupItem
Label
ListBoxItem
ListViewItem
NavigationWindow
RadioButton
RepeatButton
ScrollViewer
StatusBarItem
ToggleButton
ToolTip
UserControl
Window

MCT: Luis Dueas

Pag 98 de 473

Manual de Windows Presentation Foundation


En el ejemplo siguiente se muestra cmo crear cuatro controles Button con Content establecido en uno de los
valores siguientes:

Una cadena
Un objeto DateTime.
Un UIElement
Un Panel que contiene otros objetos UIElement.

Nota:
En el ejemplo de versin de Lenguaje de marcado de aplicaciones extensible (XAML) se podran utilizar
etiquetas <Button.Content> alrededor del contenido de cada botn, pero no es necesario.
<!--Create a Button with a string as its content.-->
<Button>This is string content of a Button</Button>
<!--Create a Button with a DateTime object as its content.-->
<Button xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:DateTime>2004/3/4 13:6:55</sys:DateTime>
</Button>
<!--Create a Button with a single UIElement as its content.-->
<Button>
<Rectangle Height="40" Width="40" Fill="Blue"/>
</Button>
<!--Create a Button with a panel that contains multiple objects
as its content.-->
<Button>
<StackPanel>
<Ellipse Height="40" Width="40" Fill="Blue"/>
<TextBlock TextAlignment="Center">Button</TextBlock>
</StackPanel>
</Button>
' Add a string to a button.
Dim stringContent As New Button()
stringContent.Content = "This is string content of a Button"
' Add a DateTime object to a button.
Dim objectContent As New Button()
Dim dateTime1 As New DateTime(2004, 3, 4, 13, 6, 55)
objectContent.Content = dateTime1
' Add a single UIElement to a button.
Dim uiElementContent As New Button()
Dim rect1 As New Rectangle()
rect1.Width = 40
rect1.Height = 40
rect1.Fill = Brushes.Blue
uiElementContent.Content = rect1
' Add a panel that contains multpile objects to a button.
Dim panelContent As New Button()
Dim stackPanel1 As New StackPanel()
Dim ellipse1 As New Ellipse()
Dim textBlock1 As New TextBlock()
ellipse1.Width = 40
ellipse1.Height = 40
ellipse1.Fill = Brushes.Blue
textBlock1.TextAlignment = TextAlignment.Center
textBlock1.Text = "Button"
stackPanel1.Children.Add(ellipse1)
stackPanel1.Children.Add(textBlock1)
panelContent.Content = stackPanel1
En la ilustracin siguiente se muestran los cuatro botones creados en el ejemplo anterior.

HeaderedContentControl
HeaderedContentControl hereda la propiedad Content de ContentControl y define la propiedad Header, que es
de tipo Object. Header proporciona un encabezado para el control. Al igual que la propiedad Content de
ContentControl, Header puede ser cualquier tipo. WPF se distribuye con tres controles que heredan de
HeaderedContentControl:

Expander
GroupBox
TabItem

MCT: Luis Dueas

Pag 99 de 473

Manual de Windows Presentation Foundation


En el ejemplo siguiente se crea un control TabControl (ItemsControl) que contiene dos objetos TabItem. El
primer TabItem tiene contenido enriquecido en Header y en Content: Header es establece en un StackPanel que
contiene una Ellipse y un TextBlock, y Content se establece en un StackPanel que contiene un TextBlock y una
Label. El Header del segundo TabItem se establece en una cadena, y el Content se establece en un solo
TextBlock.
<TabControl>
<TabItem>
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<Ellipse Width="10" Height="10" Fill="DarkGray"/>
<TextBlock>Tab 1</TextBlock>
</StackPanel>
</TabItem.Header>
<StackPanel>
<TextBlock>Enter some text</TextBlock>
<TextBox Name="textBox1" Width="50"/>
</StackPanel>
</TabItem>
<TabItem Header="Tab 2">
<!--Bind TextBlock.Text to the TextBox on the first
TabItem.-->
<TextBlock Text="{Binding ElementName=textBox1, Path=Text}"/>
</TabItem>
</TabControl>
En la ilustracin siguiente se muestra el TabControl creado por el ejemplo anterior.

ItemsControl
Los controles que heredan de ItemsControl contienen una coleccin de objetos. Un ejemplo de ItemsControl es
ListBox. Puede utilizar la propiedad ItemsSource o la propiedad Items para rellenar ItemsControl.
Propiedad ItemsSource
La propiedad ItemsSource de ItemsControl permite utilizar cualquier tipo que implemente IEnumerable como
contenido de ItemsControl. ItemsSource se suele utilizar para mostrar una recoleccin de datos o enlazar un
ItemsControl a un objeto de coleccin.
En el ejemplo siguiente se crea una clase denominada MyData que es una coleccin de cadenas simples.
Public Class MyData
Inherits ObservableCollection(Of String)
Public Sub New() '
Add("Item 1")
Add("Item 2")
Add("Item 3")
End Sub 'New
End Class 'MyData
En el ejemplo siguiente se enlaza ItemsSource a MyData.
<!--Create an instance of MyData as a resource.-->
<src:MyData x:Key="dataList"/>
...
<ListBox ItemsSource="{Binding Source={StaticResource dataList}}"/>
Dim listBox1 As New ListBox()
Dim listData As New MyData()
Dim binding1 As New Binding()
binding1.Source = listData
listBox1.SetBinding(ListBox.ItemsSourceProperty, binding1)
En la ilustracin siguiente se muestra el ListBox creado en el ejemplo anterior.

Propiedad Items

MCT: Luis Dueas

Pag 100 de 473

Manual de Windows Presentation Foundation


Si no desea utilizar un objeto que implementa IEnumerable para rellenar ItemsControl, puede agregar
elementos mediante la propiedad Items. Los elementos de ItemsControl pueden tener tipos distintos entre s.
Por ejemplo, un ListBox puede contener un elemento que sea una cadena y otro elemento que sea una Image.
Nota:
Cuando la propiedad ItemsSource est establecida, puede utilizar la propiedad Items para leer la
ItemCollection, pero no puede agregar elementos a ItemCollection ni modificarla. Si se establece la
propiedad ItemsSource en una referencia null (Nothing en Visual Basic) se quita la coleccin y se restaura
el uso de Items, que ser una ItemCollection vaca.
En el ejemplo siguiente se crea ListBox con cuatro tipos diferentes de elementos.
<!--Create a ListBox that contains a string, a Rectangle,
a Panel, and a DateTime object. These items can be accessed
via the Items property.-->
<ListBox xmlns:sys="clr-namespace:System;assembly=mscorlib"
Name="simpleListBox">
<!-- The <ListBox.Items> element is implicitly used.-->
This is a string in a ListBox
<sys:DateTime>2004/3/4 13:6:55</sys:DateTime>
<Rectangle Height="40" Width="40" Fill="Blue"/>
<StackPanel Name="itemToSelect">
<Ellipse Height="40" Fill="Blue"/>
<TextBlock>Text below an Ellipse</TextBlock>
</StackPanel>
<TextBlock>String in a TextBlock</TextBlock>
<!--</ListBox.Items>-->
</ListBox>
' Create a Button with a string as its content.
listBox1.Items.Add("This is a string in a ListBox")
' Create a Button with a DateTime object as its content.
Dim dateTime1 As New DateTime(2004, 3, 4, 13, 6, 55)
listBox1.Items.Add(dateTime1)
' Create a Button with a single UIElement as its content.
Dim rect1 As New Rectangle()
rect1.Width = 40
rect1.Height = 40
rect1.Fill = Brushes.Blue
listBox1.Items.Add(rect1)
' Create a Button with a panel that contains multiple objects
' as its content.
Dim ellipse1 As New Ellipse()
Dim textBlock1 As New TextBlock()
ellipse1.Width = 40
ellipse1.Height = 40
ellipse1.Fill = Brushes.Blue
textBlock1.TextAlignment = TextAlignment.Center
textBlock1.Text = "Text below an Ellipse"
stackPanel1.Children.Add(ellipse1)
stackPanel1.Children.Add(textBlock1)
listBox1.Items.Add(stackPanel1)
En la ilustracin siguiente se muestra el ListBox creado en el ejemplo anterior.

Clases de contenedores de elementos


Cada ItemsControl que se distribuye con WPF tiene una clase correspondiente que representa un elemento de
ItemsControl. En la tabla siguiente se muestra una lista de objetos ItemsControl que se distribuyen con WPF y
sus contenedores de elementos correspondientes.
ItemsControl

Contenedor de elemento

ComboBox

ComboBoxItem

ContextMenu

MenuItem

ListBox

ListBoxItem

MCT: Luis Dueas

Pag 101 de 473

Manual de Windows Presentation Foundation

ListView

ListViewItem

Menu

MenuItem

StatusBar

StatusBarItem

TabControl

TabItem

TreeView

TreeViewItem

Puede crear explcitamente un contenedor de elemento para cada elemento de ItemsControl, pero no es
necesario. La creacin o no de un contenedor de elemento en ItemsControl depende en gran medida del
escenario. Por ejemplo, si enlaza datos a la propiedad ItemsSource, no se crea explcitamente un contenedor
de elemento. Es importante tener presentes los puntos siguientes:

El tipo de los objetos de ItemCollection vara dependiendo de si crea explcitamente un contenedor de


elemento.

Puede obtener el contenedor de elemento aunque no lo cree explcitamente.

Style cuyo TargetType est establecido en un contenedor de elemento se aplica con independencia de
si el contenedor de elemento se crea de manera explcita o no.

La herencia de propiedades se comporta de manera diferente para los contenedores de elementos


creados implcita y explcitamente, porque slo los creados de manera explcita forman parte del rbol
lgico.

Para ilustrar estos puntos, en el ejemplo siguiente se crean dos controles ListBox. En el ejemplo se crean
objetos ListBoxItem para el primer ListBox, pero no para el segundo ListBox. En el segundo caso, ListBoxItem
se crea implcitamente para cada elemento de ListBox.
<!--Explicitly create a ListBoxItem for each item in the ListBox-->
<ListBox xmlns:sys="clr-namespace:System;assembly=mscorlib"
Name="listBoxItemListBox">
<!-- The <ListBox.Items> element is implicitly used.-->
<ListBoxItem>
This is a string in a ListBox
</ListBoxItem>
<ListBoxItem>
<sys:DateTime>2004/3/4 13:6:55</sys:DateTime>
</ListBoxItem>
<ListBoxItem>
<Rectangle Height="40" Width="40" Fill="Blue"/>
</ListBoxItem>
<ListBoxItem>
<StackPanel>
<Ellipse Height="40" Width="40" Fill="Blue"/>
<TextBlock>Text below an Ellipse</TextBlock>
</StackPanel>
</ListBoxItem>
<!--</ListBox.Items>-->
</ListBox>
...
<!--Create a ListBox that contains a string, a Rectangle,
a Panel, and a DateTime object. These items can be accessed
via the Items property.-->
<ListBox xmlns:sys="clr-namespace:System;assembly=mscorlib"
Name="simpleListBox">
<!-- The <ListBox.Items> element is implicitly used.-->
This is a string in a ListBox
<sys:DateTime>2004/3/4 13:6:55</sys:DateTime>
<Rectangle Height="40" Width="40" Fill="Blue"/>
<StackPanel Name="itemToSelect">
<Ellipse Height="40" Fill="Blue"/>
<TextBlock>Text below an Ellipse</TextBlock>
</StackPanel>
<TextBlock>String in a TextBlock</TextBlock>
<!--</ListBox.Items>-->
</ListBox>

MCT: Luis Dueas

Pag 102 de 473

Manual de Windows Presentation Foundation


La ItemCollection de cada ListBox es distinta. Cada elemento de la propiedad Items del primer ListBox es
ListBoxItem, pero su tipo es diferente en el segundo ListBox. En el ejemplo siguiente se confirma esta situacin
recorriendo en iteracin los elementos de ambos controles ListBox y comprobando el tipo de cada elemento.
Console.WriteLine("Items in simpleListBox:")
For Each item As Object In simpleListBox.Items
Console.WriteLine(item.GetType().ToString())
Next item
Console.WriteLine(vbCr + "Items in listBoxItemListBox:")
For Each item As Object In listBoxItemListBox.Items
Console.WriteLine(item.GetType().ToString())
Next item
End Sub 'ReportLBIs
...
'
Items in simpleListBox:
'
System.String
'
System.Windows.Shapes.Rectangle
'
System.Windows.Controls.StackPanel
'
System.DateTime
'
'
Items in listBoxItemListBox:
'
System.Windows.Controls.ListBoxItem
'
System.Windows.Controls.ListBoxItem
'
System.Windows.Controls.ListBoxItem
'
System.Windows.Controls.ListBoxItem
En la ilustracin siguiente se muestran los dos ListBox controles que se crearon en el ejemplo anterior.

A menudo, se necesita el contenedor de elemento de un elemento, pero no se ha creado explcitamente en la


aplicacin. Para obtener el contenedor de elemento est asociado a un elemento determinado, utilice el mtodo
ContainerFromItem. En el ejemplo siguiente se muestra cmo obtener un contenedor de elemento asociado a
un elemento cuando ListBoxItem no se ha creado de manera explcita. En el ejemplo se supone que el objeto
denominado itemToSelect no es un ListBoxItem y se ha agregado al ListBox, simpleListBox.
Dim lbi As ListBoxItem = _
CType(simpleListBox.ItemContainerGenerator.ContainerFromItem(itemToSelect), ListBoxItem)
If Not (lbi Is Nothing) Then
lbi.IsSelected = True
End If
Los estilos cuyo TargetType est establecido en un contenedor de elemento se aplican a los contenedores de
elemento creados de manera implcita y explcita. En el ejemplo siguiente se crea Style como un recurso para
un ListBoxItem que centra horizontalmente el contenido de ListBoxItem. Cuando este estilo se aplica a los
objetos ListBox, se centran los elementos en ambos objetos ListBox.
<!--Create a Style as a Resource.-->
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
La figura siguiente muestra los dos controles ListBox cuando se aplica el estilo del ejemplo anterior.

La herencia de propiedades con los estilos y los contenedores de elementos est relacionada con la estructura
del rbol lgico. Al crear explcitamente el contenedor de elemento, ste forma parte del rbol lgico. Si no se
crea el contenedor de elemento, no forma parte del rbol lgico. En la ilustracin siguiente se muestra la
diferencia en el rbol lgico entre los dos controles ListBox del ejemplo anterior.

MCT: Luis Dueas

Pag 103 de 473

Manual de Windows Presentation Foundation

Los objetos que heredan de la clase Visual heredan los valores de la propiedad de su elemento primario lgico.
En el ejemplo siguiente se crea un ListBox con dos controles TextBlock y se establece la propiedad Foreground
de ListBox en el color azul. El primer TextBlock, textBlock1, est contenido en un ListBoxItem creado de
manera explcita; el segundo TextBlock, textBlock2, no lo est. En el ejemplo tambin se define Style para un
ListBoxItem que establece el Foreground de ListBoxItem en el color verde.
<!--Create a Style as a Resource.-->
<Style TargetType="ListBoxItem">
<Setter Property="Foreground" Value="Green"/>
</Style>
...
<ListBox Foreground="Blue">
<ListBoxItem>
<TextBlock Name="textBlock1">TextBlock in a ListBoxItem.</TextBlock>
</ListBoxItem>
<TextBlock Name="textBlock2">TextBlock not in a ListBoxItem.</TextBlock>
</ListBox>
En la ilustracin siguiente se muestra el ListBox creado en el ejemplo anterior.

La cadena de textBlock1 es verde y la cadena de textBlock2 es azul, porque cada control TextBlock hereda la
propiedad Foreground de su elemento primario lgico respectivo. El elemento primario lgico de textBox1 es
ListBoxItem, y el elemento primario lgico de textBox2 es ListBox.
HeaderedItemsControl
HeaderedItemsControl hereda de la clase ItemsControl. HeaderedItemsControl define la propiedad Header, que
sigue las mismas reglas que la propiedad Header de un HeaderedContentControl. WPF se distribuye con tres
controles que heredan de HeaderedItemsControl:

MenuItem
ToolBar
TreeViewItem

En el ejemplo siguiente se crea un control TreeViewItem. TreeView contiene un solo TreeViewItem, que tiene la
etiqueta TreeViewItem 1, y contiene los elementos siguientes:

Una cadena.
Un objeto DateTime.
Un TreeViewItem que contiene un Rectangle en su Header.
Un TreeViewItem cuya propiedad Header est establecida en un StackPanel que contiene dos objetos.

Nota:
En el ejemplo se crean de manera explcita objetos TreeViewItem para estos dos ltimos elementos,
porque Rectangle y StackPanel heredan de la clase Visual. El estilo predeterminado de TreeViewItem
establece la propiedad Foreground. Los objetos secundarios heredan el valor de la propiedad del
TreeViewItem creado de manera explcita, lo que suele ser el comportamiento deseado.

MCT: Luis Dueas

Pag 104 de 473

Manual de Windows Presentation Foundation


<TreeView xmlns:sys="clr-namespace:System;assembly=mscorlib" Margin="10">
<TreeViewItem Header="TreeViewItem 1" IsExpanded="True">
TreeViewItem 1a
<sys:DateTime>2004/3/4 13:6:55</sys:DateTime>
<TreeViewItem>
<TreeViewItem.Header>
<Rectangle Height="10" Width="10" Fill="Blue"/>
</TreeViewItem.Header>
</TreeViewItem>
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Ellipse Width="10" Height="10" Fill="DarkGray"/>
<TextBlock >TreeViewItem 1d</TextBlock>
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
</TreeViewItem>
</TreeView>

4.5.3. Informacin General sobre el Modelo de Contenido de Decorator


En esta informacin general del modelo de contenido se describe el contenido compatible con Decorator. El
control Border es un tipo de Decorator.
Propiedades de contenido de Decorator
Un objeto Decorator tiene las siguientes propiedades de contenido.

Child

En la ilustracin siguiente se muestra un objeto TextBox decorado con un objeto Border a su alrededor.

Utilizar la propiedad Child


La propiedad Child especifica el elemento UIElement nico al que Decorator afecta (decora). En el ejemplo de
cdigo siguiente se muestra el uso de la propiedad Child para agregar un objeto TextBox a un objeto Border.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Border BorderThickness="20" BorderBrush="Black">
<TextBox>TextBox with a black Border around it</TextBox>
</Border>
</Page>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace SDKSample
{
public partial class BasicBorderExample : Page
{
public BasicBorderExample()
{
TextBox myTextBox = new TextBox();
// Put some initial text in the TextBox.
myTextBox.Text = "TextBox with a black Border around it";
// Create a Border
Border myBorder = new Border();
myBorder.BorderThickness = new Thickness(20);
myBorder.BorderBrush = Brushes.Black;
// Add TextBox to the Border.
myBorder.Child = myTextBox;
// myStackPanel.Children.Add(myTextBox);
this.Content = myBorder;
}
}
}
Tipos que comparten este modelo de contenido
Las clases siguientes heredan de la clase Decorator.

AdornerDecorator
Border

MCT: Luis Dueas

Pag 105 de 473

Manual de Windows Presentation Foundation

BulletDecorator
ButtonChrome
ClassicBorderDecorator
InkPresenter
ListBoxChrome
SystemDropShadowChrome
Viewbox

4.5.4. Informacin General sobre el Modelo de Contenido de Paneles


En esta informacin general sobre el modelo de contenido se describe el contenido admitido para un objeto
Panel. StackPanel y DockPanel son ejemplos de objetos Panel.
Propiedades de contenido del panel
Un objeto Panel tiene las siguientes propiedades de contenido.

Children

Utilizar la propiedad Children


La propiedad Children puede contener varios objetos, incluso otros objetos Panel. En el ejemplo de cdigo
siguiente se muestra el uso de la propiedad Children para agregar dos objetos Button a un objeto StackPanel.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Button>Button 1</Button>
<Button>Button 2</Button>
</StackPanel>
</Page>
using System;
using System.Windows;
using System.Windows.Controls;
namespace SDKSample
{
public partial class StackpanelExample : Page
{
public StackpanelExample()
{
// Create two buttons
Button myButton1 = new Button();
myButton1.Content = "Button 1";
Button myButton2 = new Button();
myButton2.Content = "Button 2";
// Create a StackPanel
StackPanel myStackPanel = new StackPanel();
// Add the buttons to the StackPanel
myStackPanel.Children.Add(myButton1);
myStackPanel.Children.Add(myButton2);
this.Content = myStackPanel;
}
}
}
Tipos que comparten este modelo de contenido
Las clases siguientes heredan de la clase Panel.

Canvas
DockPanel
Grid
TabPanel
ToolBarOverflowPanel
UniformGrid
StackPanel
ToolBarPanel
VirtualizingPanel
VirtualizingStackPanel
WrapPanel

MCT: Luis Dueas

Pag 106 de 473

Manual de Windows Presentation Foundation

4.5.5. Informacin General sobre el Modelo de Contenido de TextBlock


En esta informacin general del modelo de contenido se describe el contenido compatible con TextBlock.
TextBlock es un control ligero que permite mostrar pequeos fragmentos de contenido dinmico.
Propiedades de contenido de TextBlock
Un objeto TextBlock tiene las siguientes propiedades de contenido.

Inlines

Agregar contenido dinmico


TextBlock puede hospedar y mostrar elementos de contenido dinmico Inline. Entre los elementos que admite
se incluyen AnchoredBlock, Bold, Hyperlink, InlineUIContainer, Italic, LineBreak, Run, Span y Underline.
En los ejemplos siguientes se muestra el uso de la propiedad Inlines para agregar elementos dinmicos a un
control TextBlock.
TextBlock textBlock1 = new TextBlock();
TextBlock textBlock2 = new TextBlock();
textBlock1.TextWrapping = textBlock2.TextWrapping = TextWrapping.Wrap;
textBlock2.Background = Brushes.AntiqueWhite;
textBlock2.TextAlignment = TextAlignment.Center;
textBlock1.Inlines.Add(new Bold(new Run("TextBlock")));
textBlock1.Inlines.Add(new Run(" is designed to be "));
textBlock1.Inlines.Add(new Italic(new Run("lightweight")));
textBlock1.Inlines.Add(new Run(", and is geared specifically at integrating "));
textBlock1.Inlines.Add(new Italic(new Run("small")));
textBlock1.Inlines.Add(new Run(" portions of flow content into a UI."));
textBlock2.Text =
"By default, a TextBlock provides no UI beyond simply displaying its contents.";
En la ilustracin siguiente se muestra cmo se representa este ejemplo.

Tipos que comparten este modelo de contenido


nicamente TextBlock utiliza este modelo de contenido.

4.5.6. Informacin General sobre el Modelo de Contenido de TextBox


En esta informacin general del modelo de contenido se describe el contenido compatible con TextBox. TextBox
es un control que se puede utilizar para mostrar o editar texto sin formato. TextBox nicamente admite texto
sin formato.
En el grfico siguiente se muestra un ejemplo de TextBox.

Propiedades de contenido de TextBox


Un objeto TextBox tiene las siguientes propiedades de contenido.

Text

Utilizar la propiedad Text


La propiedad Text especifica el texto sin formato contenido dentro de TextBox. En el ejemplo de cdigo
siguiente se muestra el uso de la propiedad Text para agregar texto sin formato a un objeto TextBox.
tbSettingText.Text = "Initial text contents of the TextBox."
Tipos que comparten este modelo de contenido
nicamente TextBox utiliza este modelo de contenido.

MCT: Luis Dueas

Pag 107 de 473

Manual de Windows Presentation Foundation

5. Datos
El enlace de datos de Windows Presentation Foundation (WPF) proporciona un mtodo simple y coherente para
que las aplicaciones presenten e interacten con datos. Los elementos se pueden enlazar a los datos desde
gran variedad de orgenes de datos en forma de objetos common language runtime (CLR) y XML. Windows
Presentation Foundation (WPF) tambin proporciona un mecanismo para la transferencia de datos a travs de
las operaciones de arrastrar y colocar.

5.1. Enlace de Datos


El enlace de datos de Windows Presentation Foundation (WPF) proporciona un mtodo simple y coherente para
que las aplicaciones presenten e interacten con datos. Los elementos se pueden enlazar a los datos de
diversos orgenes de datos en forma de objetos CLR (Common Language Runtime) y XML.

5.1.1. Informacin General sobre el Enlace de Datos


El enlace de datos de Windows Presentation Foundation (WPF) proporciona un mtodo simple y coherente para
que las aplicaciones presenten e interacten con datos. Los elementos se pueden enlazar a los datos de
diversos orgenes de datos en forma de objetos common language runtime (CLR) y XML. Los objetos
ContentControl como Button y los objetos ItemsControl como ListBox y ListView tienen funciones integradas
que permiten definir de forma flexible elementos de datos individuales o colecciones de elementos de datos. A
partir de estos datos se pueden generar vistas de ordenacin, filtro y agrupacin.
La funcionalidad de enlace de datos de WPF presenta varias ventajas con respecto a los modelos tradicionales,
como un mayor nmero de propiedades que admiten de forma inherente el enlace de datos, una representacin
flexible de los datos en la interfaz de usuario y la separacin bien definida de la lgica del negocio de la interfaz
de usuario.
En este tema se describen en primer lugar los conceptos fundamentales del enlace de datos de WPF y, a
continuacin, se explica el uso de la clase Binding y otras caractersticas del enlace de datos.
Qu es el enlace de datos?
El enlace de datos es el proceso que establece una conexin entre la interfaz de usuario de la aplicacin y la
lgica del negocio. Si el enlace est correctamente configurado y los datos proporcionan las notificaciones
adecuadas, cuando los datos cambian su valor, los elementos que estn enlazados a ellos reflejan los cambios
automticamente. El enlace de datos tambin puede implicar la actualizacin automtica de los datos que
subyacen a una representacin externa de los datos de un elemento, cuando esta representacin cambia. Por
ejemplo, si el usuario modifica el valor en un elemento TextBox, el valor de los datos subyacentes se actualiza
automticamente para reflejar ese cambio.
Un uso tpico del enlace de datos es colocar los datos de configuracin locales o de servidor en formularios o en
otros controles de la interfaz de usuario. En WPF, este concepto se expande para incluir el enlace de un gran
nmero de propiedades a una gran variedad de orgenes de datos. En WPF, las propiedades de dependencia de
los elementos se pueden enlazar a objetos CLR (incluidos los objetos ADO.NET u objetos asociados a servicios
web y propiedades web) y datos XML.
Para ver un ejemplo de enlace de datos, examine la siguiente interfaz de usuario de aplicacin en Demo Data
Binding:

MCT: Luis Dueas

Pag 108 de 473

Manual de Windows Presentation Foundation

El ejemplo anterior es la interfaz de usuario de una aplicacin que muestra una lista de artculos subastados. La
aplicacin muestra las caractersticas siguientes de enlace de datos:

El contenido de ListBox se enlaza a una coleccin de objetos AuctionItem. Un objeto AuctionItem tiene
propiedades como Descripcin, StartPrice, StartDate, Categora, SpecialFeatures, etc.

Los datos (objetos AuctionItem ) mostrados en ListBox se incluyen en una plantilla para mostrar la
descripcin y el precio actual de cada artculo. Para ello se utiliza un objeto DataTemplate. Asimismo,
el aspecto de cada artculo depende del valor SpecialFeatures del elemento AuctionItem que se va a
mostrar. Si el valor SpecialFeatures de AuctionItem es Color, el artculo tiene un borde azul. Si el valor
es Highlight, el artculo tiene un borde naranja y una estrella.

El usuario puede agrupar, filtrar u ordenar los datos mediante los controles CheckBox proporcionados.
En la imagen anterior, los controles CheckBox "Group by category" y "Sort by category and date"
estn seleccionados. Es posible que haya observado que los datos se agrupan en funcin de la
categora del producto, y el nombre de las categoras se muestra en orden alfabtico. Aunque no se
aprecia muy bien en la imagen, los artculos estn ordenados tambin por fecha de inicio dentro de
cada categora. Para ello se utiliza una vista de coleccin. En la seccin Enlace a colecciones se
describe este tipo de vistas.

Cuando el usuario selecciona un artculo, ContentControl muestra los detalles del artculo seleccionado.
Esto recibe el nombre de escenario principal-detalle. En la seccin Escenario principal-detalle se
proporciona informacin sobre este tipo de escenario de enlace.

El tipo de la propiedad StartDate es DateTime, que devuelve una fecha que incluye el tiempo en
milisegundos. En esta aplicacin, se ha utilizado un convertidor personalizado para que se muestre una
cadena de fecha ms corta. En la seccin Conversin de datos se proporciona informacin sobre los
convertidores.

Cuando el usuario hace clic en el botn Add Product, se muestra el siguiente formulario:

MCT: Luis Dueas

Pag 109 de 473

Manual de Windows Presentation Foundation

El usuario puede modificar los campos del formulario, obtener una vista previa de la lista de productos
mediante la vista previa abreviada y los paneles de vista previa ms detallada y, a continuacin, hacer clic en
submit para agregar la nueva lista de productos. Todas las funciones de agrupacin, filtrado y ordenacin
existentes se aplicarn a la nueva entrada. En este caso en concreto, el artculo especificado en la imagen
anterior se mostrar como el segundo artculo dentro de la categora Computer.
Lo que no se muestra en esta imagen es la lgica de validacin proporcionada en el control TextBox Start Date.
Si el usuario escribe una fecha no vlida (con un formato no vlido o una fecha pasada), se le notificar con un
control ToolTip y un signo de exclamacin de color rojo situado junto al control TextBox. En la seccin
Validacin de datos se explica cmo crear lgica de validacin.
Antes de abordar las diferentes caractersticas de enlace de datos citadas anteriormente, en la siguiente seccin
explicaremos los conceptos fundamentales imprescindibles para comprender el enlace de datos de WPF.
Conceptos bsicos del enlace de datos
Independientemente del elemento que se vaya a enlazar y de la naturaleza del origen de datos, cada enlace
sigue siempre el modelo que se muestra en la ilustracin siguiente:

Como se muestra en la ilustracin anterior, el enlace de datos es esencialmente el puente entre el destino del
enlace y el origen del enlace. En la ilustracin se muestran los siguientes conceptos fundamentales del enlace
de datos de WPF:

Normalmente, cada enlace tiene estos cuatro componentes: un objeto de destino del enlace, una
propiedad de destino, un origen del enlace y una ruta de acceso al valor en el origen del enlace que se
va a usar. Por ejemplo, si desea enlazar el contenido de TextBox a la propiedad Nombre de un objeto
Empleado, su objeto de destino es TextBox, la propiedad de destino es la propiedad Text, el valor que
se va a utilizar es Nombre y el objeto de origen es el objeto Empleado.

La propiedad de destino debe ser una propiedad de dependencia. La mayora de las propiedades
UIElement son propiedades de dependencia y la mayora de las propiedades de dependencia, excepto
las de slo lectura, admiten el enlace de datos de forma predeterminada. (Slo los tipos
DependencyObject pueden definir propiedades de dependencia y todos los UIElement se derivan de
DependencyObject).

Si bien no se especifica en la ilustracin, cabe observar que el objeto de origen del enlace no se limita
a un objeto CLR personalizado. El enlace de datos de WPF admite datos en forma de objetos de CLR y
XML. Para proporcionar algunos ejemplos, el origen del enlace puede ser un objeto UIElement,

MCT: Luis Dueas

Pag 110 de 473

Manual de Windows Presentation Foundation


cualquier objeto de lista, un objeto de CLR que est asociado a datos de ADO.NET o servicios web, o
bien un objeto XMLNode que contiene datos XML.
Cuando consulte otros temas del kit de desarrollo de software (SDK), es importante tener en cuenta que
cuando establece un enlace, enlaza un destino del enlace a un origen del enlace. Por ejemplo, si va a mostrar
algunos datos XML subyacentes en ListBox utilizando el enlace de datos, enlazar ListBox a los datos XML.
Para establecer un enlace, se utiliza el objeto Binding. En el resto de este tema se explican muchos de los
conceptos asociados al objeto Binding y algunas de las propiedades y uso de este objeto
Direccin del flujo de datos
Como se mencion anteriormente y como se indica por la flecha de la ilustracin anterior, el flujo de datos de
un enlace puede ir desde el destino del enlace al origen del enlace (por ejemplo, el valor de origen cambia
cuando un usuario modifica el valor de TextBox) o desde el origen del enlace al destino del enlace (por ejemplo,
el contenido de TextBox se actualiza con los cambios en el origen del enlace) si el origen del enlace proporciona
las notificaciones correspondientes.
Tal vez desee que su aplicacin permita que los usuarios cambien los datos y los propaguen al objeto de origen.
O tal vez no desee permitir que los usuarios actualicen los datos de origen. Puede controlar este
comportamiento estableciendo la propiedad Mode del objeto Binding. En la ilustracin siguiente se muestran los
tipos diferentes de flujo de datos:

El enlace OneWay permite que los cambios en la propiedad de origen actualicen automticamente la
propiedad de destino, pero los cambios en la propiedad de destino no se propagan de nuevo a la
propiedad de origen. Este tipo de enlace es adecuado si el control que se va a enlazar es de slo
lectura de forma implcita. Por ejemplo, podra enlazar a un origen como un tablero de cotizaciones o
quizs su propiedad de destino no tenga ninguna interfaz de control para realizar modificaciones, como
un color de fondo enlazado a datos de una tabla. Si no hay necesidad de supervisar los cambios de la
propiedad de destino, con el modo de enlace OneWay evitar el trabajo adicional que supone usar el
modo de enlace TwoWay.

El enlace TwoWay permite que los cambios realizados en la propiedad de origen o en la de destino se
actualicen automticamente en el otro. Este tipo de enlace es adecuado para formularios modificables
u otros escenarios de interfaz de usuario totalmente interactivos. La mayora de las propiedades tienen
el enlace OneWay de forma predeterminada, pero algunas propiedades de dependencia (normalmente
las propiedades de controles modificables por el usuario como la propiedad Text de TextBox y la
propiedad IsChecked de CheckBox) tienen el enlace TwoWay de manera predeterminada. Una manera
de determinar mediante programacin si una propiedad de dependencia se enlaza de forma
predeterminada de modo unidireccional o bidireccional es obtener los metadatos de la propiedad
mediante

GetMetadata

y,

continuacin,

comprobar

el

valor

booleano

de

la

propiedad

BindsTwoWayByDefault.

OneWayToSource es el enlace inverso de OneWay; actualiza la propiedad de origen cuando cambia la


propiedad de destino. Podra utilizar este tipo de enlace si, por ejemplo, slo necesita volver a evaluar
el valor de origen de la interfaz de usuario.

El enlace OneTime, no mostrado en la ilustracin, permite que la propiedad de origen inicialice la


propiedad de destino, pero los dems cambios no se propagan. Esto significa que si el contexto de los
datos sufre un cambio o el objeto del contexto de datos cambia, el cambio se refleja en la propiedad

MCT: Luis Dueas

Pag 111 de 473

Manual de Windows Presentation Foundation


de destino. Este tipo de enlace es adecuado si utiliza los datos all donde es adecuado utilizar una
captura del estado actual o cuando los datos son realmente estticos. Este tipo de enlace tambin es
til si desea inicializar la propiedad de destino con algn valor de una propiedad de origen y no se
conoce el contexto de los datos de antemano. Se trata bsicamente de una forma ms fcil de enlace
OneWay que proporciona un mejor rendimiento en los casos en los que no cambia el valor de origen.
Observe que para detectar los cambios en el origen (aplicables a los enlaces OneWay y TwoWay), el origen
debe implementar un mecanismo apropiado de notificacin de cambios de propiedades, como
INotifyPropertyChanged.
En la pgina de propiedades de Mode se proporciona ms informacin sobre los modos de enlace y un ejemplo
sobre cmo especificar la direccin de un enlace.
Qu desencadena la actualizacin del origen
Los enlaces TwoWay u OneWayToSource realizan escuchas para detectar los cambios en la propiedad de
destino y los propagan de nuevo al origen. Esto se conoce como la actualizacin del origen. Por ejemplo, puede
modificar el texto de un control TextBox para cambiar el valor de origen subyacente. Como se describe en la
ltima seccin, el valor de la propiedad Mode del enlace determina la direccin del flujo de datos.
Pero se actualizar su valor de origen mientras modifica el texto o despus de modificarlo y sacar el mouse
fuera del control TextBox? La propiedad UpdateSourceTrigger del enlace determina qu desencadena la
actualizacin del origen. Los puntos de las flechas derecha en la ilustracin siguiente muestran la funcin de la
propiedad UpdateSourceTrigger:

Si el valor de UpdateSourceTrigger es PropertyChanged, el valor al que apunta la fecha derecha del enlace
TwoWay u OneWayToSource se actualizar en cuanto cambie la propiedad de destino. Sin embargo, si el valor
de UpdateSourceTrigger es LostFocus, ese valor slo se actualizar con el nuevo valor cuando la propiedad de
destino pierda el foco.
Al igual que ocurre con la propiedad Mode, las diferentes propiedades de dependencia tienen valores de
UpdateSourceTrigger predeterminados diferentes. El valor predeterminado de la mayora de las propiedades de
dependencia es PropertyChanged, mientras que la propiedad Text tiene un valor predeterminado de LostFocus.
Esto significa que el origen se suele actualizar cuando la propiedad de destino cambia, lo que es adecuado para
controles CheckBox y otros controles sencillos. Sin embargo, para los campos de texto, la actualizacin cada
vez que se pulsa una tecla puede disminuir el rendimiento y deniega al usuario la oportunidad usual de
retroceder y corregir los errores tipogrficos antes confirmar el nuevo valor. Por ese motivo, la propiedad Text
tiene un valor predeterminado de LostFocus en lugar de PropertyChanged.
En la tabla siguiente se proporciona un escenario de ejemplo para cada valor de UpdateSourceTrigger utilizando
TextBox como ejemplo:

Valor UpdateSourceTrigger

Cundo se actualiza
el valor de origen

Escenario de ejemplo de TextBox

LostFocus (valor
predeterminado para
TextBox.Text)

Cuando el control
TextBox pierde el foco

TextBox asociado a lgica de validacin.

PropertyChanged

Cuando escribe en el
control TextBox

Controles TextBox en una ventana de chat

MCT: Luis Dueas

Pag 112 de 473

Manual de Windows Presentation Foundation

Explicit

Cuando la aplicacin
llama a UpdateSource

Controles
TextBox
en
un
formulario
modificable (slo actualiza los valores de
origen cuando el usuario hace clic en el botn
de envo)

Crear un enlace
Como recapitulacin de algunos de los conceptos descritos en las secciones anteriores, recordemos que un
enlace se establece mediante el objeto Binding y que cada enlace tiene normalmente cuatro componentes: el
destino del enlace, la propiedad de destino, el origen del enlace y una ruta de acceso al valor de origen que se
va a utilizar. En esta seccin se explica cmo configurar un enlace.
Considere el ejemplo siguiente, en el que el objeto de origen del enlace es una clase denominada MyData que
se define en el espacio de nombres SDKSample. En esta demostracin, la clase MyData tiene una propiedad de
cadena denominada ColorName, cuyo valor se establece en "Red". Por tanto, este ejemplo genera un botn con
un fondo rojo.
<DockPanel
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:SDKSample">
<DockPanel.Resources>
<c:MyData x:Key="myDataSource"/>
</DockPanel.Resources>
<DockPanel.DataContext>
<Binding Source="{StaticResource myDataSource}"/>
</DockPanel.DataContext>
<Button Background="{Binding Path=ColorName}"
Width="150" Height="30">I am bound to be RED!</Button>
</DockPanel>
Si aplicamos este ejemplo a nuestro diagrama bsico, la ilustracin resultante tendr el siguiente aspecto. Se
trata de un enlace OneWay porque la propiedad Background admite el enlace OneWay de forma
predeterminada.

Tal vez se pregunte por qu esto funciona si la propiedad ColorName es de tipo string mientras que la
propiedad Background es de tipo Brush.
Especificar el origen del enlace
Observe que en el ejemplo anterior, el origen del enlace se especifica estableciendo la propiedad DataContext
en el elemento DockPanel. A continuacin, Button hereda el valor DataContext de DockPanel, que es su
elemento primario. Recordemos que el objeto de origen del enlace es uno de los cuatro componentes
necesarios de un enlace. Por tanto, si no se especifica el objeto de origen del enlace, el enlace no funcionar.
Hay varias formas de especificar el objeto de origen del enlace. Utilizar la propiedad DataContext en un
elemento primario es til si va a enlazar varias propiedades al mismo origen. Sin embargo, a veces puede ser
ms adecuado especificar el origen del enlace en declaraciones de enlace individuales. En el ejemplo anterior,
en lugar de utilizar la propiedad DataContext, puede especificar el origen del enlace estableciendo directamente
la propiedad Source en la declaracin de enlace del botn, como en el ejemplo siguiente:
<DockPanel.Resources>
<c:MyData x:Key="myDataSource"/>
</DockPanel.Resources>
<Button Width="150" Height="30"
Background="{Binding Source={StaticResource myDataSource},
Path=ColorName}">I am bound to be RED!
</Button>
Adems de establecer directamente la propiedad DataContext en un elemento, heredar el valor de DataContext
de un antecesor (como el botn del primer ejemplo) y especificar explcitamente el origen del enlace
estableciendo la propiedad Source en Binding (como el botn del ltimo ejemplo), tambin puede utilizar la
propiedad ElementName o la propiedad RelativeSource para especificar el origen del enlace. La propiedad

MCT: Luis Dueas

Pag 113 de 473

Manual de Windows Presentation Foundation


ElementName es til cuando se enlaza a otros elementos de la aplicacin como, por ejemplo, cuando se utiliza
un control deslizante para ajustar el ancho de un botn. La propiedad RelativeSource es til cuando el enlace se
especifica en ControlTemplate o Style.
Especificar la ruta de acceso al valor
Si su origen del enlace es un objeto, utiliza la propiedad Path para especificar el valor que se va a usar para el
enlace. Si va a enlazar a datos XML, utiliza la propiedad XPath para especificar el valor. En algunos casos,
puede ser pertinente usar la propiedad Path aunque los datos sean XML. Por ejemplo, si desea tener acceso a la
propiedad Name de un XMLNode devuelto (como resultado de una consulta XPath), debe utilizar la propiedad
Path adems de la propiedad XPath.
Observe que aunque hemos recalcado que el Path al valor que se va a utilizar es uno de los cuatro
componentes necesarios de un enlace, en los escenarios en los que se enlaza a un objeto completo, el valor
que se utiliza ser el mismo que el objeto de origen del enlace. En esos casos, se puede no especificar un Path.
Considere el ejemplo siguiente:
<ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="true"/>
En el ejemplo anterior se utiliza la sintaxis de enlace vaca: {Binding}. En este caso, ListBox hereda el objeto
DataContext de un elemento DockPanel principal (no mostrado en este ejemplo). Cuando no se especifica la
ruta de acceso, el comportamiento predeterminado es enlazar al objeto completo. Es decir, en este ejemplo, la
ruta de acceso se ha omitido porque estamos enlazando la propiedad ItemsSource al objeto completo.
Adems del enlace a una coleccin, este escenario es til tambin cuando se desea enlazar a un objeto
completo en lugar de simplemente a una propiedad individual de un objeto. Por ejemplo, si el objeto de origen
es de tipo string y simplemente desea enlazar a la propia cadena. Otro escenario comn es cuando se desea
enlazar un elemento a un objeto con varias propiedades.
Observe que puede ser necesario aplicar lgica personalizada para que los datos sean significativos para la
propiedad de destino enlazada. La lgica personalizada puede consistir en un convertidor personalizado (si no
existe la conversin de tipos predeterminada).
Binding y BindingExpression
Antes de explicar otras caractersticas y usos del enlace de datos, es conveniente presentar la clase
BindingExpression. Como ha visto en las secciones anteriores, la clase Binding es la clase de alto nivel para la
declaracin de un enlace; la clase Binding proporciona muchas propiedades que permiten especificar las
caractersticas de un enlace. Una clase relacionada, BindingExpression, es el objeto subyacente que mantiene la
conexin entre el origen y el destino. Un enlace contiene toda la informacin que se puede compartir entre
varias expresiones de enlace. Una clase BindingExpression es una expresin de instancia que no se puede
compartir y que contiene toda la informacin de las instancias de la clase Binding.
Considere, por ejemplo, lo siguiente, donde myDataObject es una instancia de la clase MyData, myBinding es el
objeto Binding de origen y la clase MyData es una clase definida que contiene una propiedad de cadena
denominada MyDataProperty. En este ejemplo se enlaza el contenido de texto de mytext, una instancia de
TextBlock, a MyDataProperty.
Dim data1 As New MyData(DateTime.Now)
Dim binding1 As New Binding("MyDataProperty")
binding1.Source = data1
Me.myText.SetBinding(TextBlock.TextProperty, binding1)
Puede utilizar el mismo objeto myBinding para crear otros enlaces. Por ejemplo, podra utilizar el objeto
myBinding para enlazar el contenido de texto de una casilla a MyDataProperty. En ese escenario, habr dos
instancias de BindingExpression que comparten el objeto myBinding.
Un

objeto

BindingExpression

se

puede

obtener

travs

del

valor

devuelto

por

la

llamada

GetBindingExpression en un objeto enlazado a datos.

MCT: Luis Dueas

Pag 114 de 473

Manual de Windows Presentation Foundation


Conversin de datos
En el ejemplo anterior, el botn es rojo porque su propiedad Background est enlazada a una propiedad de
cadena con el valor "Red". Esto funciona porque hay un convertidor de tipos en el tipo Brush para convertir el
valor de cadena en Brush.
Al agregar esta informacin a la ilustracin de la seccin Crear un enlace, obtenemos el siguiente diagrama:

Pero y si en lugar de una propiedad de tipo string, el objeto de origen del enlace tiene una propiedad Color de
tipo Color? En ese caso, para que el enlace funcione tendr que convertir el valor de la propiedad Color en
algn

valor

que

la

propiedad

Background

acepte.

Tendr

que

crear

un

convertidor

personalizado

implementando la interfaz IValueConverter, como en el ejemplo siguiente:


[ValueConversion(typeof(Color), typeof(SolidColorBrush))]
public class ColorBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
Color color = (Color)value;
return new SolidColorBrush(color);
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return null;
}
}
En la pgina de referencia de IValueConverter encontrar ms informacin al respecto.
Ahora que se ha utilizado el convertidor personalizado en lugar de la conversin predeterminada, nuestro
diagrama presenta el siguiente aspecto:

Recordemos que las conversiones predeterminadas pueden estar disponibles si el tipo que se va a enlazar
contiene convertidores de tipo. Este comportamiento depender de los convertidores de tipos disponibles en el
destino. Si no est seguro, cree su propio convertidor.
A continuacin, se incluyen algunos escenarios tpicos en los que sera lgico implementar un convertidor de
datos:

Los datos se muestran de forma diferente, dependiendo de la referencia cultural. Por ejemplo, tal vez
desee implementar un convertidor de monedas o un convertidor de fechas y horas del calendario en
funcin de los valores o normas utilizados en una determinada referencia cultural.

Los datos que se utilizan no estn diseados necesariamente para cambiar el valor textual de una
propiedad, sino para cambiar otro valor, como el origen de una imagen, o el color o estilo del texto
que se va a mostrar. En este caso se pueden utilizar convertidores para convertir el enlace de una
propiedad que tal vez no sea adecuada, como el enlace de un campo de texto a la propiedad
Background de una celda de tabla.

Hay varios controles o varias propiedades de controles enlazados a los mismos datos. En ese caso, el
enlace principal podra mostrar simplemente el texto, mientras que los otros enlaces controlan los
aspectos de presentacin, pero se sigue utilizando el mismo enlace como informacin de origen.

MCT: Luis Dueas

Pag 115 de 473

Manual de Windows Presentation Foundation

Hasta el momento, no hemos proporcionado an una descripcin del enlace MultiBinding, en el que
una propiedad de destino tiene una coleccin de enlaces. En el caso de un enlace MultiBinding, se
utiliza un IMultiValueConverter personalizado para generar un valor final a partir de los valores de los
enlaces. Por ejemplo, el color podra calcularse a partir de los valores de rojo, azul y verde, que
pueden ser valores de los mismos o de diferentes objetos de origen del enlace. Vea la pgina de la
clase MultiBinding para obtener informacin y ejemplos al respecto.

Enlace a colecciones
Un objeto de origen del enlace se puede tratar como un objeto nico cuyas propiedades contienen los datos, o
como una recoleccin de datos de objetos polimrficos que suelen estar agrupados (como el resultado de una
consulta a una base de datos). Hasta ahora slo hemos explicado el enlace a objetos individuales, pero el
enlace a una recoleccin de datos es un escenario comn. Por ejemplo, es habitual utilizar ItemsControl como
ListBox, ListView o TreeView para mostrar una recoleccin de datos.
Afortunadamente, nuestro diagrama bsico an sigue siendo vlido. Si enlaza un ItemsControl a una coleccin,
el diagrama tendr el siguiente aspecto:

Como se muestra en este diagrama, para enlazar ItemsControl a un objeto de coleccin, la propiedad que se
utiliza es ItemsSource. Puede considerar la propiedad ItemsSource como el contenido del ItemsControl.
Observe que el enlace es OneWay porque la propiedad ItemsSource admite el enlace OneWay de forma
predeterminada.
Cmo implementar colecciones
Es posible enumerar cualquier coleccin que implementa la interfaz IEnumerable. Sin embargo, para configurar
enlaces dinmicos de modo que las inserciones o eliminaciones que se realicen en la coleccin actualicen la
interfaz de usuario de forma automtica, la coleccin debe implementar la interfaz INotifyCollectionChanged.
Esta interfaz expone un evento que debe provocarse siempre que se realicen cambios en la coleccin
subyacente.
WPF proporciona la clase ObservableCollection<(Of <(T>)>), que es una implementacin integrada de una
recoleccin de datos que expone la interfaz INotifyCollectionChanged. Observe que para permitir totalmente la
transferencia de valores de datos de los objetos de origen a los destinos, cada objeto de la coleccin que
admite propiedades enlazables debe implementar tambin la interfaz INotifyPropertyChanged. Para obtener un
ejemplo, vea Ejemplo Binding to a Collection. Para obtener ms informacin, vea Informacin general sobre
orgenes de enlaces.
Antes de implementar su propia coleccin, considere la posibilidad de utilizar ObservableCollection<(Of
<(T>)>) o una de las clases de coleccin existentes, como List<(Of <(T>)>), Collection<(Of <(T>)>) y
BindingList<(Of <(T>)>), entre otras muchas. Si cuenta con un escenario avanzado y desea implementar su
propia coleccin, considere la posibilidad de utilizar IList, que proporciona una coleccin no genrica de objetos
a los que se puede obtener acceso individualmente por ndice y, por consiguiente, proporciona el mximo
rendimiento.
Vistas de coleccin
Una vez que ItemsControl est enlazado a una recoleccin de datos, quizs desee ordenar, filtrar o agrupar los
datos. Para ello, se utilizan vistas de coleccin, que son clases que implementan la interfaz ICollectionView.
Qu son las vistas de coleccin?

MCT: Luis Dueas

Pag 116 de 473

Manual de Windows Presentation Foundation


Una vista de coleccin es un nivel situado encima de la coleccin de origen del enlace, que le permite navegar y
mostrar la coleccin de origen en funcin de las consultas de ordenacin, filtrado y agrupacin, sin tener que
cambiar la propia coleccin de origen subyacente. Una vista de coleccin tambin contiene un puntero al
elemento actual de la coleccin. Si la coleccin de origen implementa la interfaz INotifyCollectionChanged, los
cambios provocados por el evento CollectionChanged se propagarn a las vistas.
Dado que las vistas no cambian las colecciones de origen subyacente, cada coleccin de origen puede tener
varias vistas asociadas. Por ejemplo, podra tener una coleccin de objetos Task. El uso de vistas le permite
mostrar los mismos datos de formas diferentes. Por ejemplo, en el lado izquierdo de la pgina es posible que
desee mostrar las tareas ordenadas por prioridad y, en el lado derecho, agrupadas por rea.
Cmo crear una vista
Una manera de crear y utilizar una vista es crear directamente una instancia del objeto de vista y utilizar a
continuacin esa instancia como el origen del enlace. Considere, por ejemplo, la aplicacin Demo Data Binding
que se muestra en la seccin Qu es el enlace de datos? La aplicacin se implementa de forma que ListBox se
enlaza a una vista a travs de la recoleccin de datos en lugar de la coleccin de datos directamente. El
ejemplo siguiente se ha extrado de la aplicacin Demo Data Binding. La clase CollectionViewSource es el proxy
de Lenguaje de marcado de aplicaciones extensible (XAML) de una clase que hereda de CollectionView. En este
ejemplo en concreto, la propiedad Source de la vista se enlaza a la coleccin AuctionItems (de tipo
ObservableCollection<(Of <(T>)>)) del objeto de aplicacin actual.
<Window.Resources>
<CollectionViewSource
Source="{Binding Source={x:Static Application.Current}, Path=AuctionItems}"
x:Key="listingDataView" />
</Window.Resources>
A continuacin, listingDataView acta como el origen del enlace para los elementos de la aplicacin, como
ListBox:
<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8"
ItemsSource="{Binding Source={StaticResource listingDataView}}">
</ListBox>
Para crear otra vista para la misma coleccin, puede crear otra instancia de CollectionViewSource y asignarle un
nombre x:Key diferente.
En la tabla siguiente se muestra qu tipos de datos de vista se crean como vista de coleccin predeterminada o
por CollectionViewSource en funcin del tipo de coleccin de origen.
Tipo de coleccin de
origen

Tipo de vista de coleccin

Notas

IEnumerable

Un tipo interno basado en


CollectionView

No se pueden agrupar los


elementos.

IList

ListCollectionView

Ms rpido.

IBindingList

BindingListCollectionView

Utilizar una vista predeterminada


Especificar una vista de coleccin como origen de enlace es una forma de crear y utilizar una vista de coleccin.
WPF tambin crea una vista de coleccin predeterminada para cada coleccin utilizada como origen de enlace.
Si enlaza directamente a una coleccin, WPF enlaza a su vista predeterminada. Tenga en cuenta que todos los
enlaces a una misma coleccin comparten esta vista predeterminada, de modo que si se realiza un cambio en
una vista predeterminada a travs de un control enlazado o mediante cdigo (como un cambio de ordenacin o
en el puntero de elemento actual, que se describe ms adelante), ste se refleja en el resto de los enlaces a la
misma coleccin.
Para obtener la vista predeterminada, se utiliza el mtodo GetDefaultView.
Vistas de coleccin con tablas de datos de ADO.NET

MCT: Luis Dueas

Pag 117 de 473

Manual de Windows Presentation Foundation


Para mejorar el rendimiento, las vistas de coleccin para objetos DataTable o DataView de ADO.NET delegan la
ordenacin y el filtrado a DataView. Esto hace que todas las vistas de coleccin del origen de datos compartan
la ordenacin y el filtrado. Para habilitar la ordenacin y el filtrado independientes de cada vista de coleccin,
inicialice cada vista de este tipo con su propio objeto DataView.
Ordenar
Como se mencion anteriormente, las vistas pueden aplicar un criterio de ordenacin a una coleccin. Cuando
este criterio existe en la coleccin subyacente, los datos pueden o no tener un orden relevante inherente. La
vista de la coleccin le permite aplicar un orden o cambiar el orden predeterminado, en funcin de los criterios
de comparacin especificados. Como es una vista de datos basada en un cliente, podra ser habitual que el
usuario quisiera ordenar las columnas de los datos de tabla por el valor correspondiente a la columna. Con las
vistas, se puede aplicar esta ordenacin controlada por el usuario, sin tener que realizar ningn cambio en la
coleccin subyacente ni tener tampoco que volver a consultar el contenido de la coleccin.
En el siguiente ejemplo se muestra la lgica de ordenacin del control CheckBox "Sort by category and date" de
la interfaz de usuario de aplicacin de la seccin Qu es el enlace de datos?:
private void AddSorting(object sender, RoutedEventArgs args)
{
// This sorts the items first by Category and within each Category,
// by StartDate. Notice that because Category is an enumeration,
// the order of the items is the same as in the enumeration declaration
listingDataView.SortDescriptions.Add(
new SortDescription("Category", ListSortDirection.Ascending));
listingDataView.SortDescriptions.Add(
new SortDescription("StartDate", ListSortDirection.Ascending));
}
Filtrar
Las vistas pueden aplicar tambin un filtro a una coleccin. Esto significa que aunque un elemento pueda existir
en la coleccin, esta vista en concreto est destinada a mostrar nicamente determinado subconjunto de la
coleccin completa. Podra filtrar los datos en funcin de una condicin. Por ejemplo, como ocurre en la
aplicacin de la seccin Qu es el enlace de datos?, el control CheckBox "Show only bargains" contiene lgica
para filtrar los artculos con un costo de 25 dlares o ms. El cdigo siguiente se ejecuta para establecer
ShowOnlyBargainsFilter como el controlador de eventos Filter cuando se selecciona CheckBox:
listingDataView.Filter += new FilterEventHandler(ShowOnlyBargainsFilter);
El controlador de eventos ShowOnlyBargainsFilter se implementa del modo siguiente:
private void ShowOnlyBargainsFilter(object sender, FilterEventArgs e)
{
AuctionItem product = e.Item as AuctionItem;
if (product != null)
{
// Filter out products with price 25 or above
if (product.CurrentPrice < 25)
{
e.Accepted = true;
}
else
{
e.Accepted = false;
}
}
}
Si utiliza directamente alguna de las clases CollectionView en lugar de CollectionViewSource, deber usar la
propiedad Filter para especificar una devolucin de llamada.
Grupo
Salvo la clase interna que ve una coleccin IEnumerable, todas las vistas de coleccin admiten la funcionalidad
de agrupacin, que permite al usuario dividir la coleccin en la vista de coleccin en grupos lgicos. Los grupos
pueden ser explcitos, donde el usuario proporciona una lista de grupos, o implcitos, donde los grupos se
generan dinmicamente en funcin de los datos.
En e ejemplo siguiente se muestra la lgica del control CheckBox "Group by category":
// This groups the items in the view by the property "Category"

MCT: Luis Dueas

Pag 118 de 473

Manual de Windows Presentation Foundation


PropertyGroupDescription groupDescription = new PropertyGroupDescription();
groupDescription.PropertyName = "Category";
listingDataView.GroupDescriptions.Add(groupDescription);
Punteros de elemento actual
Las vistas admiten tambin la nocin de elemento actual. Puede navegar por los objetos en una vista de
coleccin. A medida que navega por los objetos, mueve un puntero de elemento que le permite recuperar el
objeto ubicado concretamente en esa posicin en la coleccin.
Dado que WPF slo se enlaza a una coleccin mediante una vista (una vista especificada por el usuario o la
vista predeterminada de la coleccin), todos los enlaces a las colecciones tienen un puntero de elemento actual.
Al enlazar a una vista, el carcter de barra diagonal ("/") de un valor Path designa el elemento actual de la
vista. En el ejemplo siguiente, el contexto de datos es una vista de coleccin. La primera lnea enlaza a la
coleccin. La segunda lnea enlaza al elemento actual de la coleccin. La tercera lnea enlaza a la propiedad

Description del elemento actual de la coleccin.


<Button Content="{Binding }" />
<Button Content="{Binding Path=/}" />
<Button Content="{Binding Path=/Description}" />
La sintaxis de barra diagonal y propiedad tambin puede apilarse para recorrer una jerarqua de colecciones. En
el ejemplo siguiente se enlaza al elemento actual de una coleccin denominada Offices, que es una propiedad
del elemento actual de la coleccin de origen.
<Button Content="{Binding /Offices/}" />
Toda ordenacin o filtrado que se aplique a la coleccin puede afectar al puntero del elemento actual. La
ordenacin conserva el puntero del elemento actual en el ltimo elemento seleccionado, pero se reestructura la
vista de coleccin a su alrededor. (Quizs el elemento seleccionado estaba antes al principio de la lista, pero
ahora puede que est en alguna parte del medio). El filtrado conserva el elemento seleccionado si la seleccin
permanece en la vista despus del filtrado. De lo contrario, el puntero del elemento actual se establece en el
primer elemento de la vista de coleccin filtrada.
Escenario de enlace principal-detalle
La nocin de elemento actual no es slo til para la navegacin de elementos en una coleccin, sino tambin
para el escenario de enlace principal-detalle. Considere de nuevo la interfaz de usuario de aplicacin de la
seccin Qu es el enlace de datos? En esa aplicacin, la seleccin dentro de ListBox determina el contenido
mostrado en ContentControl. O dicho de otra forma, cuando se selecciona un elemento ListBox, el control
ContentControl muestra los detalles del elemento seleccionado.
Puede implementar el escenario principal-detalle simplemente con dos o ms controles enlazados a la misma
vista. En el ejemplo siguiente de Demo Data Binding se muestra el marcado de los controles ListBox y
ContentControl que aparecen en la interfaz de usuario de aplicacin en la seccin Qu es el enlace de datos?:
<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8"
ItemsSource="{Binding Source={StaticResource listingDataView}}">
</ListBox>
<ContentControl Name="Detail" Grid.Row="3" Grid.ColumnSpan="3"
Content="{Binding Source={StaticResource listingDataView}}"
ContentTemplate="{StaticResource detailsProductListingTemplate}"
Margin="9,0,0,0"/>
Observe que ambos controles estn enlazados al mismo origen, el recurso esttico listingDataView (vea la
definicin de este recurso en la seccin Cmo crear una vista). Esto funciona porque cuando un objeto singleton
(el control ContentControl en este caso) est enlazado a una vista de coleccin, se enlaza automticamente a la
propiedad CurrentItem de la vista. Observe que los objetos CollectionViewSource sincronizan automticamente
la moneda y la seleccin. Si el control de lista no est enlazado a un objeto CollectionViewSource como en este
ejemplo, tendr que establecer su propiedad IsSynchronizedWithCurrentItem en true para que funcione.
Tal vez haya observado que en el ejemplo anterior se utiliza una plantilla. De hecho, los datos no se mostraran
de la forma deseada sin el uso de plantillas (la utilizada explcitamente por el control ContentControl y la

MCT: Luis Dueas

Pag 119 de 473

Manual de Windows Presentation Foundation


utilizada implcitamente por el control ListBox). Trataremos el tema de la inclusin de datos en plantillas en la
siguiente seccin.
Inclusin de datos en plantillas
Sin el uso de plantillas de datos, nuestra interfaz de usuario de aplicacin de la seccin Qu es el enlace de
datos? tendra el aspecto siguiente:

Como se muestra en el ejemplo de la seccin anterior, los controles ListBox y ContentControl estn enlazados
al objeto de coleccin completo (o, ms concretamente, a la vista del objeto de coleccin) de AuctionItem. Sin
instrucciones especficas sobre cmo mostrar la recoleccin de datos, el control ListBox muestra una
representacin en forma de cadena de cada objeto de la coleccin subyacente y el control ContentControl
muestra una representacin en forma de cadena del objeto al que est enlazado.
Para resolver ese problema, la aplicacin define objetos DataTemplate. Como se muestra en el ejemplo de la
seccin anterior, ContentControl utiliza explcitamente el objeto detailsProductListingTemplate DataTemplate. El
control ListBox utiliza implcitamente el objeto DataTemplate siguiente al mostrar los objetos AuctionItem en la
coleccin:
<DataTemplate DataType="{x:Type src:AuctionItem}">
<Border BorderThickness="1" BorderBrush="Gray"
Padding="7" Name="border" Margin="3" Width="500">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="86"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Polygon Grid.Row="0" Grid.Column="0" Grid.RowSpan="4"
Fill="Yellow" Stroke="Black" StrokeThickness="1"
StrokeLineJoin="Round" Width="20" Height="20"
Stretch="Fill"
Points="9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7"
Visibility="Hidden" Name="star"/>
<TextBlock Grid.Row="0" Grid.Column="1" Margin="0,0,8,0"
Name="descriptionTitle"
Style="{StaticResource smallTitleStyle}">Description:</TextBlock>
<TextBlock Name="DescriptionDTDataType" Grid.Row="0" Grid.Column="2"
Text="{Binding Path=Description}"
Style="{StaticResource textStyleTextBlock}"/>
<TextBlock Grid.Row="1" Grid.Column="1" Margin="0,0,8,0"
Name="currentPriceTitle"
Style="{StaticResource smallTitleStyle}">Current Price:</TextBlock>
<StackPanel Grid.Row="1" Grid.Column="2" Orientation="Horizontal">
<TextBlock Text="$" Style="{StaticResource textStyleTextBlock}"/>
<TextBlock Name="CurrentPriceDTDataType"
Text="{Binding Path=CurrentPrice}"
Style="{StaticResource textStyleTextBlock}"/>
</StackPanel>
</Grid>
</Border>

MCT: Luis Dueas

Pag 120 de 473

Manual de Windows Presentation Foundation


<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=SpecialFeatures}">
<DataTrigger.Value>
<src:SpecialFeatures>Color</src:SpecialFeatures>
</DataTrigger.Value>
<DataTrigger.Setters>
<Setter Property="BorderBrush" Value="DodgerBlue" TargetName="border" />
<Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
<Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
<Setter Property="BorderThickness" Value="3" TargetName="border" />
<Setter Property="Padding" Value="5" TargetName="border" />
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger Binding="{Binding Path=SpecialFeatures}">
<DataTrigger.Value>
<src:SpecialFeatures>Highlight</src:SpecialFeatures>
</DataTrigger.Value>
<Setter Property="BorderBrush" Value="Orange" TargetName="border" />
<Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
<Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
<Setter Property="Visibility" Value="Visible" TargetName="star" />
<Setter Property="BorderThickness" Value="3" TargetName="border" />
<Setter Property="Padding" Value="5" TargetName="border" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Con el uso de estos dos objetos DataTemplate, la interfaz de usuario resultante es la que se muestra en la
seccin Qu es el enlace de datos? Como puede ver en esa captura de pantalla, adems de permitir colocar los
datos en los controles, los objetos DataTemplate le permiten definir un aspecto atractivo para sus datos. Por
ejemplo, se utilizan objetos DataTrigger en el objeto DataTemplate anterior para que los elementos
AuctionItem con el valor SpecialFeatures de HighLight se muestren con un borde naranja y una estrella.
Validacin de datos
La mayora de las aplicaciones que toman datos proporcionados por el usuario requieren lgica de validacin
para asegurarse de que el usuario ha escrito la informacin esperada. Las comprobaciones de validacin
pueden basarse en el tipo, intervalo, formato u otros requisitos especficos de la aplicacin. En esta seccin se
describe cmo funciona la validacin de datos en WPF.
Asociar reglas de validacin a un enlace
El modelo de enlace de datos de WPF permite asociar ValidationRules al objeto Binding. Vase, por ejemplo, el
siguiente cdigo XAML del control TextBox Add Product Listing "Start Price" de la seccin Qu es el enlace de
datos?:
<TextBox Name="StartPriceEntryForm" Grid.Row="2" Grid.Column="1"
Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5">
<TextBox.Text>
<Binding Path="StartPrice" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
La propiedad ValidationRules toma una coleccin de objetos ValidationRule. ExceptionValidationRule es un
objeto ValidationRule integrado que comprueba las excepciones producidas durante la actualizacin de la
propiedad de origen del enlace. En este ejemplo en concreto, la propiedad de origen del enlace es StartPrice
(de tipo entero) y la propiedad de destino es TextBox.Text. Cuando el usuario especifica un valor que no se
puede convertir en un entero, se produce una excepcin, lo que ocasiona que el enlace se marque como no
vlido.
Puede crear tambin su propia regla de validacin derivando de la clase ValidationRule e implementando el
mtodo Validate. En el ejemplo siguiente se muestra la regla utilizada por el control TextBox Add Product
Listing "Start Date" de la seccin Qu es un enlace de datos?:
class FutureDateRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
DateTime date;
try

MCT: Luis Dueas

Pag 121 de 473

Manual de Windows Presentation Foundation


{

date = DateTime.Parse(value.ToString());
}
catch (FormatException)
{
return new ValidationResult(false, "Value is not a valid date.");
}
if (DateTime.Now.Date > date)
{
return new ValidationResult(false, "Please enter a date in the future.");
}
else
{
return ValidationResult.ValidResult;
}
}

StartDateEntryFormTextBoxutiliza FutureDateRule, como se muestra en el ejemplo siguiente:


<TextBox Name="StartDateEntryForm" Grid.Row="3" Grid.Column="1"
Validation.ErrorTemplate="{StaticResource validationTemplate}"
Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5">
<TextBox.Text>
<Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged"
Converter="{StaticResource dateConverter}" >
<Binding.ValidationRules>
<src:FutureDateRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Observe que como el valor de UpdateSourceTrigger es PropertyChanged, el motor de enlace actualiza el valor
de origen cada vez que se pulsa una tecla, lo que significa que tambin comprueba cada regla de la coleccin
ValidationRules cada vez que se pulsa una tecla. Ofreceremos una descripcin ms detallada en la seccin
Proceso de validacin.
Proporcionar comentarios visuales
Si el usuario especifica un valor no vlido, quizs desee proporcionar algunos comentarios sobre el error en la
interfaz de usuario de la aplicacin. Una manera de proporcionar esos comentarios es establecer la propiedad
asociada Validation.ErrorTemplate en un objeto ControlTemplate personalizado. Como se muestra en el
subapartado anterior, el control TextBox StartDateEntryForm utiliza una propiedad ErrorTemplate denominada
validationTemplate. En el ejemplo siguiente se muestra la definicin de validationTemplate:
<ControlTemplate x:Key="validationTemplate">
<DockPanel>
<TextBlock Foreground="Red" FontSize="20">!</TextBlock>
<AdornedElementPlaceholder/>
</DockPanel>
</ControlTemplate>
El elemento AdornedElementPlaceholder especifica dnde se debe colocar el control que se va a adornar.
Asimismo, podra utilizar un objeto ToolTip para mostrar el mensaje de error. Los controles TextBox
StartDateEntryForm y StartPriceEntryForm utilizan el estilo textStyleTextBox, que crea un objeto ToolTip que
muestra el mensaje de error. En el ejemplo siguiente se muestra la definicin de textStyleTextBox. La
propiedad adjunta Validation.HasError es true cuando uno o varios de los enlaces de las propiedades del
elemento enlazado producen un error.
<Style x:Key="textStyleTextBox" TargetType="TextBox">
<Setter Property="Foreground" Value="#333333" />
<Setter Property="MaxLength" Value="40" />
<Setter Property="Width" Value="392" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
Con la propiedad ErrorTemplate personalizada y el objeto ToolTip, el control TextBox StartDateEntryForm
adopta el siguiente aspecto cuando se produce un error de validacin:

MCT: Luis Dueas

Pag 122 de 473

Manual de Windows Presentation Foundation


Si el objeto Binding tiene reglas de validacin asociadas, pero no especifica ErrorTemplate en el control
enlazado, se utilizar una propiedad ErrorTemplate predeterminada para informar a los usuarios de que se ha
producido un error de validacin. La propiedad ErrorTemplate predeterminada es una plantilla de control que
define un borde rojo en la capa de adorno. Con la propiedad ErrorTemplate predeterminada y el objeto ToolTip,
la interfaz de usuario del control TextBox StartPriceEntryForm adopta el siguiente aspecto cuando se produce
un error de validacin:

Proceso de validacin
La validacin normalmente se produce cuando el valor de un destino se transfiere a la propiedad de origen del
enlace. Esto se produce en los enlaces TwoWay y OneWayToSource. Como ya se ha comentado, lo que causa
una actualizacin del origen depende del valor de la propiedad UpdateSourceTrigger.
A continuacin, se describe el proceso de validacin: Tenga en cuenta que si se produce un error de validacin
o de cualquier otro tipo en cualquier momento del proceso, ste se detiene.
1.

El motor de enlace comprueba si se ha definido algn objeto ValidationRule cuya propiedad


ValidationStep se haya establecido en RawProposedValue para dicho objeto Binding, en cuyo caso llama
al mtodo Validate en cada ValidationRule hasta que una de las reglas de validacin detecte un error o
hasta que se cumplan todas las reglas de validacin.

2.

A continuacin, el motor de enlace llama al convertidor, si existe alguno.

3.

Si el convertidor funciona correctamente, el motor de enlace comprueba si se ha definido algn objeto


ValidationRule cuya propiedad ValidationStep se haya establecido en ConvertedProposedValue para dicho
objeto Binding, en cuyo caso llama al mtodo Validate en cada ValidationRule que tenga la propiedad
ValidationStep establecida en ConvertedProposedValue hasta que una de las reglas de validacin detecte
un error o hasta que se cumplan todas las reglas de validacin.

4.
5.

El motor de enlace establece la propiedad de origen.


El motor de enlace comprueba si se ha definido algn objeto ValidationRule cuya propiedad
ValidationStep se haya establecido en UpdatedValue para dicho objeto Binding, en cuyo caso llama al
mtodo Validate en cada ValidationRule que tenga la propiedad ValidationStep establecida en
UpdatedValue hasta que una de las reglas de validacin detecte un error o hasta que se cumplan todas
las reglas de validacin.

6.

El motor de enlace comprueba si se ha definido algn objeto ValidationRule cuya propiedad


ValidationStep se haya establecido en CommittedValue para dicho objeto Binding, en cuyo caso llama al
mtodo Validate en cada ValidationRule que tenga la propiedad ValidationStep establecida en
CommittedValue hasta que una de las reglas de validacin detecte un error o hasta que se cumplan todas
las reglas de validacin.

Si un objeto ValidationRule no pasa en ningn momento por este proceso, el motor de enlace crea un objeto
ValidationError y lo agrega a la coleccin Validation.Errors del elemento enlazado. Antes de que el motor de
enlace ejecute los objetos ValidationRule en un paso determinado, ste quita cualquier objeto ValidationError
que se haya agregado a la propiedad adjunta Validation.Errors del elemento enlazado durante dicho paso. Por
ejemplo, si se produce un error de un objeto ValidationRule cuya propiedad ValidationStep se ha establecido en
UpdatedValue, la prxima vez que se realice el proceso de validacin, el motor de enlace quitar dicho objeto
ValidationError inmediatamente antes de llamar a cualquier objeto ValidationRule que tenga la propiedad
ValidationStep establecida en UpdatedValue.

MCT: Luis Dueas

Pag 123 de 473

Manual de Windows Presentation Foundation


Cuando la propiedad Validation.Errors no est vaca, la propiedad adjunta Validation.HasError del elemento se
establece en true. Adems, si la propiedad NotifyOnValidationError de Binding se establece en true, el motor de
enlace provoca el evento adjunto Validation.Error en el elemento.
Asimismo, observe que una transferencia de valor vlida en cualquiera de las dos direcciones (del destino al
origen o del origen al destino) borra la propiedad adjunta Validation.Errors.
Si el enlace tiene un objeto ExceptionValidationRule asociado y se produce una excepcin cuando el motor de
enlace establece el origen, dicho motor comprueba si hay una propiedad UpdateSourceExceptionFilter. Puede
usar la devolucin de llamada de UpdateSourceExceptionFilter para proporcionar un controlador personalizado
que controle las excepciones. Si no se especifica una propiedad UpdateSourceExceptionFilter en el objeto
Binding, el motor de enlaces crea un objeto ValidationError con la excepcin y lo agrega a la coleccin
Validation.Errors del elemento enlazado.
Mecanismo de depuracin
Puede establecer la propiedad asociada PresentationTraceSources.TraceLevel en un objeto relacionado con el
enlace para recibir informacin sobre el estado de un enlace especfico.

5.1.2. Informacin General sobre Orgenes de Enlaces


En el enlace de datos, el objeto de origen de enlace (origen) hace referencia al objeto del que se obtienen los
datos. En este tema se describen los tipos de objetos que pueden utilizarse como origen.
Origen de un enlace
El enlace de datos de Windows Presentation Foundation (WPF) admite los siguientes tipos de origen de enlace:
Origen de enlace

Descripcin

Objetos common
language runtime
(CLR)

Puede enlazar a propiedades pblicas, subpropiedades e indizadores de cualquier


objeto common language runtime (CLR). El motor de enlace utiliza la reflexin
CLR para obtener los valores de las propiedades. Como alternativa, tambin
funcionan con el motor de enlace los objetos que implementan
ICustomTypeDescriptor o que tienen un TypeDescription Provider registrado.

Datos ADO.NET

Puede enlazar a objetos ADO.NET como DataTable. El objeto DataView


de ADO.NET implementa IBindingList, que proporciona notificaciones de cambios
para los que el motor de enlace realiza escuchas.

Datos XML

Puede enlazar a y ejecutar consultas XPath en XmlNode, XmlDocument o


XmlElement. Una manera cmoda de tener acceso a los datos XML que
constituyen el origen de enlace en el marcado es utilizar un objeto
XmlDataProvider.
Tambin puede enlazar a XElement o XDocument, o bien enlazar a los resultados
de consultas ejecutadas en objetos de estos tipos mediante LINQ for XML. Una
manera cmoda de tener acceso a los datos XML que constituyen el origen de
enlace en el marcado es utilizar un objeto ObjectDataProvider.

DependencyObject

Puede enlazar a
DependencyObject.

propiedades

de

dependencia

de

cualquier

objeto

Utilizar una clase CLR como objeto de origen de enlace


En esta seccin se explica lo que debe saber para implementar una clase CLR para que acte como un objeto
de origen.
Proporcionar notificaciones de cambio
Si utiliza el enlace OneWay o TwoWay (por ejemplo, si desea que la interfaz de usuario se actualice cuando
cambien dinmicamente las propiedades de origen), debe implementar un mecanismo apropiado de notificacin
de cambio de propiedad. El mecanismo recomendado es que la clase CLR implemente la interfaz
INotifyPropertyChanged.

MCT: Luis Dueas

Pag 124 de 473

Manual de Windows Presentation Foundation


Si no implementa INotifyPropertyChanged, entonces deber utilizar su propio sistema de notificacin para
asegurarse de que los datos utilizados en un enlace estn siempre actualizados. Puede proporcionar
notificaciones de cambio admitiendo el modelo PropertyChanged para cada propiedad cuyas notificaciones de
cambio desea obtener. Para admitir este modelo, se define un evento nombreDePropiedadChanged para cada
propiedad, donde nombreDePropiedad es el nombre de la propiedad. El evento se provoca cada vez que cambia
la propiedad.
Si el objeto de origen implementa un mecanismo de notificacin de cambio de propiedades apropiado, el
destino se actualiza automticamente. Si por cualquier motivo el objeto de origen no proporciona notificaciones
de cambio de propiedad correctas, puede utilizar el mtodo UpdateTarget para actualizar explcitamente la
propiedad de destino.
Otras caractersticas
En la lista siguiente se proporcionan otros puntos importantes que tener en cuenta:

Si desea crear el objeto en XAML, la clase debe tener un constructor predeterminado. En algunos
lenguajes de .NET, como C#, el constructor predeterminado puede crearse automticamente.

Las propiedades que se utilizan como propiedades de origen para un enlace deben ser propiedades
pblicas de la clase. Para los enlaces no se puede tener acceso a las propiedades de interfaz definidas
explcitamente; ni tampoco a las propiedades protegidas, privadas o virtuales que no tengan una
implementacin base.

No se puede enlazar a los campos pblicos de una clase CLR.

El tipo de la propiedad declarado en la clase es el tipo que se pasa al enlace. Sin embargo, el tipo que
el enlace utiliza en realidad depende del tipo de la propiedad del destino del enlace, no de la propiedad
de origen. Si existe alguna diferencia de tipos, puede ser conveniente escribir a un convertidor para
administrar la manera de pasar inicialmente la propiedad personalizada al enlace.

Utilizar objetos completos como origen de enlace


Puede utilizar un objeto completo como origen de un enlace. Para ello, se especifica el objeto como origen del
enlace mediante la propiedad Source o DataContext y, a continuacin, no se proporciona ninguna ruta de
acceso excepto una declaracin de enlace en blanco: {Binding}. Los escenarios en que puede resultar til esta
posibilidad son el enlace a objetos de tipo String, el enlace a objetos con varias propiedades que le interesan o
el enlace a objetos de coleccin.
Observe que puede ser necesario aplicar la lgica personalizada para que los datos sean significativos para la
propiedad de destino enlazada. La lgica personalizada puede consistir en un convertidor personalizado (si no
existe la conversin de tipos predeterminada) o en un objeto DataTemplate.
Utilizar objetos de coleccin como origen de enlace
Con frecuencia, el objeto que se desea utilizar como origen es una coleccin de varios objetos personalizados,
cada uno de los cuales representa un objeto de datos que acta como origen para una instancia de un enlace
repetido. Por ejemplo, supongamos que tiene una coleccin CustomerOrders compuesta de objetos
CustomerOrder, y que la aplicacin itera en la coleccin para determinar cuntas rdenes hay y qu datos
contiene cada una.
Es posible enumerar cualquier coleccin que implementa la interfaz IEnumerable. Sin embargo, para configurar
enlaces dinmicos de modo que las inserciones o eliminaciones que se realicen en la coleccin actualicen la
interfaz de usuario de forma automtica, la coleccin debe implementar la interfaz INotifyCollectionChanged.
Esta interfaz expone un evento que debe provocarse siempre que se realicen cambios en la coleccin
subyacente.

MCT: Luis Dueas

Pag 125 de 473

Manual de Windows Presentation Foundation


WPF proporciona la clase ObservableCollection<(Of <(T>)>), que es una implementacin integrada de una
recoleccin de datos que expone la interfaz INotifyCollectionChanged. Los objetos de datos individuales de la
coleccin deben cumplir los requisitos que se describen en las secciones anteriores. Antes de implementar su
propia coleccin, considere la posibilidad de utilizar ObservableCollection<(Of <(T>)>) o una de las clases de
coleccin existentes, como List<(Of <(T>)>), Collection<(Of <(T>)>) y BindingList<(Of <(T>)>), entre otras
muchas.
WPF nunca se enlaza directamente a una coleccin. Si se especifica una coleccin como origen de datos, WPF
se enlaza en realidad a la vista predeterminada de la coleccin.
Si cuenta con un escenario avanzado y desea implementar su propia coleccin, considere la posibilidad de
utilizar IList, que proporciona una coleccin no genrica de objetos a los que se puede obtener acceso
individualmente por ndice y, por consiguiente, proporciona el mximo rendimiento.
Requisitos de permiso
En la tabla siguiente se resume a qu tipos de propiedad se puede enlazar en una aplicacin que se ejecuta con
permisos de plena confianza o de confianza parcial:
Tipo de propiedad
(todos los modificadores
de acceso)

Propiedad
CLR

Propiedad
CLR

Propiedad de
dependencia

Dependencia
Propiedad

Nivel de confianza

Plena
confianza

Confianza
parcial

Plena confianza

Confianza
parcial

Clase pblica

Clase privada

No

En esta tabla se describen los puntos importantes siguientes sobre los requisitos de permiso en el enlace de
datos:

Para las propiedades CLR, el enlace de datos funciona siempre que el motor de enlace pueda tener
acceso a la propiedad de origen mediante la reflexin. De lo contrario, el motor de enlace emite una
advertencia que indica que no se puede buscar la propiedad y utiliza el valor de reserva o el valor
predeterminado, si est disponible.

Siempre se puede enlazar a propiedades de dependencia.

El requisito de permiso para el enlace XML es similar: en un recinto de seguridad de confianza parcial, se
produce un error de XmlDataProvider cuando no se dispone de permiso para tener acceso a los datos de que se
trate.

5.1.3. Informacin General sobre Plantillas de Datos


El modelo de plantillas de datos de WPF proporciona gran flexibilidad para definir la presentacin de los datos.
Los controles de WPF tienen funcionalidad integrada para admitir la personalizacin de la presentacin de
datos. Este tema muestra primero cmo definir un objeto DataTemplate y, a continuacin, presenta otras
caractersticas de las plantillas de datos, tales como la seleccin de plantillas en funcin de lgica personalizada
y la compatibilidad para la presentacin de datos jerrquicos.
Conceptos bsicos sobre plantillas de datos
Para mostrar por qu DataTemplate es importante, examinaremos un ejemplo de enlace de datos. En este
ejemplo, tenemos un objeto ListBox que se enlaza a una lista de objetos Task. Cada objeto Task tiene

TaskName (cadena), Description (cadena), Priority (int) y una propiedad de tipo TaskType, que es una Enum con
valores Home y Work.

MCT: Luis Dueas

Pag 126 de 473

Manual de Windows Presentation Foundation


<Window x:Class="SDKSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SDKSample"
Title="Introduction to Data Templating Sample">
<Window.Resources>
<local:Tasks x:Key="myTodoList"/>
</Window.Resources>
<StackPanel>
<TextBlock Name="blah" FontSize="20" Text="My Task List:"/>
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"/>
</StackPanel>
</Window>
Sin DataTemplate
Sin un objeto DataTemplate, nuestro objeto ListBox es similar, actualmente, a:

Lo que ocurre es que, sin ninguna instruccin concreta, el objeto ListBox llama de forma predeterminada a
ToString al intentar mostrar los objetos de la coleccin. Por consiguiente, si el objeto Task reemplaza el mtodo
ToString, el objeto ListBox muestra la representacin de cadena de cada objeto de origen de la coleccin
subyacente.
Por ejemplo, si la clase Task reemplaza el mtodo ToString de esta manera, donde name es el campo para la
propiedad TaskName:
public override string ToString()
{
return name.ToString();
}
Entonces, el objeto ListBox tiene un aspecto similar al siguiente:

Sin embargo, eso resulta limitante e inflexible. Adems, si se est enlazando a datos XML, no podra
reemplazar ToString.
Definir una plantilla de datos simple
La solucin es definir un objeto DataTemplate. Una forma de hacerlo es establecer la propiedad ItemTemplate
del objeto ListBox en un objeto DataTemplate. Lo que especifique en el objeto DataTemplate se convertir en la
estructura visual del objeto de datos. El objeto DataTemplate siguiente es bastante simple. Estamos
proporcionando instrucciones para que cada elemento aparezca como tres elementos TextBlock dentro de un
objeto StackPanel. Cada elemento TextBlock se enlaza a una propiedad de la clase Task.
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

MCT: Luis Dueas

Pag 127 de 473

Manual de Windows Presentation Foundation


Los datos subyacentes para los ejemplos de este tema son una coleccin de objetos CLR. Si est enlazando a
datos XML, los conceptos fundamentales son los mismos, pero hay una ligera diferencia sintctica. Por ejemplo,
en lugar de tener Path=TaskName, establecera XPath en @TaskName (si TaskName es un atributo del nodo
XML ).
Ahora, nuestro control ListBox tiene el aspecto siguiente:

Crear la plantilla de datos como un recurso


En el ejemplo anterior, definimos el objeto DataTemplate en lnea. Es ms comn definirlo en la seccin de
recursos para que pueda ser un objeto reutilizable, como en el ejemplo siguiente:
<Window.Resources>
<DataTemplate x:Key="myTaskTemplate">
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
Ahora puede utilizar myTaskTemplate como recurso, tal como se muestra en el ejemplo siguiente:
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"/>
Dado que myTaskTemplate es un recurso, ahora puede utilizarlo en otros controles que tengan una propiedad
que admita un tipo DataTemplate. Como antes se mostr, para objetos ItemsControl, tales como el control
ListBox, es la propiedad ItemTemplate. Para objetos ContentControl, es la propiedad ContentTemplate.
La propiedad DataType
La clase DataTemplate tiene una propiedad DataType que es muy similar a la propiedad TargetType de la clase
Style. Por consiguiente, en lugar de especificar x:Key para el objeto DataTemplate en el ejemplo anterior,
puede hacer lo siguiente:
<DataTemplate DataType="{x:Type local:Task}">
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
Este objeto DataTemplate se aplica automticamente a todos los objetos Task. Tenga en cuenta que, en este
caso, x:Key se establece implcitamente. Por consiguiente, si asigna un valor x:Key a este objeto
DataTemplate, estar reemplazando el valor implcito de x:Key y el objeto DataTemplate no se aplicar
automticamente.
Si est enlazando un objeto ContentControl a una coleccin de los objetos Task, el objeto ContentControl no
utiliza automticamente el objeto DataTemplate anterior. Esto se debe a que el enlace en un control
ContentControl necesita ms informacin para poder distinguir si el usuario desea enlazar a una coleccin
completa o a los objetos individuales. Si el control ContentControl realiza un seguimiento de la seleccin de un
tipo de ItemsControl, puede establecer la propiedad Path del enlace de ContentControl en "/" para indicar que
le interesa el elemento actual. De lo contrario, debe especificar explcitamente el objeto DataTemplate
estableciendo la propiedad ContentTemplate.

MCT: Luis Dueas

Pag 128 de 473

Manual de Windows Presentation Foundation


La propiedad DataType resulta particularmente til en el caso de una coleccin CompositeCollection de
diferentes tipos de objetos de datos.
Agregar a la plantilla de datos
Actualmente, los datos aparecen con la informacin necesaria, pero est claro que hay espacio para la mejora.
Podemos mejorar la presentacin agregando un elemento Border, un elemento Grid y algunos elementos
TextBlock que describan los datos que se muestran.
<DataTemplate x:Key="myTaskTemplate">
<Border Name="border" BorderBrush="Aqua" BorderThickness="1" Padding="5" Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Task Name:"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=TaskName}" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Description:"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Priority:"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Priority}"/>
</Grid>
</Border>
</DataTemplate>
La captura de pantalla siguiente muestra el control ListBox con este objeto DataTemplatemodificado:

Podemos establecer HorizontalContentAlignment en Stretch en el control ListBox para asegurarse de que el


ancho de los elementos ocupe todo el espacio:
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"
HorizontalContentAlignment="Stretch"/>
Con la propiedad HorizontalContentAlignment establecida en Stretch, el control ListBox tiene ahora este
aspecto:

Utilizar DataTriggers para aplicar valores de propiedad


La presentacin actual no nos indica si Task es una tarea domstica o una tarea de oficina. Recuerde que el
objeto Task tiene una propiedad TaskType de tipo TaskType, que es una enumeracin con valores Home y
Work.
En el ejemplo siguiente, el objeto DataTrigger establece el valor de BorderBrush del elemento denominado
border en Yellow si la propiedad TaskType es TaskType.Home.

MCT: Luis Dueas

Pag 129 de 473

Manual de Windows Presentation Foundation


<DataTemplate x:Key="myTaskTemplate">
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=TaskType}">
<DataTrigger.Value>
<local:TaskType>Home</local:TaskType>
</DataTrigger.Value>
<Setter TargetName="border" Property="BorderBrush" Value="Yellow"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Ahora, nuestra aplicacin tiene el aspecto siguiente. Las tareas domsticas aparecen con un borde amarillo y
las tareas de oficina con un borde aguamarina:

En este ejemplo, el objeto DataTrigger utiliza un objeto Setter para establecer un valor de propiedad. Las clases
de desencadenador tambin tienen propiedades EnterActions y ExitActions, que permiten iniciar un conjunto de
acciones tales como animaciones. Adems, tambin hay una clase MultiDataTrigger que permite aplicar
cambios en funcin de varios valores de propiedad enlazados a datos.
Una manera alternativa de lograr el mismo efecto es enlazar la propiedad BorderBrush a la propiedad TaskType
y utilizar un convertidor de valores para devolver el color en funcin del valor TaskType. La creacin del efecto
anterior mediante un convertidor es ligeramente ms eficaz por lo que se refiere a rendimiento. Adems, la
creacin de un convertidor propio ofrece ms flexibilidad, dado que se proporciona lgica propia. En ltimo
trmino, la tcnica que elija depender del escenario y de sus preferencias.
Cules son los elementos de una plantilla de datos?
En el ejemplo anterior, colocamos el desencadenador dentro del objeto DataTemplate utilizando la propiedad
DataTemplate.Triggers El objeto Setter del desencadenador establece el valor de una propiedad de un elemento
(el elemento Border) que se encuentra dentro del objeto DataTemplate. Sin embargo, si las propiedades que
afectan a Setters no son propiedades de elementos que estn dentro del objeto DataTemplate actual, puede
que sea ms conveniente establecer las propiedades mediante un objeto Style para la clase (si el control que
est enlazando es un control ListBox) ListBoxItem. Por ejemplo, si desea que el objeto Trigger anime el valor
Opacity del elemento cuando el mouse seale a un elemento, defina desencadenadores dentro de un estilo
ListBoxItem.
En general, tenga en cuenta que el objeto DataTemplate se aplica a cada uno de los elementos ListBoxItem
generados (para obtener ms informacin sobre cmo y donde se aplica realmente, vea la pgina
ItemTemplate). El objeto DataTemplate solamente est relacionado con la presentacin y la apariencia de los
objetos de datos. En la mayora de los casos, todos los dems aspectos de la presentacin, tales como el
aspecto que tiene un elemento cuando se selecciona o cmo dispone los elementos el control ListBox, no
pertenecen a la definicin de un objeto DataTemplate.
Elegir una plantilla de datos en funcin de propiedades del objeto de datos
En la seccin La propiedad DataType, explicamos que puede definir diferentes plantillas de datos para
diferentes objetos de datos. Esto es especialmente til cuando se tiene una coleccin CompositeCollection de
diferentes tipos o colecciones con elementos de diferentes tipos. En la seccin Utilizar DataTriggers para aplicar
valores de propiedad hemos mostrado que si se tiene una coleccin del mismo tipo de objetos de datos se
puede crear un objeto DataTemplate y, a continuacin, utilizar desencadenadores para aplicar cambios en

MCT: Luis Dueas

Pag 130 de 473

Manual de Windows Presentation Foundation


funcin de los valores de propiedad de cada objeto de datos. Sin embargo, los desencadenadores permiten
aplicar valores de propiedad o iniciar animaciones, pero no ofrecen la flexibilidad necesaria para reconstruir la
estructura de los objetos de datos. Algunos escenarios pueden exigirle que cree un objeto DataTemplate
diferente para objetos de datos que sean del mismo tipo pero tengan propiedades diferentes.
Por ejemplo, cuando un objeto Task tiene un valor Priority de 1, quiz desee darle una apariencia
completamente diferente para actuar como una alerta para usted. En ese caso, debe crear un objeto
DataTemplate para la presentacin de los objetos Task prioritarios. Agreguemos el siguiente objeto
DataTemplate a la seccin de recursos:
<DataTemplate x:Key="importantTaskTemplate">
<DataTemplate.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="20"/>
</Style>
</DataTemplate.Resources>
<Border Name="border" BorderBrush="Red" BorderThickness="1"
Padding="5" Margin="5">
<DockPanel HorizontalAlignment="Center">
<TextBlock Text="{Binding Path=Description}" />
<TextBlock>!</TextBlock>
</DockPanel>
</Border>
</DataTemplate>
Observe que este ejemplo utiliza la propiedad DataTemplate.Resources Los recursos definidos en esa seccin
son compartidos por los elementos del objeto DataTemplate.
Para proporcionar lgica que permita de elegir qu objeto DataTemplate se debe utilizar en funcin del valor
Priority del objeto de datos, cree una subclase de DataTemplateSelector e invalide el mtodo SelectTemplate.
En el ejemplo siguiente, el mtodo SelectTemplate proporciona lgica para devolver la plantilla adecuada en
funcin del valor de la propiedad Priority. La plantilla que se devuelve se encuentra en los recursos del
elemento Window envolvente.
using System.Windows;
using System.Windows.Controls;
namespace SDKSample
{
public class TaskListDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate
SelectTemplate(object item, DependencyObject container)
{
if (item != null && item is Task)
{
Task taskitem = item as Task;
Window window = Application.Current.MainWindow;
if (taskitem.Priority == 1)
return window.FindResource("importantTaskTemplate") as DataTemplate;
else
return window.FindResource("myTaskTemplate") as DataTemplate;
}

}
return null;

}
}
Podemos declarar entonces TaskListDataTemplateSelector como recurso:
<Window.Resources>
<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>
</Window.Resources>
Para utilizar el recurso selector de plantillas, asgnelo a la propiedad ItemTemplateSelector del objeto ListBox.
El objeto ListBox llama al mtodo SelectTemplate de TaskListDataTemplateSelector para cada uno de los
elementos de la coleccin subyacente. La llamada pasa el objeto de datos como parmetro de elemento. El
objeto DataTemplate devuelto por el mtodo se aplica a continuacin a ese objeto de datos.
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
HorizontalContentAlignment="Stretch"/>
Con el selector de la plantilla en su lugar, el control ListBox aparece ahora como sigue:

MCT: Luis Dueas

Pag 131 de 473

Manual de Windows Presentation Foundation

Con esto concluye la explicacin de este ejemplo.


Estilos y plantillas para ItemsControl
Aunque el control ItemsControl no es el nico tipo de control con el que se puede utilizar un control
DataTemplate, es un escenario muy comn para enlazar un control ItemsControl a una coleccin. En la seccin
Cules son los elementos de una plantilla de datos? explicamos que la definicin del objeto DataTemplate slo
debe tener relacin con la presentacin de datos. Para saber cundo no es conveniente utilizar un objeto
DataTemplate, es importante entender las diferentes propiedades de estilo y de plantilla que proporciona el
control ItemsControl. El ejemplo siguiente se ha diseado para mostrar la funcin de cada una de estas
propiedades. El control ItemsControl de este ejemplo est enlazado a la misma coleccin Tasks que en el
ejemplo anterior. A efectos de la demostracin, los estilos y las plantillas del ejemplo estn todos declarados en
el propio cdigo.
<ItemsControl Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}">
<!--The ItemsControl has no default visual appearance.
Use the Template property to specify a ControlTemplate to define
the appearance of an ItemsControl. The ItemsPresenter uses the specified
ItemsPanelTemplate (see below) to layout the items. If an
ItemsPanelTemplate is not specified, the default is used. (For ItemsControl,
the default is an ItemsPanelTemplate that specifies a StackPanel.-->
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Border BorderBrush="Aqua" BorderThickness="1" CornerRadius="15">
<ItemsPresenter/>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<!--Use the ItemsPanel property to specify an ItemsPanelTemplate
that defines the panel that is used to hold the generated items.
In other words, use this property if you want to affect
how the items are laid out.-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!--Use the ItemTemplate to set a DataTemplate to define
the visualization of the data objects. This DataTemplate
specifies that each data object appears with the Proriity
and TaskName on top of a silver ellipse.-->
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="18"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataTemplate.Resources>
<Grid>
<Ellipse Fill="Silver"/>
<StackPanel>
<TextBlock Margin="3,3,3,0" Text="{Binding Path=Priority}"/>

MCT: Luis Dueas

Pag 132 de 473

Manual de Windows Presentation Foundation


<TextBlock Margin="3,0,3,7" Text="{Binding Path=TaskName}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<!--Use the ItemContainerStyle property to specify the appearance
of the element that contains the data. This ItemContainerStyle
gives each item container a margin and a width. There is also
a trigger that sets a tooltip that shows the description of
the data object when the mouse hovers over the item container.-->
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Control.Width" Value="100"/>
<Setter Property="Control.Margin" Value="5"/>
<Style.Triggers>
<Trigger Property="Control.IsMouseOver" Value="True">
<Setter Property="Control.ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=Content.Description}"/>
</Trigger>
</Style.Triggers>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
A continuacin se muestra una captura de pantalla del ejemplo cuando se representa:

Observe que en lugar de utilizar ItemTemplate, puede utilizar ItemTemplateSelector. Consulte la seccin
anterior para ver un ejemplo. De igual forma, en lugar de utilizar ItemContainerStyle, tiene la opcin de utilizar
ItemContainerStyleSelector.
Otras dos propiedades relacionadas con el estilo del control ItemsControl que no se muestran aqu son
GroupStyle y GroupStyleSelector.
Compatibilidad con datos jerrquicos
Hasta ahora solamente hemos examinado cmo enlazar y mostrar una nica coleccin. En ocasiones tendr
colecciones que contengan otras colecciones. La clase HierarchicalDataTemplate se ha diseado para utilizarla
con tipos HeaderedItemsControl para mostrar tales datos. En el ejemplo siguiente, ListLeagueList es una lista
de objetos League. Cada objeto League tiene un Name y una coleccin de objetos Division. Cada Division tiene
un Name y una coleccin de objetos Team, y cada objeto Team tiene un Name.
<Window x:Class="SDKSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="HierarchicalDataTemplate Sample"
xmlns:src="clr-namespace:SDKSample">
<DockPanel>
<DockPanel.Resources>
<src:ListLeagueList x:Key="MyList"/>
<HierarchicalDataTemplate DataType
= "{x:Type src:League}"
ItemsSource = "{Binding Path=Divisions}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType
= "{x:Type src:Division}"
ItemsSource = "{Binding Path=Teams}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type src:Team}">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</DockPanel.Resources>
<Menu Name="menu1" DockPanel.Dock="Top" Margin="10,10,10,10">
<MenuItem Header="My Soccer Leagues"
ItemsSource="{Binding Source={StaticResource MyList}}" />
</Menu>
<TreeView>
<TreeViewItem ItemsSource="{Binding Source={StaticResource MyList}}"
Header="My Soccer Leagues" />
</TreeView>
</DockPanel>
</Window>

MCT: Luis Dueas

Pag 133 de 473

Manual de Windows Presentation Foundation


En el ejemplo se muestra que, con el uso de HierarchicalDataTemplate, es fcil mostrar datos de listas que
contengan otras listas. A continuacin, se muestra una captura de pantalla del ejemplo.

5.1.4. Informacin General sobre Declaraciones de Enlaces


En este tema se describen las distintas maneras de declarar un enlace.
Declarar un enlace en XAML
En esta seccin se explica cmo declarar un enlace en Lenguaje de marcado de aplicaciones extensible (XAML).
Uso de extensiones de marcado
Binding es una extensin de marcado. Cuando se utiliza la extensin de enlace para declarar un enlace, la
declaracin consiste en una serie de clusulas que se sitan tras la palabra clave Binding y estn separadas por
comas (,). Las clusulas de la declaracin de enlace pueden estar en cualquier orden y hay muchas
combinaciones posibles. Las clusulas son pares de valores Nombre=Valor, donde Nombre es el nombre de la
propiedad Binding y Valor es el valor que se establece para la propiedad.
Al crear cadenas de declaracin de enlace en el marcado, deben asociarse a la propiedad de dependencia
concreta de un objeto de destino. En el ejemplo siguiente se muestra cmo enlazar la propiedad TextBox.Text
mediante la extensin de enlace, especificando las propiedades Source, Path y UpdateSourceTrigger.
<TextBlock Text="{Binding Source={StaticResource myDataSource}, Path=PersonName}"/>
Puede especificar la mayora de las propiedades de la clase Binding de este modo.
Sintaxis de elementos de objeto
La sintaxis de elementos de objeto es una alternativa a la creacin de la declaracin de enlace. En la mayora
de los casos, utilizar la extensin de marcado o la sintaxis de elementos de objeto no aporta ninguna ventaja
concreta. Sin embargo, en aquellos casos en que la extensin de marcado no admite el escenario, como cuando
el tipo del valor de la propiedad no es de cadena y no existe ninguna conversin de tipos para l, es preciso
utilizar la sintaxis de elementos de objeto.
A continuacin, se muestra un ejemplo de sintaxis de elementos de objeto y de uso de la extensin de
marcado:
<TextBlock Name="myconvertedtext"
Foreground="{Binding Path=TheDate,
Converter={StaticResource MyConverterReference}}">
<TextBlock.Text>
<Binding Path="TheDate"
Converter="{StaticResource MyConverterReference}"/>
</TextBlock.Text>
</TextBlock>
En el ejemplo se enlaza la propiedad Foreground declarando un enlace mediante la sintaxis de extensiones. La
declaracin de enlace de la propiedad Text utiliza la sintaxis de elementos de objeto.
MultiBinding y PriorityBinding

MCT: Luis Dueas

Pag 134 de 473

Manual de Windows Presentation Foundation


MultiBinding y PriorityBinding no admiten la sintaxis de extensiones XAML. Por consiguiente, debe utilizar la
sintaxis de elementos de objeto para declarar MultiBinding o PriorityBinding en XAML.
Crear un enlace mediante cdigo
Otra manera de especificar un enlace consiste en establecer directamente las propiedades de un objeto Binding
mediante cdigo. En el ejemplo siguiente se muestra cmo crear un objeto Binding y especificar las
propiedades en cdigo:
private void OnPageLoaded(object sender, EventArgs e)
{
// Make a new source, to grab a new timestamp
MyData myChangedData = new MyData();
// Create a new binding
// TheDate is a property of type DateTime on MyData class
Binding myNewBindDef = new Binding("TheDate");
myNewBindDef.Mode = BindingMode.OneWay;
myNewBindDef.Source = myChangedData;
myNewBindDef.Converter = TheConverter;
myNewBindDef.ConverterCulture = new CultureInfo("en-US");
// myDatetext is a TextBlock object that is the binding target object
BindingOperations.SetBinding(myDateText, TextBlock.TextProperty, myNewBindDef);
BindingOperations.SetBinding(myDateText, TextBlock.ForegroundProperty, myNewBindDef);
}
Si el objeto que se enlaza es FrameworkElement o FrameworkContentElement, puede llamar directamente al
mtodo SetBinding del objeto en lugar de utilizar BindingOperations.SetBinding.
Sintaxis de ruta de acceso enlace
Utilice la propiedad Path para especificar el valor del origen al que desea enlazar:

En el caso ms simple, el valor de la propiedad Path es el nombre de la propiedad del objeto de origen
que se usar para el enlace, como Path=PropertyName.

Las subpropiedades de una propiedad se pueden especificar mediante una sintaxis parecida, como en
C#. Por ejemplo, la clusula Path=ShoppingCart.Order establece el enlace a la subpropiedad Order del
objeto o la propiedad ShoppingCart.

Para enlazar a una propiedad asociada, coloque la propiedad asociada entre parntesis. Por ejemplo,
para enlazar a la propiedad asociada DockPanel.Dock, la sintaxis es Path=(DockPanel.Dock).

Los indizadores de una propiedad se pueden especificar entre corchetes despus del nombre de la
propiedad donde se aplica el indizador. Por ejemplo, la clusula Path=ShoppingCart[0] establece el
enlace al ndice que corresponde a cmo se administra la cadena literal "0" en la indizacin interna de
la propiedad. Tambin se admiten indizadores anidados.

Los indizadores y las subpropiedades se pueden mezclar en una clusula Path; por ejemplo,
Path=ShoppingCart.ShippingInfo[MailingAddress,Street].

Dentro de los indizadores puede tener varios parmetros de indizador separados por comas (,). El tipo
de

cada

parmetro

se

puede

especificar

con

parntesis.

Por

ejemplo,

puede

tener

Path="[(sys:Int32)42,(sys:Int32)24]", donde sys se asigna al espacio de nombres System.

Cuando el origen es una vista de coleccin, el elemento actual puede especificarse con una barra
diagonal (/). Por ejemplo, la clusula Path=/ establece el enlace en el elemento actual de la vista.
Cuando el origen es una coleccin, esta sintaxis especifica el elemento actual de la vista de coleccin
predeterminada.

Se pueden combinar nombres de propiedad y barras diagonales para recorrer las propiedades que son
colecciones. Por ejemplo, Path=/Offices/ManagerName especifica el elemento actual de la coleccin de
origen, que contiene una propiedad Offices que tambin es una coleccin. Su elemento actual es un
objeto que contiene una propiedad ManagerName.

MCT: Luis Dueas

Pag 135 de 473

Manual de Windows Presentation Foundation

Opcionalmente, se puede usar una ruta de acceso con punto (.) para enlazar al origen actual. Por
ejemplo, Text={Binding} es equivalente a Text={Binding Path=.}.

Mecanismo de escape

Dentro de los indizadores ([ ]), el carcter de intercalacin (^) es el carcter de escape para el
carcter siguiente.

Si establece Path en XAML, tambin deber marcar con caracteres de escape (mediante entidades
XML) algunos caracteres especiales del analizador XML:

Utilice &amp; como secuencia de escape para el carcter "&".

Utilice &gt; como secuencia de escape de la etiqueta de cierre">".

Adems, si describe el enlace completo en un atributo utilizando la sintaxis de extensin de marcado,


deber utilizar un mecanismo de escape (usando la barra diagonal inversa \) para los caracteres
especiales del analizador de extensin de marcado de WPF:

La barra diagonal inversa (\) es el propio carcter de escape.


El signo igual (=) separa el nombre de propiedad del valor de la propiedad.
La coma (,) separa las propiedades.
La llave de cierre (}) es el fin de una extensin de marcado.

Comportamientos predeterminados
El comportamiento predeterminado el siguiente si no se especifica en la declaracin.

Se crea un convertidor predeterminado que intenta hacer una conversin de tipos entre el valor del
origen de enlace y el valor del destino de enlace. Si no se puede realizar una conversin, el convertidor
predeterminado devuelve null.

Si no se establece ConverterCulture, el motor de enlace utiliza la propiedad Language del objeto de


destino de enlace. En XAML, el valor predeterminado se establece en "en-US" o se hereda del
elemento raz (o de cualquier elemento) de la pgina, si se ha establecido explcitamente.

Siempre que el enlace tenga ya un contexto de datos (por ejemplo, el contexto de datos heredado
procedente de un elemento primario), y que el elemento o la coleccin que ese contexto devuelva sea
adecuado para el enlace sin requerir ninguna modificacin ulterior de la ruta, una declaracin de
enlace puede no tener ninguna clusula: {Binding}. Con frecuencia, los enlaces para estilos de datos
se especifican de este modo, donde el enlace acta en una coleccin.

El valor predeterminado de la propiedad Mode puede ser unidireccional y bidireccional, segn la


propiedad de dependencia que se enlace. Siempre puede declarar explcitamente el modo de enlace,
para asegurarse de que su comportamiento sea el deseado. En general, las propiedades de control que
puede modificar el usuario, como TextBox.Text y RangeBase.Value, tienen como valor predeterminado
enlaces bidireccionales, mientras que la mayora de las dems propiedades tienen como valor
predeterminado enlaces unidireccionales.

El valor predeterminado de UpdateSourceTrigger vara entre PropertyChanged y LostFocus, tambin


segn la propiedad de dependencia enlazada. El valor predeterminado de la mayora de las
propiedades de dependencia es PropertyChanged, mientras que la propiedad TextBox.Text tiene un
valor predeterminado de LostFocus.

5.1.5. Temas Cmo sobre Enlace de Datos


En los temas de esta seccin se describe cmo utilizar el enlace de datos para enlazar elementos a los datos
procedentes de diversos orgenes de datos en forma de objetos de Common Language Runtime (CLR) y XML.

MCT: Luis Dueas

Pag 136 de 473

Manual de Windows Presentation Foundation

5.1.5.1. Cmo: Crear un Enlace Sencillo


En este ejemplo se muestra cmo crear un Binding sencillo.
Ejemplo
En este ejemplo, tenemos un objeto Person con una propiedad de cadena denominada PersonName. El objeto
Person se define en el espacio de nombres denominado SDKSample.
En el ejemplo siguiente se crea una instancia del objeto Person con un valor de la propiedad PersonName de
Joe. Esto se hace en la seccin Resources y se le asigna x:Key.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:SDKSample"
SizeToContent="WidthAndHeight"
Title="Simple Data Binding Sample">
<Window.Resources>
<src:Person x:Key="myDataSource" PersonName="Joe"/>
</Window.Resources>
</Window>
Para enlazar la propiedad PersonName, se hace lo siguiente:
<TextBlock Text="{Binding Source={StaticResource myDataSource}, Path=PersonName}"/>
Como resultado, TextBlock aparece con el valor "Joe".

5.1.5.2. Cmo: Especificar el Origen de Enlace


En el enlace de datos, el objeto de origen de enlace hace referencia al objeto del que se obtienen los datos. En
este tema se describen las distintas maneras de especificar el origen de enlace.
Ejemplo
Para enlazar varias propiedades a un mismo origen, utilice la propiedad DataContext, que proporciona una
manera cmoda de establecer un mbito dentro del cual todas las propiedades enlazadas a datos heredan un
origen comn.
En el ejemplo siguiente, el contexto de datos se establece en el elemento raz de la aplicacin. Esto permite que
todos los elementos secundarios hereden ese contexto de datos. Los datos para el enlace proceden de una
clase de datos personalizada, NetIncome, a la que se hace referencia directamente mediante una asignacin
que recibe la clave de recurso de incomeDataSource.
<Grid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.DirectionalBinding"
xmlns:c="clr-namespace:SDKSample"
Name="Page1">
<Grid.Resources>
<c:NetIncome x:Key="incomeDataSource"/>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Padding" Value="8"/>
</Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Margin" Value="0,6,0,0"/>
</Style>
</Grid.Resources>
<Grid.DataContext>
<Binding Source="{StaticResource incomeDataSource}"/>
</Grid.DataContext>
</Grid>
Nota:
En el ejemplo anterior se crea una instancia del objeto en marcado y se utiliza como recurso. Si desea
enlazar a un objeto del que ya se ha creado una instancia en el cdigo, deber establecer la propiedad
DataContext mediante programacin.
Como alternativa, si desea especificar explcitamente el origen en los enlaces individuales, dispone de las
opciones siguientes. stas tienen prioridad sobre el contexto de datos heredado.

MCT: Luis Dueas

Pag 137 de 473

Manual de Windows Presentation Foundation

Propiedad

Descripcin

Source

Esta propiedad se utiliza para establecer el origen en una instancia de un objeto. Si no


necesita la funcionalidad de establecer un mbito en el que varias propiedades hereden
el mismo contexto de datos, puede utilizar la propiedad Source en lugar de la propiedad
DataContext.

RelativeSource

Resulta til si desea especificar el origen con relacin a la ubicacin donde se encuentra
el destino de enlace. Algunos escenarios comunes donde puede utilizar esta propiedad
son aqullos en los que desea enlazar una propiedad del elemento a otra propiedad del
mismo elemento o en los que se define un enlace en un estilo o una plantilla.

ElementName

Especifica una cadena que representa el elemento que se desea enlazar. Resulta til si
desear enlazar la propiedad de otro elemento de la aplicacin. Por ejemplo, si desea
utilizar un control Slider para controlar el alto de otro control de la aplicacin, o si desea
enlazar la propiedad Content del control a la propiedad SelectedValue del control
ListBox.

5.1.5.3. Cmo: Hacer que los Datos estn Disponibles para el Enlace en XAML
En este tema se explican maneras diferentes de hacer que los datos estn disponibles para el enlace en
Lenguaje de marcado de aplicaciones extensible (XAML), dependiendo de las necesidades de la aplicacin.
Ejemplo
Si tiene un objeto common language runtime (CLR) al que desea enlazar en XAML, una manera de hacer que
est disponible para el enlace es definirlo como recurso y asignarle una clave x:Key. En el ejemplo siguiente,
tenemos un objeto Person con una propiedad de cadena denominada PersonName. El objeto Person se define
en el espacio de nombres denominado SDKSample.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:SDKSample"
SizeToContent="WidthAndHeight"
Title="Simple Data Binding Sample">
<Window.Resources>
<src:Person x:Key="myDataSource" PersonName="Joe"/>
</Window.Resources>
A continuacin, puede enlazar al objeto en XAML, como se muestra en el ejemplo siguiente.
<TextBlock Text="{Binding Source={StaticResource myDataSource}, Path=PersonName}"/>
Como alternativa, puede utilizar la clase ObjectDataProvider, como en el ejemplo siguiente.
<ObjectDataProvider x:Key="myDataSource" ObjectType="{x:Type src:Person}">
<ObjectDataProvider.ConstructorParameters>
<system:String>Joe</system:String>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
El enlace se define de la misma manera:
<TextBlock Text="{Binding Source={StaticResource myDataSource}, Path=PersonName}"/>
En este ejemplo concreto, el resultado es el mismo: dispone de TextBlock con el contenido de texto Joe. Sin
embargo, la clase ObjectDataProvider proporciona funcionalidad que permite enlazar al resultado de un
mtodo. Puede optar por utilizar la clase ObjectDataProvider si necesita la funcionalidad que proporciona.
Sin embargo, si el enlace se va a establecer a un objeto que ya se ha creado, deber establecer DataContext
mediante cdigo, como en el ejemplo siguiente.
DataSet myDataSet;
private void OnInit(object sender, EventArgs e)
{
string mdbFile = Path.Combine(AppDataPath, "BookData.mdb");
string connString = string.Format(
"Provider=Microsoft.Jet.OLEDB.4.0; Data Source={0}", mdbFile);
OleDbConnection conn = new OleDbConnection(connString);
OleDbDataAdapter adapter = new OleDbDataAdapter("SELECT * FROM BookTable;", conn);
myDataSet = new DataSet();
adapter.Fill(myDataSet, "BookTable");
// myListBox is a ListBox control.

MCT: Luis Dueas

Pag 138 de 473

Manual de Windows Presentation Foundation


// Set the DataContext of the ListBox to myDataSet
myListBox.DataContext = myDataSet;
}

5.1.5.4. Cmo: Controlar Cundo el Texto de TextBox Actualiza el Origen


En este tema se describe cmo utilizar la propiedad UpdateSourceTrigger para controlar el control de tiempo de
las actualizaciones del origen de enlace. En el tema se utiliza el control TextBox como ejemplo.
Ejemplo
El valor predeterminado de UpdateSourceTrigger de la propiedad TextBox.Text es LostFocus. Esto significa que
si una aplicacin tiene un objeto TextBox con una propiedad TextBox.Text enlazada a datos, el siguiente texto
que se escriba en TextBox no actualizar el origen de enlace hasta que TextBox pierda el foco (por ejemplo,
cuando haga clic fuera de TextBox).
Si desea que el origen se actualice a medida que se escribe, debe establecer la propiedad UpdateSourceTrigger
del enlace en PropertyChanged. En el ejemplo siguiente, las propiedades Text de TextBox y de TextBlock estn
enlazadas a la misma propiedad de origen. La propiedad UpdateSourceTrigger del enlace de TextBox est
establecida en PropertyChanged.
<Label>Enter a Name:</Label>
<TextBox>
<TextBox.Text>
<Binding Source="{StaticResource myDataSource}" Path="Name"
UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
<Label>The name you entered:</Label>
<TextBlock Text="{Binding Source={StaticResource myDataSource}, Path=Name}"/>
Como resultado, TextBlock muestra el mismo texto (dado que el origen cambia) a medida que el usuario
escribe texto en TextBox, como se muestra en la captura de pantalla siguiente del ejemplo:

Si tiene un cuadro de dilogo o un formulario modificable por el usuario y desea diferir las actualizaciones del
origen hasta que el usuario haya terminado de modificar los campos y haga clic en "Aceptar", puede establecer
el valor de UpdateSourceTrigger de los enlaces en Explicit, como en el ejemplo siguiente:
<TextBox Name="itemNameTextBox"
Text="{Binding Path=ItemName, UpdateSourceTrigger=Explicit}" />
Cuando se establece el valor de UpdateSourceTrigger en Explicit, el valor del origen cambia nicamente cuando
la aplicacin llama al mtodo UpdateSource. En el siguiente ejemplo se muestra cmo llamar a UpdateSource
para itemNameTextBox.
Me.itemNameTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource()
Me.bidPriceTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource()
// itemNameTextBox is an instance of a TextBox
BindingExpression be =
itemNameTextBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
Nota:
Puede utilizar la misma tcnica para las propiedades de otros controles, pero debe tener presente que el
valor predeterminado de UpdateSourceTrigger de la mayora de las dems propiedades es
PropertyChanged.
Nota:

MCT: Luis Dueas

Pag 139 de 473

Manual de Windows Presentation Foundation

La propiedad UpdateSourceTrigger se encarga de las actualizaciones del origen y, por consiguiente,


nicamente es pertinente para los enlaces TwoWay o OneWay. Para que los enlaces TwoWay y OneWay
funcionen, el objeto de origen debe proporcionar notificaciones de cambio de propiedad.

5.1.5.5. Cmo: Especificar la Direccin del Enlace


En este ejemplo se muestra cmo especificar si el enlace actualizar nicamente la propiedad de destino de
enlace (destino), la propiedad de origen de enlace (origen) o ambas.
Ejemplo
Se utiliza la propiedad Mode para especificar la direccin del enlace. En la lista de enumeraciones siguiente se
muestran las opciones disponibles para las actualizaciones de enlace:

TwoWay actualiza la propiedad de destino o de origen cada vez que cambia la propiedad de destino o
de origen.

OneWay actualiza la propiedad de destino nicamente cuando cambia la propiedad de origen.


OneTime actualiza nicamente la propiedad de destino cuando se inicia la aplicacin o cuando
DataContext sufre un cambio.

OneWayToSource actualiza la propiedad de origen cuando cambia la propiedad de destino.


Default hace que se utilice el valor de Mode predeterminado de la propiedad de destino.

En el ejemplo siguiente se muestra cmo establecer la propiedad Mode.


<TextBlock Name="IncomeText" Grid.Row="0" Grid.Column="1"
Text="{Binding Path=TotalIncome, Mode=OneTime}"/>
Para detectar los cambios en el origen (aplicables a los enlaces OneWay y TwoWay), el origen debe
implementar un mecanismo apropiado de notificacin de cambios de propiedades, como INotifyProperty
Changed.
Para los enlaces TwoWay o OneWayToSource, puede controlar el momento en que se producen las
actualizaciones del origen estableciendo la propiedad UpdateSourceTrigger.

5.1.5.6. Cmo: Enlazar a una Coleccin y Mostrar Informacin Basada en la


Seleccin
En un escenario principal-detalle simple, hay un objeto ItemsControl enlazado a datos, como un control ListBox.
Basndose en la seleccin del usuario, se muestra ms informacin sobre el elemento seleccionado. En este
ejemplo se muestra cmo implementar este escenario.
Ejemplo
En este ejemplo, People es una coleccin ObservableCollection<(Of <(T>)>) de clases Person. Esta clase
Person contiene tres propiedades: FirstName, LastName y HomeTown, todas de tipo string.
<Window x:Class="SDKSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SDKSample"
Title="Binding to a Collection"
SizeToContent="WidthAndHeight">
<Window.Resources>
<local:People x:Key="MyFriends"/>
</Window.Resources>
<StackPanel>
<TextBlock FontFamily="Verdana" FontSize="11"
Margin="5,15,0,10" FontWeight="Bold">My Friends:</TextBlock>
<ListBox Width="200" IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource MyFriends}}"/>
<TextBlock FontFamily="Verdana" FontSize="11"
Margin="5,15,0,5" FontWeight="Bold">Information:</TextBlock>
<ContentControl Content="{Binding Source={StaticResource MyFriends}}"
ContentTemplate="{StaticResource DetailTemplate}"/>
</StackPanel>
</Window>

MCT: Luis Dueas

Pag 140 de 473

Manual de Windows Presentation Foundation


ContentControl utiliza la plantilla de datos DataTemplate siguiente, que define cmo se presenta la informacin
de Person:
<DataTemplate x:Key="DetailTemplate">
<Border Width="300" Height="100" Margin="20"
BorderBrush="Aqua" BorderThickness="1" Padding="8">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="First Name:"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=FirstName}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Last Name:"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=LastName}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Home Town:"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=HomeTown}"/>
</Grid>
</Border>
</DataTemplate>
A continuacin, se muestra una captura de pantalla de lo que el ejemplo genera. ContentControl muestra las
dems propiedades de la persona seleccionada.

Los dos puntos importantes de este ejemplo son:


1.

ListBox y ContentControl se enlazan al mismo origen. No se especifican las propiedades Path de ambos
enlaces porque los dos controles se enlazan al objeto de coleccin completo.

2.

Para que funcione, debe establecer la propiedad IsSynchronizedWithCurrentItem en true. Al establecer


esta propiedad se garantiza que el elemento seleccionado se establezca siempre como CurrentItem.
Como alternativa, si el control ListBox obtiene sus datos de un objeto CollectionViewSource, sincronizar
automticamente la seleccin y la actualizacin.

Observe que la clase Person invalida el mtodo ToString la manera siguiente. De manera predeterminada,
ListBox llama a ToString y muestra una representacin de cadena de cada objeto en la coleccin enlazada. Por
ello, cada objeto Person aparece como un nombre en ListBox.
Public Overrides Function ToString() As String
Return Me._firstname.ToString
End Function

5.1.5.7. Cmo: Enlazar a una Enumeracin


En este ejemplo se muestra cmo enlazar a una enumeracin enlazando al mtodo GetValues de la misma.
Ejemplo

MCT: Luis Dueas

Pag 141 de 473

Manual de Windows Presentation Foundation


En el ejemplo siguiente, ListBox muestra la lista de valores de HorizontalAlignment de la enumeracin mediante
el enlace de datos. ListBox y Button estn enlazados de tal modo que puede cambiar el valor de la propiedad
HorizontalAlignment de Button seleccionando un valor en ListBox.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
SizeToContent="WidthAndHeight"
Title="Show Enums in a ListBox using Binding">
<Window.Resources>
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type sys:Enum}"
x:Key="AlignmentValues">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="HorizontalAlignment" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<Border Margin="10" BorderBrush="Aqua"
BorderThickness="3" Padding="8">
<StackPanel Width="300">
<TextBlock>Choose the HorizontalAlignment value of the Button:</TextBlock>
<ListBox Name="myComboBox" SelectedIndex="0" Margin="8"
ItemsSource="{Binding Source={StaticResource AlignmentValues}}"/>
<Button Content="Click Me!"
HorizontalAlignment="{Binding ElementName=myComboBox,
Path=SelectedItem}"/>
</StackPanel>
</Border>
</Window>

5.1.5.8. Cmo: Enlazar las Propiedades de dos Controles


En este ejemplo se muestra cmo enlazar la propiedad de un control del que se ha creado una instancia con la
de otro, utilizando la propiedad ElementName.
Ejemplo
En el ejemplo siguiente se muestra cmo enlazar la propiedad Background de un control Canvas con la
propiedad SelectedItem.Content de un control ComboBox:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="460" Height="200"
Title="Binding the Properties of Two Controls">
<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="16"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="DockPanel.Dock" Value="Top"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
<Style TargetType="Canvas">
<Setter Property="Height" Value="50"/>
<Setter Property="Width" Value="50"/>
<Setter Property="Margin" Value="8"/>
<Setter Property="DockPanel.Dock" Value="Top"/>
</Style>
<Style TargetType="ComboBox">
<Setter Property="Width" Value="150"/>
<Setter Property="Margin" Value="8"/>
<Setter Property="DockPanel.Dock" Value="Top"/>
</Style>
</Window.Resources>
<Border Margin="10" BorderBrush="Silver" BorderThickness="3" Padding="8">
<DockPanel>
<TextBlock>Choose a Color:</TextBlock>
<ComboBox Name="myComboBox" SelectedIndex="0">
<ComboBoxItem>Green</ComboBoxItem>
<ComboBoxItem>Blue</ComboBoxItem>
<ComboBoxItem>Red</ComboBoxItem>
</ComboBox>
<Canvas>
<Canvas.Background>
<Binding ElementName="myComboBox" Path="SelectedItem.Content"/>
</Canvas.Background>
</Canvas>
</DockPanel>
</Border>
</Window>

MCT: Luis Dueas

Pag 142 de 473

Manual de Windows Presentation Foundation


Cuando se representa este ejemplo tiene un aspecto similar a lo siguiente:

Nota

La propiedad de destino del vnculo (en este ejemplo, la propiedad Background) debe ser una propiedad

de dependencia.

5.1.5.9. Cmo: Implementar la Validacin de Enlaces


En este ejemplo se muestra cmo utilizar una propiedad ErrorTemplate y un desencadenador de estilo para
informar visualmente al usuario cuando escribe un valor no vlido, de acuerdo con una regla de validacin
personalizada.
Ejemplo
El contenido de texto del control TextBox en el ejemplo siguiente se enlaza a la propiedad Age (de tipo int) de
un objeto de enlace de origen denominado ods. El enlace est configurado para utilizar una regla de validacin
denominada AgeRangeRule, de tal forma que si el usuario especifica caracteres no numricos o un valor menor
que 21 o mayor que 130, aparezca un signo de admiracin rojo al lado del cuadro de texto y se muestre una
informacin sobre herramientas con el mensaje de error cuando el usuario mueva el mouse sobre el cuadro de
texto.
<TextBox Name="textBox1" Width="50" FontSize="15"
Validation.ErrorTemplate="{StaticResource validationTemplate}"
Style="{StaticResource textBoxInError}"
Grid.Row="1" Grid.Column="1" Margin="2">
<TextBox.Text>
<Binding Path="Age" Source="{StaticResource ods}"
UpdateSourceTrigger="PropertyChanged" >
<Binding.ValidationRules>
<c:AgeRangeRule Min="21" Max="130"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
En el ejemplo siguiente se muestra la implementacin de AgeRangeRule, que hereda de ValidationRule e
invalida el mtodo Validate. Se llama al mtodo Int32.Parse() para el valor, a fin de asegurarse de que no
contiene ningn carcter no vlido. El mtodo Validate devuelve ValidationResult, que indica si el valor es
vlido; esto se determina en funcin de si se detecta una excepcin durante el anlisis y de si el valor de edad
se encuentra fuera de los lmites mnimo y mximo.
public class AgeRangeRule : ValidationRule
{
private int _min;
private int _max;
public AgeRangeRule()
{
}
public int Min
{
get { return _min; }
set { _min = value; }
}
public int Max
{
get { return _max; }
set { _max = value; }
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
int age = 0;
try
{
if (((string)value).Length > 0)
age = Int32.Parse((String)value);
}
catch (Exception e)
{
return new ValidationResult(false, "Illegal characters or " + e.Message);
}
if ((age < Min) || (age > Max))

MCT: Luis Dueas

Pag 143 de 473

Manual de Windows Presentation Foundation


{

return new ValidationResult(false,


"Please enter an age in the range: " + Min + " - " + Max + ".");

}
else
{
return new ValidationResult(true, null);
}
}
}
En el ejemplo siguiente se muestra el objeto ControlTemplate validationTemplate personalizado que crea un
signo de admiracin rojo para notificar al usuario la existencia de un error de validacin. Se utilizan plantillas de
control para volver a definir el aspecto de un control.
<ControlTemplate x:Key="validationTemplate">
<DockPanel>
<TextBlock Foreground="Red" FontSize="20">!</TextBlock>
<AdornedElementPlaceholder/>
</DockPanel>
</ControlTemplate>
Como se muestra en el ejemplo siguiente, el objeto ToolTip que muestra el mensaje de error se crea utilizando
el estilo denominado textBoxInError. Si el valor de HasError es true, el desencadenador establece la
informacin sobre herramientas del control TextBox actual en su primer error de validacin. La propiedad
RelativeSource se establece en Self, en referencia al elemento actual.

<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">


<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
Tenga en cuenta que si no proporciona una propiedad ErrorTemplate personalizada, aparece la plantilla de error
predeterminada para proporcionar informacin visual al usuario cuando se produce un error de validacin.
Asimismo, WPF proporciona una regla de validacin integrada que detecta las excepciones iniciadas durante la
actualizacin de la propiedad de origen de enlace.

5.1.5.10. Cmo: Implementar Lgica de Validacin en Objetos Personalizados


En este ejemplo se muestra cmo implementar la lgica de validacin en un objeto personalizado y, a
continuacin, enlazar a este objeto.
Ejemplo
Puede proporcionar lgica de validacin en la capa de negocios si el objeto de origen implementa
IDataErrorInfo, como en el ejemplo siguiente:
public class Person : IDataErrorInfo
{
private int age;
public int Age
{
get { return age; }
set { age = value; }
}
public string Error
{
get
{
return null;
}
}
public string this[string name]
{
get
{
string result = null;
if (name == "Age")
{
if (this.age < 0 || this.age > 150)

MCT: Luis Dueas

Pag 144 de 473

Manual de Windows Presentation Foundation


{

result = "Age must not be less than 0 or greater than 150.";

}
}
return result;
}
}

En el ejemplo siguiente, la propiedad de texto del cuadro de texto enlaza a la propiedad Age del objeto Person,
que est disponible para el enlace mediante una declaracin de recurso a la que se asigna el atributo
x:Keydata. DataErrorValidationRule comprueba los errores de validacin provocados por la implementacin de
IDataErrorInfo.
<TextBox Style="{StaticResource textBoxInError}">
<TextBox.Text>
<!--By setting ValidatesOnExceptions to True, it checks for exceptions
that are thrown during the update of the source property.
An alternative syntax is to add <ExceptionValidationRule/> within
the <Binding.ValidationRules> section.-->
<Binding Path="Age" Source="{StaticResource data}"
ValidatesOnExceptions="True"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<!--DataErrorValidationRule checks for validation
errors raised by the IDataErrorInfo object.-->
<!--Alternatively, you can set ValidationOnDataErrors="True" on the
Binding.-->
<DataErrorValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Como alternativa, en lugar de usar DataErrorValidationRule, puede establecer la propiedad ValidatesOnData
Errors en true.

5.1.5.11. Cmo: Obtener el Objeto de Enlace a Partir de una Propiedad de


Destino Enlazada
En este ejemplo se muestra cmo obtener el objeto de enlace de una propiedad de destino enlazada a datos.
Ejemplo
Puede hacer lo siguiente para obtener el objeto Binding:
// textBox3 is an instance of a TextBox
// the TextProperty is the data-bound dependency property
Binding myBinding = BindingOperations.GetBinding(textBox3, TextBox.TextProperty);
Nota:
Debe especificar la propiedad de dependencia del enlace que desea obtener, porque es posible que el
enlace de datos se utilice en ms de una propiedad del objeto de destino.
Como alternativa, puede obtener BindingExpression y, a continuacin, obtener el valor de la propiedad
ParentBinding.
Nota:
Si el enlace es un objeto MultiBinding, utilice BindingOperations.GetMultiBinding. Si es una propiedad
PriorityBinding, utilice BindingOperations.GetPriorityBinding. Si no est seguro de si la propiedad de destino
se
ha
enlazado
mediante
Binding,
MultiBinding
o
PriorityBinding,
puede
utilizar
BindingOperations.GetBindingBase.

5.1.5.12. Cmo: Implementar una CompositeCollection


Ejemplo
En el ejemplo siguiente se muestra cmo mostrar varias colecciones y elementos como una lista utilizando la
clase CompositeCollection. En este ejemplo, GreekGods es una coleccin ObservableCollection<(Of <(T>)>) de

MCT: Luis Dueas

Pag 145 de 473

Manual de Windows Presentation Foundation


objetos GreekGod personalizados. Se definen plantillas de datos para que los objetos GreekGod y los objetos
GreekHero aparezcan con un color de primer plano oro y cian, respectivamente.
<Window Background="Cornsilk"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:SDKSample"
x:Class="SDKSample.Window1"
Title="CompositeCollections"
SizeToContent="WidthAndHeight">
<Window.Resources>
<c:GreekGods x:Key="GreekGodsData"/>
<XmlDataProvider x:Key="GreekHeroesData" XPath="GreekHeroes/Hero">
<x:XData>
<GreekHeroes xmlns="">
<Hero Name="Jason" />
<Hero Name="Hercules" />
<Hero Name="Bellerophon" />
<Hero Name="Theseus" />
<Hero Name="Odysseus" />
<Hero Name="Perseus" />
</GreekHeroes>
</x:XData>
</XmlDataProvider>
<DataTemplate DataType="{x:Type c:GreekGod}">
<TextBlock Text="{Binding Path=Name}" Foreground="Gold"/>
</DataTemplate>
<DataTemplate DataType="Hero">
<TextBlock Text="{Binding XPath=@Name}" Foreground="Cyan"/>
</DataTemplate>
</Window.Resources>
<StackPanel>
<TextBlock FontSize="18" FontWeight="Bold" Margin="10"
HorizontalAlignment="Center">Composite Collections Sample</TextBlock>
<ListBox Name="myListBox" Height="300" Width="200" Background="White">
<ListBox.ItemsSource>
<CompositeCollection>
<CollectionContainer
Collection="{Binding Source={StaticResource GreekGodsData}}" />
<CollectionContainer
Collection="{Binding Source={StaticResource GreekHeroesData}}" />
<ListBoxItem Foreground="Red">Other Listbox Item 1</ListBoxItem>
<ListBoxItem Foreground="Red">Other Listbox Item 2</ListBoxItem>
</CompositeCollection>
</ListBox.ItemsSource>
</ListBox>
</StackPanel>
</Window>

5.1.5.13. Cmo: Convertir Datos Enlazados


En este ejemplo se muestra cmo aplicar la conversin a datos que se utilizan en enlaces.
Para convertir datos durante el enlace, debe crear una clase que implemente la interfaz IValueConverter, que
incluye los mtodos Convert y ConvertBack.
Ejemplo
En el ejemplo siguiente se muestra la implementacin de un convertidor de fecha que convierte el valor de
fecha pasado de manera que slo muestre el ao, el mes y el da. Al implementar la interfaz IValueConverter,
es recomendable decorar la implementacin con un atributo ValueConversionAttribute para indicar a las
herramientas de programacin los tipos de datos que participan en la conversin, como en el ejemplo
siguiente:
[ValueConversion(typeof(DateTime), typeof(String))]
public class DateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo
culture)
{
DateTime date = (DateTime)value;
return date.ToShortDateString();
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
string strValue = value.ToString();
DateTime resultDateTime;
if (DateTime.TryParse(strValue, out resultDateTime))

MCT: Luis Dueas

Pag 146 de 473

Manual de Windows Presentation Foundation


{

return resultDateTime;
}
return value;

}
Una vez creado un convertidor, puede agregarlo como recurso en el archivo Lenguaje de marcado de
aplicaciones extensible (XAML). En el ejemplo siguiente, src se asigna al espacio de nombres en el que se
define DateConverter.
<src:DateConverter x:Key="dateConverter"/>
Por ltimo, puede utilizar el convertidor en el enlace mediante la sintaxis siguiente. En el ejemplo siguiente, el
contenido de texto de TextBlock se enlaza a StartDate, que es una propiedad de un origen de datos externo.
<TextBlock Grid.Row="2" Grid.Column="0" Margin="0,0,8,0"
Name="startDateTitle"
Style="{StaticResource smallTitleStyle}">Start Date:</TextBlock>
<TextBlock Name="StartDateDTKey" Grid.Row="2" Grid.Column="1"
Text="{Binding Path=StartDate, Converter={StaticResource dateConverter}}"
Style="{StaticResource textStyleTextBlock}"/>
Los recursos de estilo a los que se hace referencia en el ejemplo anterior se definen en la seccin de recurso del
ejemplo, no mostrada en este tema.

5.1.5.14. Cmo: Crear un Enlace en Cdigo


En este ejemplo se muestra cmo crear y establecer un objeto Binding en cdigo.
Ejemplo
Las clases FrameworkElement y FrameworkContentElement exponen un mtodo SetBinding. Para enlazar un
elemento que hereda de cualquiera de estas clases, puede llamar directamente al mtodo SetBinding, como en
el ejemplo siguiente. En este ejemplo, myDataObject es una instancia de la clase MyData y myBinding es la
clase de origen del objeto Binding. La clase MyData es una clase definida que contiene una propiedad de
cadena denominada MyDataProperty. En el ejemplo siguiente se muestra cmo enlazar el contenido de texto de
mytext, que es una instancia de TextBlock, a MyDataProperty.
Dim data1 As New MyData(DateTime.Now)
Dim binding1 As New Binding("MyDataProperty")
binding1.Source = data1
Me.myText.SetBinding(TextBlock.TextProperty, binding1)
Si lo prefiere, puede utilizar el mtodo SetBinding de la clase BindingOperations. En el ejemplo siguiente,
myNewBindDef es un objeto Binding que describe el enlace. El destino de enlace es myDateText, una instancia
de la clase TextBlock.
// myDatetext is a TextBlock object that is the binding target object
BindingOperations.SetBinding(myDateText, TextBlock.TextProperty, myNewBindDef);
BindingOperations.SetBinding(myDateText, TextBlock.ForegroundProperty, myNewBindDef);

5.1.5.15. Cmo: Obtener la Vista Predeterminada de una Recoleccin de Datos


Las vistas permiten ver la misma recoleccin de datos de maneras diferentes, segn los criterios de ordenacin,
filtrado o agrupacin. Cada recoleccin tiene una vista predeterminada compartida, que se utiliza como origen
de enlace cuando en el enlace se especifica una recoleccin como origen. En este ejemplo se muestra cmo
obtener la vista predeterminada de una recoleccin.
Ejemplo
Para crear la vista, se necesita una referencia de objeto a la recoleccin. Este objeto de datos se puede obtener
haciendo referencia al propio objeto subyacente, obteniendo el contexto de datos, obteniendo una propiedad
del origen de datos u obteniendo una propiedad del enlace. En este ejemplo se muestra cmo obtener la
propiedad DataContext de un objeto de datos y utilizarla para abrir directamente la vista predeterminada de
esta recoleccin.
myCollectionView = (CollectionView)
CollectionViewSource.GetDefaultView(rootElem.DataContext);

MCT: Luis Dueas

Pag 147 de 473

Manual de Windows Presentation Foundation


En este ejemplo el elemento raz es un objeto StackPanel. La propiedad DataContext se establece en
myDataSource, que hace referencia a un proveedor de datos que es una coleccin ObservableCollection<(Of
<(T>)>) de objetos Order.
<StackPanel.DataContext>
<Binding Source="{StaticResource myDataSource}"/>
</StackPanel.DataContext>
Otra posibilidad es crear instancias y enlazar a su propia vista de recoleccin mediante la clase
CollectionViewSource. Esta vista de recoleccin slo la comparten los controles que se enlazan a ella
directamente.

5.1.5.16. Cmo: Navegar por los Objetos de una Coleccin de Datos mediante
CollectionView
Las vistas permiten ver la misma recoleccin de datos de maneras diferentes, segn cmo se ordene, filtre o
agrupe. Las vistas tambin proporcionan el concepto de indicador de registro actual y habilitan el movimiento
del puntero. En este ejemplo se muestra cmo obtener el objeto actual y se navega por los objetos de una
recoleccin de datos utilizando la funcionalidad proporcionada en la clase CollectionView.
Ejemplo
En este ejemplo, myCollectionView es un objeto CollectionView que es una vista de una recoleccin enlazada.
En el ejemplo siguiente, OnButton es un controlador de eventos para los botones Previous y Next de una
aplicacin, que permiten al usuario navegar por la recoleccin de datos. Observe que las propiedades
IsCurrentBeforeFirst y IsCurrentAfterLast informan de si el indicador de registro actual ha alcanzado el principio
o el final de la lista, respectivamente, para que se pueda llamar a MoveCurrentToFirst y MoveCurrentToLast,
segn proceda.
La propiedad CurrentItem de la vista se convierte en Order para devolver el elemento de orden actual de la
recoleccin.
//OnButton is called whenever the Next or Previous buttons
//are clicked to change the currency
private void OnButton(Object sender, RoutedEventArgs args)
{
Button b = sender as Button;
switch (b.Name)
{
case "Previous":
myCollectionView.MoveCurrentToPrevious();
if (myCollectionView.IsCurrentBeforeFirst)
{
myCollectionView.MoveCurrentToLast();
}
break;
case "Next":
myCollectionView.MoveCurrentToNext();
if (myCollectionView.IsCurrentAfterLast)
{
myCollectionView.MoveCurrentToFirst();
}
break;
o = myCollectionView.CurrentItem as Order;
// TODO: do something with the current Order o
}

5.1.5.17. Cmo: Filtrar Datos en una Vista


En este ejemplo se muestra cmo filtrar los datos en una vista.
Ejemplo
Para crear un filtro, se define un mtodo que proporciona la lgica de filtrado. El mtodo se utiliza como
devolucin de llamada y acepta un parmetro de tipo object. El mtodo siguiente devuelve todos los objetos
Order cuya propiedad filled est establecida en "No", y deja fuera el resto de los objetos.

MCT: Luis Dueas

Pag 148 de 473

Manual de Windows Presentation Foundation


Public Function Contains(ByVal de As Object) As Boolean
Dim order1 As Order = TryCast(de, Order)
Return (order1.Filled Is "No")
End Function
A continuacin, puede aplicar el filtro, como se muestra en el ejemplo siguiente. En este ejemplo,
myCollectionView es un objeto ListCollectionView.
myCollectionView.Filter = new Predicate<object>(Contains);
Para deshacer el filtrado, puede establecer la propiedad Filter en null:
myCollectionView.Filter = null;
Si el objeto de vista procede de un objeto CollectionViewSource, la lgica de filtrado se aplica estableciendo un
controlador

para

el

evento

Filter.

En

el

ejemplo

siguiente,

listingDataView

es

una

instancia

de

CollectionViewSource.
listingDataView.Filter += new FilterEventHandler(ShowOnlyBargainsFilter);
En

el

ejemplo

siguiente

se

muestra

la

implementacin

del

controlador

de

eventos

del

filtro

ShowOnlyBargainsFilter de ejemplo. Este controlador de eventos usa la propiedad Accepted para filtrar objetos
AuctionItem que tienen un CurrentPrice de 25 $ o ms.
private void ShowOnlyBargainsFilter(object sender, FilterEventArgs e)
{
AuctionItem product = e.Item as AuctionItem;
if (product != null)
{
// Filter out products with price 25 or above
if (product.CurrentPrice < 25)
{
e.Accepted = true;
}
else
{
e.Accepted = false;
}
}
}

5.1.5.18. Cmo: Ordenar Datos en una Vista


En este ejemplo se describe cmo ordenar los datos en una vista.
Ejemplo
En el ejemplo siguiente se crean un control ListBox simple y un control Button:
<Window x:Class="ListBoxSort_snip.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxSort_snip" Height="300" Width="300">
<DockPanel>
<ListBox Name="myListBox" DockPanel.Dock="Top">
<ListBoxItem>my</ListBoxItem>
<!--Or you can set the content this way:-->
<!--<ListBoxItem Content="my"/>-->
<ListBoxItem>1</ListBoxItem>
<ListBoxItem>Sort</ListBoxItem>
<ListBoxItem>3</ListBoxItem>
<ListBoxItem>ListBox</ListBoxItem>
<ListBoxItem>2</ListBoxItem>
</ListBox>
<Button Click="OnClick" Width="30" Height="20" DockPanel.Dock="Top">Sort</Button>
</DockPanel>
</Window>
El controlador de eventos Click del botn contiene la lgica necesaria para ordenar los elementos de ListBox en
orden descendente. Puede hacerlo porque al agregar elementos a ListBox de esta manera se agregan al objeto
ItemCollection de ListBox, y ItemCollection se deriva de la clase CollectionView. Si enlaza ListBox a una
coleccin mediante la propiedad ItemsSource, puede utilizar la misma tcnica para ordenar.
private void OnClick(object sender, RoutedEventArgs e)
{
myListBox.Items.SortDescriptions.Add(
new SortDescription("Content", ListSortDirection.Descending));
}
Siempre que tenga una referencia al objeto de vista, puede utilizar la misma tcnica para ordenar el contenido
de otras vistas de coleccin.

MCT: Luis Dueas

Pag 149 de 473

Manual de Windows Presentation Foundation

5.1.5.19. Cmo: Ordenar y Agrupar Datos mediante una Vista en XAML


En este ejemplo se muestra cmo crear una vista de una recoleccin de datos en Lenguaje de marcado de
aplicaciones extensible (XAML). Las vistas aportan las funcionalidades de agrupar, ordenar, filtrar y la nocin de
un elemento actual.
Ejemplo
En el ejemplo siguiente, se define el recurso esttico denominado places como una coleccin de objetos Place,
en la que cada objeto Place consta de un nombre de ciudad y su estado. El prefijo src se asigna al espacio de
nombres donde se define el origen de datos Places. Los prefijos scm y dat se asignan a los espacios de nombres
System.ComponentModel y System.Windows.Data respectivamente.
En el ejemplo siguiente se crea una vista de la recoleccin de datos ordenada por el nombre de la ciudad y
agrupada por el estado.
<Window.Resources>
<src:Places x:Key="places"/>
<CollectionViewSource Source="{StaticResource places}" x:Key="cvs">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="CityName"/>
</CollectionViewSource.SortDescriptions>
<CollectionViewSource.GroupDescriptions>
<dat:PropertyGroupDescription PropertyName="State"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
La vista puede ser entonces un origen de enlace, como en el ejemplo siguiente:
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}"
DisplayMemberPath="CityName" Name="lb">
<ListBox.GroupStyle>
<x:Static Member="GroupStyle.Default"/>
</ListBox.GroupStyle>
</ListBox>
Para los enlaces a datos XML definidos en un recurso XmlDataProvider, coloque el smbolo @ delante del
nombre XML.
<XmlDataProvider x:Key="myTasks" XPath="Tasks/Task">
<x:XData>
<Tasks xmlns="">
<Task Name="Groceries" Priority="2" Type="Home">
<CollectionViewSource x:Key="mySortedTasks"
Source="{StaticResource myTasks}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="@Priority" />
</CollectionViewSource.SortDescriptions>
<CollectionViewSource.GroupDescriptions>
<dat:PropertyGroupDescription PropertyName="@Priority" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>

5.1.5.20. Cmo: Usar el Patrn Principal Detalle con datos Jerrquicos


En este ejemplo se muestra cmo implementar el patrn principal-detalle con datos jerrquicos.
Ejemplo
En este ejemplo, LeagueList es una coleccin de Leagues. Cada League tiene un valor Name y una coleccin de
Divisions y cada Division tiene un nombre y una coleccin de Teams. Cada Team tiene un nombre de equipo.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:SDKSample"
Width="400" Height="180"
Title="Master-Detail Binding"
Background="Silver">
<Window.Resources>
<src:LeagueList x:Key="MyList"/>
<DockPanel DataContext="{Binding Source={StaticResource MyList}}">
<StackPanel>
<Label>My Soccer Leagues</Label>
<ListBox ItemsSource="{Binding}" DisplayMemberPath="Name"

MCT: Luis Dueas

Pag 150 de 473

Manual de Windows Presentation Foundation


IsSynchronizedWithCurrentItem="true"/>
</StackPanel>
<StackPanel>
<Label Content="{Binding Path=Name}"/>
<ListBox ItemsSource="{Binding Path=Divisions}" DisplayMemberPath="Name"
IsSynchronizedWithCurrentItem="true"/>
</StackPanel>
<StackPanel>
<Label Content="{Binding Path=Divisions/Name}"/>
<ListBox DisplayMemberPath="Name" ItemsSource="{Binding Path=Divisions/Teams}"/>
</StackPanel>
</DockPanel>
</Window>
A continuacin, se muestra una captura de pantalla del ejemplo. El control ListBox Divisions realiza
automticamente el seguimiento de las selecciones en el control ListBox Leagues y muestra los datos
correspondientes. El control ListBox Teams realiza el seguimiento de las selecciones en los otros dos controles
ListBox.

Los dos puntos importantes de este ejemplo son:


1.

Los tres controles ListBox estn enlazados al mismo origen. Debe establecer la propiedad Path del
enlace para especificar qu nivel de datos desea que muestre el control ListBox.

2.

Debe establecer la propiedad IsSynchronizedWithCurrentItem en true en los controles ListBox de la


seleccin de la que est realizando el seguimiento. Al establecer esta propiedad se garantiza que el
elemento seleccionado se establezca siempre como CurrentItem. Alternativamente, si el control ListBox
obtiene sus datos de un objeto CollectionViewSource, sincronizar automticamente la seleccin y la
actualizacin.

5.1.5.21. Cmo: Usar el Patrn Principal Detalle con Datos XML Jerrquicos
En este ejemplo se muestra cmo implementar el patrn principal-detalle con datos XML jerrquicos.
Ejemplo
Este ejemplo es la versin de datos XML del ejemplo descrito en Cmo: Usar el patrn principal-detalle con
datos jerrquicos. En este ejemplo, los datos pertenecen al archivo League.xml. Observe que el tercer control
ListBox realiza el seguimiento de los cambios de seleccin del segundo ListBox enlazndose a su propiedad
SelectedValue.
<Window x:Class="SDKSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Multiple ListBox Binding Sample"
Width="400" Height="200"
Background="Cornsilk">
<Window.Resources>
<XmlDataProvider x:Key="MyList" Source="Data\Leagues.xml"
XPath="Leagues/League"/>
<DataTemplate x:Key="dataTemplate">
<TextBlock Text="{Binding XPath=@name}" />
</DataTemplate>
</Window.Resources>
<DockPanel DataContext="{Binding Source={StaticResource MyList}}">
<StackPanel>
<Label>My Soccer Leagues</Label>
<ListBox ItemsSource="{Binding}"
ItemTemplate="{StaticResource dataTemplate}"
IsSynchronizedWithCurrentItem="true"/>
</StackPanel>

MCT: Luis Dueas

Pag 151 de 473

Manual de Windows Presentation Foundation


<StackPanel>
<Label Content="{Binding XPath=@name}"/>
<ListBox Name="divisionsListBox"
ItemsSource="{Binding XPath=Division}"
ItemTemplate="{StaticResource dataTemplate}"
IsSynchronizedWithCurrentItem="true"/>
</StackPanel>
<StackPanel>
<Label Content="{Binding XPath=@name}"/>
<ListBox DataContext="{Binding ElementName=divisionsListBox,
Path=SelectedItem}"
ItemsSource="{Binding XPath=Team}"
ItemTemplate="{StaticResource dataTemplate}"/>
</StackPanel>
</DockPanel>
</Window>

5.1.5.22. Cmo: Generar un Valor Basado en una Lista de Elementos Enlazados


MultiBinding permite enlazar una propiedad del destino de enlace a una lista de propiedades de origen y, a
continuacin, aplicar la lgica para generar un valor con las entradas indicadas. En este ejemplo de
complemento se muestra cmo utilizar MultiBinding.
Ejemplo
En el ejemplo siguiente, NameListData hace referencia a una coleccin de objetos PersonName, que son objetos
que contienen dos propiedades, firstName y lastName. En el ejemplo siguiente se genera un objeto TextBlock
que muestra el nombre y el apellido de una persona, con el apellido en primer lugar.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:SDKSample"
x:Class="SDKSample.Window1"
Width="400"
Height="280"
Title="MultiBinding Sample">
<Window.Resources>
<c:NameList x:Key="NameListData"/>
<c:NameConverter x:Key="myNameConverter"/>
</Window.Resources>
<TextBlock Name="textBox2" DataContext="{StaticResource NameListData}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource myNameConverter}"
ConverterParameter="FormatLastFirst">
<Binding Path="FirstName"/>
<Binding Path="LastName"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Window>
Para entender cmo se genera el formato que muestra primero el apellido, estudiaremos la implementacin de
NameConverter:
Public Class NameConverter
Implements IMultiValueConverter
Public Function Convert1(ByVal values() As Object, ByVal targetType As System.Type, _
ByVal parameter As Object, _
ByVal culture As System.Globalization.CultureInfo) As Object _
Implements System.Windows.Data.IMultiValueConverter.Convert
Select Case CStr(parameter)
Case "FormatLastFirst"
Return (values(1) & ", " & values(0))
End Select
Return (values(0) & " " & values(1))
End Function
Public Function ConvertBack1(ByVal value As Object, ByVal targetTypes() As
System.Type, ByVal parameter As Object, ByVal culture As
System.Globalization.CultureInfo) As Object() Implements
System.Windows.Data.IMultiValueConverter.ConvertBack
Return CStr(value).Split(New Char() {" "c})
End Function
End Class
NameConverter implementa la interfaz IMultiValueConverter. NameConverter toma los valores de los enlaces
individuales y los almacena en la matriz de objetos de valores. El orden en el que los elementos Binding
aparecen bajo el elemento MultiBinding es el orden en el que esos valores estn almacenados en la matriz. El

MCT: Luis Dueas

Pag 152 de 473

Manual de Windows Presentation Foundation


argumento de parmetro del mtodo de Converter, que aplica una modificacin al parmetro para determinar
cmo dar formato al nombre, hace referencia al valor del atributo ConverterParameter.

5.1.5.23. Cmo: Implementar la Notificacin de Cambio de Propiedad


Para que en los enlaces OneWay o TwoWay se permita que las propiedades de destino de enlace reflejen
automticamente los cambios dinmicos del origen de enlace (por ejemplo, para que el panel de vista previa se
actualice automticamente cuando el usuario edite un formulario), es preciso que la clase proporcione las
notificaciones apropiadas de cambio de propiedad. En este ejemplo se muestra cmo crear una clase que
implementa INotifyPropertyChanged.
Ejemplo
Para implementar INotifyPropertyChanged, es preciso declarar el evento PropertyChanged y crear el mtodo
OnPropertyChanged. A continuacin, para cada propiedad cuyas notificaciones de cambio desee habilitar,
deber llamar a OnPropertyChanged cada vez que se actualice la propiedad.
Imports System.ComponentModel
' This class implements INotifyPropertyChanged
' to support one-way and two-way bindings
' (such that the UI element updates when the source
' has been changed dynamically)
Public Class Person
Implements INotifyPropertyChanged
Private personName As String
Sub New()
End Sub
Sub New(ByVal Name As String)
Me.personName = Name
End Sub
' Declare the event
Public Event PropertyChanged As PropertyChangedEventHandler Implements
INotifyPropertyChanged.PropertyChanged
Public Property Name() As String
Get
Return personName
End Get
Set(ByVal value As String)
personName = value
' Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Name")
End Set
End Property
' Create the OnPropertyChanged method to raise the event
Protected Sub OnPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
End Class

5.1.5.24. Cmo: Crear y Enlazar a una Coleccin ObservableCollection


En

este

ejemplo

se

muestra

cmo

crear

enlazar

una

coleccin

que

se

deriva

de

la

clase

ObservableCollection<(Of <(T>)>), que es una clase de coleccin que proporciona notificaciones cuando se
agregan o quitan elementos.
Ejemplo
En el siguiente ejemplo de cdigo se muestra la implementacin de una coleccin NameList.
Public Class NameList
Inherits ObservableCollection(Of PersonName)
' Methods
Public Sub New()
MyBase.Add(New PersonName("Willa", "Cather"))
MyBase.Add(New PersonName("Isak", "Dinesen"))
MyBase.Add(New PersonName("Victor", "Hugo"))
MyBase.Add(New PersonName("Jules", "Verne"))
End Sub
End Class
Public Class PersonName
' Methods
Public Sub New(ByVal first As String, ByVal last As String)
Me._firstName = first
Me._lastName = last
End Sub
' Properties

MCT: Luis Dueas

Pag 153 de 473

Manual de Windows Presentation Foundation


Public Property FirstName() As String
Get
Return Me._firstName
End Get
Set(ByVal value As String)
Me._firstName = value
End Set
End Property
Public Property LastName() As String
Get
Return Me._lastName
End Get
Set(ByVal value As String)
Me._lastName = value
End Set
End Property
' Fields
Private _firstName As String
Private _lastName As String
End Class
Puede hacer que la coleccin est disponible para enlazarla de la misma manera que con otros objetos de
common language runtime (CLR), como se describe en Cmo: Hacer que los datos estn disponibles para el
enlace en XAML. Por ejemplo, puede crear instancias de la coleccin en XAML y especificar la coleccin como un
recurso, como se muestra aqu:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:SDKSample"
x:Class="SDKSample.Window1"
Width="400"
Height="280"
Title="MultiBinding Sample">
<Window.Resources>
<c:NameList x:Key="NameListData"/>
</Window.Resources>
A continuacin, puede enlazar la coleccin:
<ListBox Width="200"
ItemsSource="{Binding Source={StaticResource NameListData}}"
ItemTemplate="{StaticResource NameItemTemplate}"
IsSynchronizedWithCurrentItem="True"/>
La definicin de NameItemTemplate no se muestra aqu.
Nota:
Los objetos de la coleccin deben cumplir los requisitos descritos en Informacin general sobre orgenes de
enlaces. En particular, si utiliza OneWay o TwoWay (por ejemplo, si desea que la interfaz de usuario se
actualice cuando cambien dinmicamente las propiedades de origen), debe implementar un mecanismo
apropiado de notificacin de cambio de propiedad, como la interfaz INotifyPropertyChanged.

5.1.5.25. Cmo: Implementar PriorityBinding


PriorityBinding en Windows Presentation Foundation (WPF) funciona especificando una lista de enlaces. La lista
de enlaces se ordena por orden de prioridad descendente. Si el enlace de mxima prioridad devuelve
correctamente un valor cuando se procesa, entonces nunca es necesario procesar los dems enlaces de la lista.
Puede suceder que el enlace de mxima prioridad tarde mucho tiempo en evaluarse; si esto ocurre, se utilizar
el enlace con el siguiente nivel de prioridad que devuelva correctamente un valor hasta que un enlace con una
prioridad superior devuelva correctamente un valor.
Ejemplo
Para mostrar cmo funciona PriorityBinding, se ha creado el objeto AsyncDataSource con las tres propiedades
siguientes: FastDP, SlowerDP y SlowestDP.
El descriptor de acceso get de FastDP devuelve el valor del miembro de datos _fastDP.
El descriptor de acceso get de SlowerDP espera durante 3 segundos antes de devolver el valor del miembro de
datos _slowerDP.

MCT: Luis Dueas

Pag 154 de 473

Manual de Windows Presentation Foundation


El descriptor de acceso get de SlowestDP espera durante 5 segundos antes de devolver el valor del miembro de
datos _slowestDP.
Nota:
El nico fin de este ejemplo es usarlo para realizar una demostracin. Las instrucciones de Microsoft .NET
no recomiendan la definicin de propiedades con rdenes de magnitud ms lentos que los de un conjunto
de campos.
Public Class AsyncDataSource
' Properties
Public Property FastDP As String
Get
Return Me._fastDP
End Get
Set(ByVal value As String)
Me._fastDP = value
End Set
End Property
Public Property SlowerDP As String
Get
Thread.Sleep(3000)
Return Me._slowerDP
End Get
Set(ByVal value As String)
Me._slowerDP = value
End Set
End Property
Public Property SlowestDP As String
Get
Thread.Sleep(5000)
Return Me._slowestDP
End Get
Set(ByVal value As String)
Me._slowestDP = value
End Set
End Property
' Fields
Private _fastDP As String
Private _slowerDP As String
Private _slowestDP As String
End Class
La propiedad Text se enlaza al AsyncDS anterior utilizando PriorityBinding:
<Window.Resources>
<c:AsyncDataSource SlowestDP="Slowest Value" SlowerDP="Slower Value"
FastDP="Fast Value" x:Key="AsyncDS" />
</Window.Resources>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"
DataContext="{Binding Source={StaticResource AsyncDS}}">
<TextBlock FontSize="18" FontWeight="Bold" Margin="10"
HorizontalAlignment="Center">Priority Binding</TextBlock>
<TextBlock Background="Honeydew" Width="100" HorizontalAlignment="Center">
<TextBlock.Text>
<PriorityBinding FallbackValue="defaultvalue">
<Binding Path="SlowestDP" IsAsync="True"/>
<Binding Path="SlowerDP" IsAsync="True"/>
<Binding Path="FastDP" />
</PriorityBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
Cuando el motor de enlace procesa los objetos Binding, comienza por el primer objeto Binding, que est
enlazado a la propiedad SlowestDP. Cuando se procesa este objeto Binding, no devuelve correctamente un
valor porque est inactivo durante 5 segundos, por lo que se procesa el elemento Binding siguiente. El Binding
siguiente no devuelve correctamente un valor porque est inactivo durante 3 segundos. Entonces, el motor de
enlace pasa al elemento Binding siguiente, que est enlazado a la propiedad FastDP. Este objeto Binding
devuelve el valor "Fast Value". Ahora, TextBlock muestra el valor "Fast Value".
Una vez transcurridos 3 segundos, la propiedad SlowerDP devuelve el valor "Slower Value". Entonces,
TextBlock muestra el valor "Slower Value".
Una vez transcurridos 5 segundos, la propiedad SlowestDP devuelve el valor "Slowest Value". Este enlace es el
de mxima prioridad, porque figura en primer lugar en la lista. Ahora, TextBlock muestra el valor "Slowest
Value".

MCT: Luis Dueas

Pag 155 de 473

Manual de Windows Presentation Foundation

5.1.5.26. Cmo: Enlazar a Datos XML mediante XMLDataProvider y Consultas


XPath
En este ejemplo se muestra cmo enlazar a datos XML utilizando XmlDataProvider.
Con XmlDataProvider, los datos subyacentes a los que se puede tener acceso mediante el enlace de datos en la
aplicacin pueden ser cualquier rbol de nodos XML. En otras palabras, XmlDataProvider proporciona una
manera cmoda de utilizar cualquier rbol de nodos XML como origen de enlace.
Ejemplo
En el ejemplo siguiente, los datos se incrustan directamente como una isla de datos de XML en la seccin
Resources. Una isla de datos de XML se debe incluir en etiquetas <x:XData> y tener siempre un nodo raz
nico, que es Inventory en este ejemplo.
Nota:
El nodo raz de los datos XML tiene un atributo xmlns que establece el espacio de nombres de XML en una
cadena vaca. Se trata de un requisito para aplicar las consultas XPath a una isla de datos insertada dentro
de la pgina de XAML. En este caso de insercin, el marcado XAML y, en consecuencia, la isla de datos,
hereda el espacio de nombres System.Windows. Por ello, es preciso establecer el espacio de nombres en
blanco, a fin de evitar que el espacio de nombres System.Windows certifique las consultas Xpath, lo que las
dirigira incorrectamente.
<StackPanel
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="Cornsilk">
<StackPanel.Resources>
<XmlDataProvider x:Key="InventoryData" XPath="Inventory/Books">
<x:XData>
<Inventory xmlns="">
<Books>
<Book ISBN="0-7356-0562-9" Stock="in" Number="9">
<Title>XML in Action</Title>
<Summary>XML Web Technology</Summary>
</Book>
<Book ISBN="0-7356-1370-2" Stock="in" Number="8">
<Title>Programming Microsoft Windows With C#</Title>
<Summary>C# Programming using the .NET Framework</Summary>
</Book>
<Book ISBN="0-7356-1288-9" Stock="out" Number="7">
<Title>Inside C#</Title>
<Summary>C# Language Programming</Summary>
</Book>
<Book ISBN="0-7356-1377-X" Stock="in" Number="5">
<Title>Introducing Microsoft .NET</Title>
<Summary>Overview of .NET Technology</Summary>
</Book>
<Book ISBN="0-7356-1448-2" Stock="out" Number="4">
<Title>Microsoft C# Language Specifications</Title>
<Summary>The C# language definition</Summary>
</Book>
</Books>
<CDs>
<CD Stock="in" Number="3">
<Title>Classical Collection</Title>
<Summary>Classical Music</Summary>
</CD>
<CD Stock="out" Number="9">
<Title>Jazz Collection</Title>
<Summary>Jazz Music</Summary>
</CD>
</CDs>
</Inventory>
</x:XData>
</XmlDataProvider>
</StackPanel.Resources>
<TextBlock FontSize="18" FontWeight="Bold" Margin="10"
HorizontalAlignment="Center">XML Data Source Sample</TextBlock>
<ListBox
Width="400" Height="300" Background="Honeydew">
<ListBox.ItemsSource>
<Binding Source="{StaticResource InventoryData}"
XPath="*[@Stock='out'] | *[@Number>=8 or @Number=3]"/>
</ListBox.ItemsSource>

MCT: Luis Dueas

Pag 156 de 473

Manual de Windows Presentation Foundation


<!--Alternatively, you can do the following. -->
<!--<ListBox Width="400" Height="300" Background="Honeydew"
ItemsSource="{Binding Source={StaticResource InventoryData},
XPath=*[@Stock\=\'out\'] | *[@Number>\=8 or @Number\=3]}">-->
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="12" Foreground="Red">
<TextBlock.Text>
<Binding XPath="Title"/>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
Como se muestra en este ejemplo, para crear la misma declaracin de enlace en la sintaxis de atributo es
preciso crear caracteres de escape correctos para los caracteres especiales.
ListBox mostrar los elementos siguientes cuando se ejecute este ejemplo. Se trata de los elementos Title de
todos los elementos que se encuentran bajo Books cuyo valor de Stock sea "out" o cuyo valor de Number sea 3
o mayor o igual que 8. Observe que no se devuelve ningn elemento CD porque el valor de XPath establecido
en XmlDataProvider indica que nicamente se deben exponer los elementos Books (bsicamente, estableciendo
un filtro).

En este ejemplo, se muestran los ttulos de los libros porque la propiedad XPath del enlace del objeto TextBlock
en DataTemplate se establece en "Title". Si desea mostrar el valor de un atributo, como el ISBN, deber
establecer ese valor de XPath en "@ISBN".
El mtodo XmlNode.SelectNodes administra las propiedades XPath en WPF. Puede modificar las consultas del
XPath para obtener resultados diferentes. A continuacin se muestran algunos ejemplos para la consulta XPath
en el control ListBox enlazado del ejemplo anterior:

XPath="Book[1]" devolver el primer elemento de libro ("XML in Action"). Observe que los ndices de
XPath se basan en 1, no en 0.

XPath="Book[@*]" devolver todos los elementos de libro con cualquier atributo.

XPath="Book[last()-1]" devolver los elementos de libro del segundo a ltimo libro ("Introducing
Microsoft .NET").

XPath="*[position()>3]" devolver todos los elementos de libro salvo los 3 primeros.

Al ejecutar una consulta XPath, devuelve un objeto XmlNode o una lista de XmlNodes. XmlNode es un objeto
common language runtime (CLR), lo que significa que puede utilizar la propiedad Path para enlazar a las
propiedades de common language runtime (CLR). Estudie de nuevo el ejemplo anterior. Si el resto del ejemplo
permanece igual y cambia el enlace de TextBlock por lo siguiente, ver los nombres de los objetos XmlNodes
devueltos en el control ListBox. En este caso, el nombre de todos los nodos devueltos es "Book".
<TextBlock FontSize="12" Foreground="Red">
<TextBlock.Text>
<Binding Path="Name"/>
</TextBlock.Text>
</TextBlock>
En algunas aplicaciones, puede que no sea oportuno incrustar XML como una isla de datos dentro del cdigo
fuente de la pgina XAML, ya que es imprescindible conocer el contenido exacto de los datos en tiempo de
compilacin. Por consiguiente, tambin se admite la obtencin de los datos de un archivo XML externo, como
en el ejemplo siguiente:
<XmlDataProvider x:Key="BookData" Source="data\bookdata.xml" XPath="Books"/>
Si los datos XML residen en un archivo XML remoto, el acceso a ellos se define asignando una URL adecuada al
atributo Source, como sigue:

MCT: Luis Dueas

Pag 157 de 473

Manual de Windows Presentation Foundation


<XmlDataProvider x:Key="BookData" Source="http://MyUrl" XPath="Books"/>

5.1.5.27. Cmo: Enlazar a los Resultados de una Consulta LINQ para XML,
XDocument o XElement
En este ejemplo se muestra cmo enlazar datos XML a un elemento ItemsControl mediante XDocument.
Ejemplo
El cdigo XAML siguiente define un elemento ItemsControl e incluye una plantilla de datos para los datos de
tipo Planet en el espacio de nombres XML http://planetsNS. Un tipo de datos XML que ocupa un espacio de
nombres debe incluir el espacio de nombres entre llaves y, si aparece donde podra aparecer una extensin de
marcado XAML, debe preceder al espacio de nombres con una secuencia de escape de llaves. Este cdigo
enlaza a las propiedades dinmicas que corresponden a los mtodos Element y Attribute de la clase XElement.
Las propiedades dinmicas permiten a XAML enlazar a las propiedades dinmicas que comparten los nombres
de los mtodos. Tenga en cuenta cmo la declaracin de espacio de nombres predeterminada para XML no se
aplica a los nombres de atributo.
<StackPanel Name="stacky">
<StackPanel.Resources>
<DataTemplate DataType="{}{http://planetsNS}Planet" >
<StackPanel Orientation="Horizontal">
<TextBlock Width="100" Text="{Binding
Path=Element[{http://planetsNS}DiameterKM].Value}" />
<TextBlock Width="100" Text="{Binding Path=Attribute[Name].Value}" />
<TextBlock Text="{Binding Path=Element[{http://planetsNS}Details].Value}" />
</StackPanel>
</DataTemplate>
</StackPanel.Resources>
<ItemsControl
ItemsSource="{Binding }" >
</ItemsControl>
</StackPanel>
El cdigo de C# siguiente llama a Load y establece el contexto de datos del panel de pila en todos los
subelementos del elemento denominado SolarSystemPlanets en el espacio de nombres XML http://planetsNS.
planetsDoc = XDocument.Load("../../Planets.xml");
stacky.DataContext =
planetsDoc.Element("{http://planetsNS}SolarSystemPlanets").Elements();
Los datos XML pueden almacenarse como recurso XAML mediante ObjectDataProvider. Para obtener un ejemplo
completo, vea Cdigo de origen de L2DBForm.xaml. En el ejemplo siguiente se muestra cmo el cdigo puede
establecer el contexto de datos en un recurso de objetos.
planetsDoc = (XDocument)((ObjectDataProvider)Resources["justTwoPlanets"]).Data;
stacky.DataContext =
planetsDoc.Element("{http://planetsNS}SolarSystemPlanets").Elements();
Las propiedades dinmicas que se asignan a Element y Attribute proporcionan flexibilidad dentro de XAML. Su
cdigo tambin puede enlazar a los resultados de una consulta LINQ for XML. Este ejemplo enlaza a los
resultados de la consulta ordenados por un valor de elemento.
stacky.DataContext =
from c in planetsDoc.Element("{http://planetsNS}SolarSystemPlanets").Elements()
orderby Int32.Parse(c.Element("{http://planetsNS}DiameterKM").Value)
select c;

5.1.5.28. Cmo: Usar Espacios de Nombres XML en el Enlace de Datos


Ejemplo
En este ejemplo se muestra cmo administrar los espacios de nombres especificados en el origen de enlace de
XML.
Si los datos XML tienen la definicin de espacio de nombres XML siguiente:
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">

MCT: Luis Dueas

Pag 158 de 473

Manual de Windows Presentation Foundation


Puede utilizar el elemento XmlNamespaceMapping para asignar el espacio de nombres a Prefix, como en el
ejemplo siguiente. A continuacin, puede utilizar Prefix para hacer referencia al espacio de nombres XML. En
este ejemplo, ListBox muestra los elementos title y dc:date de cada item.
<StackPanel.Resources>
<XmlNamespaceMappingCollection x:Key="mapping">
<XmlNamespaceMapping Uri="http://purl.org/dc/elements/1.1/" Prefix="dc" />
</XmlNamespaceMappingCollection>
<XmlDataProvider Source="http://msdn.microsoft.com/subscriptions/rss.xml"
XmlNamespaceManager="{StaticResource mapping}"
XPath="rss/channel/item" x:Key="provider"/>
<DataTemplate x:Key="dataTemplate">
<Border BorderThickness="1" BorderBrush="Gray">
<Grid Width="600" Height="50">
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition Height="25"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding XPath=title}" />
<TextBlock Grid.Row="1" Text="{Binding XPath=dc:date}" />
</Grid>
</Border>
</DataTemplate>
</StackPanel.Resources>
<ListBox Width="600" Height="600" Background="Honeydew"
ItemsSource="{Binding Source={StaticResource provider}}"
ItemTemplate="{StaticResource dataTemplate}"/>
Observe que la propiedad Prefix que se especifica no tiene que coincidir necesariamente con la utilizada en el
origen XML; aunque el prefijo cambie en el origen XML, la asignacin sigue funcionando.
En este ejemplo concreto, los datos XML proceden de un servicio web, pero el elemento XmlNamespaceMapping
tambin funciona con datos XML insertados o datos XML de un archivo incrustado.

5.1.5.29. Cmo: Enlazar a un Origen de Datos ADO .NET


En este ejemplo se muestra cmo enlazar un control ListBox de Windows Presentation Foundation (WPF) a un
DataSet de ADO.NET.
Ejemplo
En este ejemplo, se utiliza un objeto OleDbConnection para conectar al origen de datos, que es un archivo
Access MDB especificado en la cadena de conexin. Una vez establecida la conexin, se crea un objeto
OleDbDataAdpater. El objeto OleDbDataAdpater ejecuta una instruccin select de Lenguaje de consulta
estructurado (SQL) para recuperar el conjunto de registros de la base de datos. Los resultados del comando de
SQL se almacenan en una DataTable de DataSet llamando al mtodo Fill de OleDbDataAdapter. El elemento
DataTable de este ejemplo se denomina BookTable. A continuacin, en el ejemplo se establece la propiedad
DataContext de ListBox en el objeto DataSet.
DataSet myDataSet;
private void OnInit(object sender, EventArgs e)
{
string mdbFile = Path.Combine(AppDataPath, "BookData.mdb");
string connString = string.Format(
"Provider=Microsoft.Jet.OLEDB.4.0; Data Source={0}", mdbFile);
OleDbConnection conn = new OleDbConnection(connString);
OleDbDataAdapter adapter = new OleDbDataAdapter("SELECT * FROM BookTable;", conn);
myDataSet = new DataSet();
adapter.Fill(myDataSet, "BookTable");
// myListBox is a ListBox control. Set the DataContext of the ListBox to myDataSet
myListBox.DataContext = myDataSet;
}
Despus, se puede enlazar la propiedad ItemsSource de ListBox al elemento BookTable de DataSet:
<ListBox Name="myListBox" Height="200"
ItemsSource="{Binding Path=BookTable}"
ItemTemplate ="{StaticResource BookItemTemplate}"/>
BookItemTemplate es el objeto DataTemplate que define cmo aparecen los datos:
<StackPanel.Resources>
<c:IntColorConverter x:Key="MyConverter"/>
<DataTemplate x:Key="BookItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250" />

MCT: Luis Dueas

Pag 159 de 473

Manual de Windows Presentation Foundation


<ColumnDefinition Width="100" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Title}" Grid.Column="0"
FontWeight="Bold" />
<TextBlock Text="{Binding Path=ISBN}" Grid.Column="1" />
<TextBlock Grid.Column="2" Text="{Binding Path=NumPages}"
Background="{Binding Path=NumPages,
Converter={StaticResource MyConverter}}"/>
</Grid>
</DataTemplate>
</StackPanel.Resources>
IntColorConverter convierte un valor int en un color. Al utilizar este convertidor, el color de la propiedad
Background del tercer objeto TextBlock aparece verde si el valor de NumPages es inferior a 350 y rojo si no es
as. La implementacin del convertidor no se muestra aqu.

5.1.5.30. Cmo: Enlazar a un Mtodo


En el ejemplo siguiente se muestra cmo enlazar a un mtodo mediante ObjectDataProvider.
Ejemplo
En este ejemplo, TemperatureScale es una clase que tiene un mtodo ConvertTemp, que toma dos parmetros
(uno de tipo double y uno de tipo enumTempType) y convierte el valor dado de una escala de temperatura a
otra. En el ejemplo siguiente, se utiliza ObjectDataProvider para crear instancias del objeto TemperatureScale.
Se llama al mtodo ConvertTemp con dos parmetros especificados.
<Window.Resources>
<ObjectDataProvider ObjectType="{x:Type local:TemperatureScale}"
MethodName="ConvertTemp" x:Key="convertTemp">
<ObjectDataProvider.MethodParameters>
<system:Double>0</system:Double>
<local:TempType>Celsius</local:TempType>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<local:DoubleToString x:Key="doubleToString" />
</Window.Resources>
Ahora que el mtodo est disponible como recurso, puede enlazar sus resultados. En el ejemplo siguiente, la
propiedad Text de TextBox y la propiedad SelectedValue de ComboBox se enlazan a los dos parmetros del
mtodo. Esto permite al usuario especificar la temperatura que se desea convertir y la escala de temperatura
desde la que efectuar la conversin. Observe que BindsDirectlyToSource est establecido en true porque
estamos enlazando la propiedad MethodParameters de la instancia ObjectDataProvider y no las propiedades del
objeto contenidas en ObjectDataProvider (el objeto TemperatureScale).
El Content de la ltima Label se actualiza cuando el usuario modifica el contenido de TextBox o la seleccin de
ComboBox.
<Label Grid.Row="1" HorizontalAlignment="Right">Enter the degree to convert:</Label>
<TextBox Grid.Row="1" Grid.Column="1" Name="tb">
<TextBox.Text>
<Binding Source="{StaticResource convertTemp}" Path="MethodParameters[0]"
BindsDirectlyToSource="true" UpdateSourceTrigger="PropertyChanged"
Converter="{StaticResource doubleToString}">
<Binding.ValidationRules>
<local:InvalidCharacterRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<ComboBox Grid.Row="1" Grid.Column="2"
SelectedValue="{Binding Source={StaticResource convertTemp},
Path=MethodParameters[1], BindsDirectlyToSource=true}">
<local:TempType>Celsius</local:TempType>
<local:TempType>Fahrenheit</local:TempType>
</ComboBox>
<Label Grid.Row="2" HorizontalAlignment="Right">Result:</Label>
<Label Content="{Binding Source={StaticResource convertTemp}}"
Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2"/>
El mtodo convertidor DoubleToString toma un valor de tipo double y lo convierte en un valor de tipo string en
la direccin Convert (del origen de enlace al destino de enlace, que es la propiedad Text) y convierte el valor
string en un valor double en la direccin ConvertBack.

MCT: Luis Dueas

Pag 160 de 473

Manual de Windows Presentation Foundation


InvalidationCharacterRule es una ValidationRule que comprueba si hay caracteres no vlidos. La plantilla de
error predeterminada, que es un borde rojo alrededor de un control TextBox, aparece para notificar a los
usuarios cuando el valor de entrada no es un valor de tipo double.

5.1.5.31. Cmo: Configurar la Notificacin de Actualizaciones de Enlaces


En este ejemplo se muestra cmo establecer que se le notifique cuando se actualice la propiedad de destino de
enlace (destino) o de origen de enlace (origen) de un enlace.
Ejemplo
Windows Presentation Foundation (WPF) provoca un evento de actualizacin de datos cada vez que se actualiza
el origen o el destino de enlace. Internamente, este evento se utiliza para informar a la interfaz de usuario (UI)
de que debe actualizarse, porque los datos de enlace han cambiado. Tenga en cuenta que, para que estos
eventos y el enlace uni o bidireccional funcionen correctamente, es preciso implementar la clase de datos
mediante la interfaz INotifyPropertyChanged.
Establezca la propiedad NotifyOnTargetUpdated o NotifyOnSourceUpdated (o ambas) en true en el enlace. El
controlador que se proporciona para realizar escuchas a fin de detectar este evento debe estar asociado
directamente al elemento sobre cuyos cambios desea ser informado, o al contexto de datos global si desea
tener conocimiento de cualquier cambio en el contexto.
A continuacin figura un ejemplo que muestra cmo configurar la notificacin cuando se actualiza una
propiedad de destino.
<TextBlock Grid.Row="1" Grid.Column="1" Name="RentText"
Text="{Binding Path=Rent, Mode=OneWay, NotifyOnTargetUpdated=True}"
TargetUpdated="OnTargetUpdated"/>
A continuacin, puede asignar un controlador basado en el delegado EventHandler<T>, OnTargetUpdated en
este ejemplo, para controlar el evento:
private void OnTargetUpdated(Object sender, DataTransferEventArgs args)
{
// Handle event
}
Se pueden utilizar parmetros del evento para determinar los sobre la propiedad que ha cambiado (como el
tipo o el elemento concreto, si el mismo controlador est asociado a ms de un elemento); esto puede resultar
til si hay varias propiedades enlazadas al mismo elemento.

5.1.5.32. Cmo: Borrar Enlaces


En este ejemplo se muestra cmo borrar los enlaces de un objeto.
Ejemplo
Para borrar un enlace de una propiedad individual de un objeto, llame a ClearBinding como se muestra en el
ejemplo siguiente. En el ejemplo siguiente se quita el enlace de TextProperty de mytext, un objeto TextBlock.
BindingOperations.ClearBinding(Me.myText, TextBlock.TextProperty)
Al borrar el enlace, se quita el enlace de modo que el valor de la propiedad de dependencia se cambia a aqul
que tendra sin el enlace. Este valor puede ser un valor predeterminado, un valor heredado o un valor de un
enlace de la plantilla de datos.
Para borrar los enlaces de todas las posibles propiedades de un objeto, utilice ClearAllBindings.

5.1.5.33. Cmo: Buscar Elementos Generados por un Objeto DataTemplate


En este ejemplo se muestra cmo buscar elementos generados por un objeto DataTemplate.
Ejemplo
En este ejemplo, hay un control ListBox enlazado a algunos datos XML:

MCT: Luis Dueas

Pag 161 de 473

Manual de Windows Presentation Foundation


<ListBox Name="myListBox" ItemTemplate="{StaticResource myDataTemplate}"
IsSynchronizedWithCurrentItem="True">
<ListBox.ItemsSource>
<Binding Source="{StaticResource InventoryData}" XPath="Books/Book"/>
</ListBox.ItemsSource>
</ListBox>
El control ListBox utiliza el siguiente objeto DataTemplate:
<DataTemplate x:Key="myDataTemplate">
<TextBlock Name="textBlock" FontSize="14" Foreground="Blue">
<TextBlock.Text>
<Binding XPath="Title"/>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
Si desea recuperar el elemento TextBlock generado por el objeto DataTemplate de un elemento ListBoxItem
determinado, debe obtener el elemento ListBoxItem, hallar el objeto ContentPresenter contenido en ese
ListBoxItem y, a continuacin, llamar al mtodo FindName del objeto DataTemplate establecido para ese objeto
ContentPresenter. En el ejemplo siguiente se muestra cmo realizar estos pasos. Con fines de demostracin, en
este ejemplo se crea un cuadro de mensaje que muestra el contenido de texto de DataTemplate, el bloque de
texto generado.
// Getting the currently selected ListBoxItem
// Note that the ListBox must have
// IsSynchronizedWithCurrentItem set to True for this to work
ListBoxItem myListBoxItem =
(ListBoxItem)(myListBox.ItemContainerGenerator.ContainerFromItem(myListBox.Items.CurrentItem));
// Getting the ContentPresenter of myListBoxItem
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListBoxItem);
// Finding textBlock from the DataTemplate that is set on that ContentPresenter
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
TextBlock myTextBlock = (TextBlock)myDataTemplate.FindName("textBlock", myContentPresenter);
// Do something to the DataTemplate-generated TextBlock
MessageBox.Show("The text of the TextBlock of the selected list item: "
+ myTextBlock.Text);
A continuacin se muestra la implementacin de FindVisualChild, que utiliza los mtodos de VisualTreeHelper:
private childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
return (childItem)child;
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}

5.1.5.34. Cmo: Enlazar a un Servicio Web


En este ejemplo se muestra cmo enlazar a objetos devueltos por llamadas a mtodos del servicio web.
Ejemplo
En este ejemplo se utiliza MSDN/TechNet Publishing System (MTPS) Content Service para recuperar la lista de
idiomas admitidos por un documento concreto.
Antes de llamar a un servicio web, es preciso crear una referencia a l. Para crear una referencia web al
servicio de MTPS mediante Microsoft Visual Studio, siga estos pasos:
1.

Abra el proyecto en Visual Studio.

2.

En el men Proyecto, haga clic en Agregar referencia Web.

3.

En el cuadro de dilogo, establezca la Direccin URL en


http://services.msdn.microsoft.com/contentservices/contentservice.asmx?wsdl.

MCT: Luis Dueas

Pag 162 de 473

Manual de Windows Presentation Foundation


4.

Haga clic en Ir y, a continuacin, en Agregar referencia.

Luego, llame al mtodo del servicio web y establezca la propiedad DataContext del control o la ventana de que
se trate en el objeto devuelto. El mtodo GetContent del servicio MTPS toma una referencia al objeto
getContentRequest. Por consiguiente, en el ejemplo siguiente se configura primero un objeto de solicitud:
// 1. Include the web service namespace
using BindtoContentService.com.microsoft.msdn.services;
// 2. Set up the request object
// To use the MSTP web service, we need to configure and send a request
// In this example, we create a simple request that has the ID of the XmlReader.Read
method page
getContentRequest request = new getContentRequest();
request.contentIdentifier = "abhtw0f1";
// 3. Create the proxy
ContentService proxy = new ContentService();
// 4. Call the web service method and set the DataContext of the Window
// (GetContent returns an object of type getContentResponse)
This.DataContext = proxy.GetContent(request);
Una vez establecido DataContext, puede crear enlaces a las propiedades del objeto en que se ha establecido
DataContext. En este ejemplo, DataContext se ha establecido en el objeto getContentResponse devuelto por
el mtodo GetContent. En el ejemplo siguiente, ItemsControl muestra los valores de locale de
availableVersionsAndLocales de getContentResponse, y se enlaza a esta propiedad.
<ItemsControl Grid.Column="1" Grid.Row="2" Margin="0,3,0,0"
ItemsSource="{Binding Path=availableVersionsAndLocales}"
DisplayMemberPath="locale"/>

5.1.5.35. Cmo: Enlazar a los Resultados de una Consulta LINQ


En este ejemplo se muestra cmo ejecutar una consulta LINQ y, a continuacin, enlazar a los resultados.
Ejemplo
En el ejemplo siguiente se crean dos cuadros de lista. El primer cuadro de lista contiene tres elementos de lista.
<ListBox SelectionChanged="ListBox_SelectionChanged" SelectedIndex="0"
Margin="10,0,10,0">
<ListBoxItem>1</ListBoxItem>
<ListBoxItem>2</ListBoxItem>
<ListBoxItem>3</ListBoxItem>
</ListBox>
<ListBox Width="400" Margin="10" Name="myListBox" HorizontalContentAlignment="Stretch"
ItemsSource="{Binding}" ItemTemplate="{StaticResource myTaskTemplate}"/>
Al seleccionar un elemento del primer cuadro de lista, se invoca el controlador de eventos siguiente. En este
ejemplo, Tasks es una coleccin de objetos Task. La clase Task tiene una propiedad denominada Priority. Este
controlador de eventos ejecuta una consulta LINQ que devuelve la coleccin de objetos Task que tienen el valor
de prioridad seleccionado y, a continuacin, los establece como DataContext:
using System.Linq;
Tasks tasks = new Tasks();
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int pri = Int32.Parse(((sender as ListBox).SelectedItem as
ListBoxItem).Content.ToString());
this.DataContext = from task in tasks
where task.Priority == pri
select task;
}
El segundo cuadro de lista enlaza a esa coleccin porque su valor de ItemsSource est establecido en
{Binding}. Como resultado, muestra la coleccin devuelta (en funcin de myTaskTemplate DataTemplate).

5.2. Arrastrar y Colocar


Windows Presentation Foundation (WPF) proporciona una infraestructura de arrastrar y colocar muy flexible que
admite arrastrar y colocar datos dentro de las aplicaciones de WPF as como otras aplicaciones de Windows.

5.2.1. Informacin General sobre la Funcin de Arrastrar y Colocar


En este tema se proporciona informacin general sobre la compatibilidad con la operacin de arrastrar y colocar
en las aplicaciones de Windows Presentation Foundation (WPF). Arrastrar y colocar suele referirse a un mtodo

MCT: Luis Dueas

Pag 163 de 473

Manual de Windows Presentation Foundation


de interaccin con la interfaz de usuario (UI) que implica utilizar un mouse (u otro dispositivo sealador) para
seleccionar uno o ms objetos, arrastrarlos hasta un destino de colocacin deseado de la interfaz de usuario y
colocarlos.
Compatibilidad con arrastrar y colocar en WPF
En las operaciones de arrastrar y colocar suelen participar dos partes: un origen de arrastre, donde se origina el
objeto que se arrastra, y un destino de colocacin, que recibe el objeto al colocarlo. El origen de arrastre y el
destino de colocacin pueden pertenecer a la misma aplicacin o a distintas aplicaciones.
El tipo y nmero de objetos que se pueden manipular con arrastrar y colocar son completamente arbitrarios;
uno o ms archivos o carpetas y selecciones de contenido son algunos de los objetos ms comunes que se
manipulan mediante este tipo de operaciones. La accin o acciones concretas que se realizan durante una
operacin de arrastrar y colocar son especficas de la aplicacin y, a menudo, vienen determinadas por el
contexto. Por ejemplo, al arrastrar una seleccin de archivos desde una carpeta hasta otra dentro del mismo
dispositivo de almacenamiento, de manera predeterminada los archivos se mueven; en cambio, al arrastrar
archivos desde un recurso compartido Convencin de nomenclatura universal (UNC) hasta una carpeta local, de
manera predeterminada los archivos se copian. Las funciones de arrastrar y colocar proporcionadas por WPF se
han diseado para ser extremadamente flexibles y personalizables, a fin de admitir gran variedad de escenarios
de arrastrar y colocar. La funcin de arrastrar y colocar permite manipular los objetos dentro de una misma
aplicacin o entre aplicaciones diferentes; tambin se proporciona compatibilidad plena con la funcin de
arrastrar y colocar entre aplicaciones de WPF y otras aplicaciones de Windows. La funcin de arrastrar y colocar
constituye un aspecto de la transferencia de datos, que es un rea ms general; adems de arrastrar y colocar,
la transferencia de datos incluye utilizar el portapapeles del sistema para las operaciones de copiar y pegar.
Nota de seguridad: la funcin de arrastrar y colocar de OLE no funciona en la zona de Internet.
Datos y objetos de datos
Los datos que se transfieren como parte de una operacin de arrastrar y colocar se almacenan en un objeto de
datos. Conceptualmente, un objeto de datos est compuesto de uno o ms pares de:

una clase Object que contiene los datos reales, y


un identificador del formato de datos correspondiente.

Los datos en s pueden estar compuestos de cualquier elemento que se puede representar como una clase base
Object. El formato de datos correspondiente es una cadena o Type que proporciona informacin sobre el
formato de los datos. Los objetos de datos permiten hospedar varios pares de datos-(formato de datos); esto
permite que un mismo objeto de datos proporcione los datos en varios formatos.
Todos los objetos de datos deben implementar la interfaz IDataObject, que proporciona el siguiente conjunto de
mtodos estndar que habilitan y facilitan la transferencia de datos.
Mtodo

Resumen

GetData

Recupera un objeto de datos en un formato de datos especificado.

GetDataPresent

Comprueba si los datos estn disponibles en un formato especificado o se pueden


convertir a l.

GetFormats

Devuelve una lista de formatos en los que se almacenan los datos de este objeto de
datos; o bien, a los que se pueden convertir.

SetData

Almacena los datos especificados en este objeto de datos.

WPF proporciona una implementacin bsica de IDataObject en la clase DataObject; la clase DataObject
estndar es suficiente para numerosos escenarios de transferencia de datos.

MCT: Luis Dueas

Pag 164 de 473

Manual de Windows Presentation Foundation


Los objetos de datos suelen incluir una funcin para convertir automticamente datos almacenados en un
formato a un formato diferente al extraer los datos; esta funcin se denomina autoconversin.
Al consultar los formatos de datos disponibles en un objeto de datos, los formatos de datos autoconvertibles se
pueden filtrar para distinguirlos de los formatos de datos nativos; para ello, se llama al mtodo
GetFormats(Boolean) o GetDataPresent(String, Boolean) y se establece el parmetro autoConvert en falso. Al
agregar datos a un objeto de datos con el mtodo SetData(String, Object, Boolean), se puede prohibir la
autoconversin de los datos estableciendo el parmetro autoConvert en falso.
Eventos de arrastrar y colocar
Las operaciones de arrastrar y colocar admiten un modelo basado en eventos. Tanto el origen de arrastre como
el destino de colocacin utilizan un conjunto estndar de eventos para controlar las operaciones de arrastrar y
colocar. En las tablas siguientes se resumen los eventos de arrastrar y colocar estndar.
Eventos del origen de arrastre
Evento

Resumen

GiveFeedback

Este evento se produce cuando se inicia una operacin de arrastrar y


colocar, y se permite que el destino de colocacin enve informacin de
comentarios al origen de arrastre; este ltimo suele utilizar estos
comentarios para ajustar dinmicamente el aspecto del puntero del mouse a
fin de proporcionar informacin al usuario. Se trata de un evento de
propagacin.

QueryContinueDrag

Este evento se produce cuando hay un cambio en el estado del teclado o del
botn del mouse durante una operacin de arrastrar y colocar, y permite al
origen de colocacin cancelar la operacin de arrastrar y colocar en funcin
de ese estado. Se trata de un evento de propagacin.

PreviewGiveFeedback

Versin de tnel de GiveFeedback.

PreviewQueryContinueDrag

Versin de tnel de QueryContinueDrag.

Eventos del destino de colocacin


Evento

Resumen

DragEnter

Este evento se produce cuando se arrastra un objeto a los lmites del destino de
colocacin. Se trata de un evento de propagacin.

DragLeave

Este evento se produce cuando se arrastra un objeto fuera de los lmites del destino
de colocacin. Se trata de un evento de propagacin.

DragOver

Este evento se produce cuando se arrastra (mueve) un objeto dentro de los lmites
del destino de colocacin. Se trata de un evento de propagacin.

Drop

Este evento se produce cuando un objeto se coloca en el destino de colocacin. Se


trata de un evento de propagacin.

PreviewDragEnter

Versin de tnel de DragEnter.

PreviewDragLeave

Versin de tnel de DragLeave.

PreviewDragOver

Versin de tnel de DragOver.

PreviewDrop

Versin de tnel de Drop.

Trabajar con objetos de datos


En esta seccin se describen las tcnicas comunes para crear objetos de datos y trabajar con ellos.
Utilizar constructores DataObject

MCT: Luis Dueas

Pag 165 de 473

Manual de Windows Presentation Foundation


La clase DataObject proporciona varios constructores sobrecargados que facilitan la operacin de rellenar una
nueva instancia de DataObject con un solo par de datos-(formato de datos).
En el ejemplo de cdigo siguiente se crea un nuevo objeto de datos y se utiliza uno de los constructores
sobrecargados (DataObject(String, Object)) para inicializar el objeto de datos con una cadena y un formato de
datos especificado. En este caso, el formato de los datos lo especifica una cadena; la clase DataFormats
proporciona un conjunto de cadenas de tipo predefinidas. Se permite la autoconversin de los datos
almacenados de forma predeterminada.
string stringData = "Some string data to store...";
string dataFormat = DataFormats.UnicodeText;
DataObject dataObject = new DataObject(dataFormat, stringData);
Almacenar datos en varios formatos
Un mismo objeto de datos puede almacenar los datos en varios formatos. El uso estratgico de varios formatos
de datos dentro de un mismo objeto de datos hace que el objeto de datos se pueda utilizar potencialmente por
una mayor diversidad de destinos de colocacin que si nicamente fuese posible representar un formato de
datos. En general, tenga en cuenta que un origen de arrastre debe ser vlido con respecto a los formatos de
datos los destinos potenciales pueden utilizar.
En el ejemplo siguiente se muestra cmo utilizar el mtodo SetData(String, Object) para agregar datos a un
objeto de datos en varios formatos.
DataObject dataObject = new DataObject();
string sourceData = "Some string data to store...";
// Encode the source string into Unicode byte arrays.
byte[] unicodeText = Encoding.Unicode.GetBytes(sourceData); // UTF-16
byte[] utf8Text = Encoding.UTF8.GetBytes(sourceData);
byte[] utf32Text = Encoding.UTF32.GetBytes(sourceData);
// The DataFormats class does not provide data format fields for denoting
// UTF-32 and UTF-8, which are seldom used in practice; the following strings
// will be used to identify these "custom" data formats.
string utf32DataFormat = "UTF-32";
string utf8DataFormat = "UTF-8";
// Store the text in the data object, letting the data object choose
// the data format (which will be DataFormats.Text in this case).
dataObject.SetData(sourceData);
// Store the Unicode text in the data object. Text data can be automatically
// converted to Unicode (UTF-16 / UCS-2) format on extraction from the data object;
// Therefore, explicitly converting the source text to Unicode is generally unnecessary,
and
// is done here as an exercise only.
dataObject.SetData(DataFormats.UnicodeText, unicodeText);
// Store the UTF-8 text in the data object...
dataObject.SetData(utf8DataFormat, utf8Text);
// Store the UTF-32 text in the data object...
dataObject.SetData(utf32DataFormat, utf32Text);
Consultar los formatos disponibles en un objeto de datos
Dado que un mismo objeto de datos puede contener un nmero arbitrario de formatos de datos, los objetos de
datos incluyen medios para recuperar una lista de los formatos de datos disponibles.
En el ejemplo de cdigo siguiente se utiliza la sobrecarga de GetFormats para obtener una matriz de cadenas
que representan todos los formatos de datos disponibles en un objeto de datos (tanto nativos como
autoconvertibles).
DataObject dataObject = new DataObject("Some string data to store...");
// Get an array of strings, each string denoting a data format
// that is available in the data object. This overload of GetDataFormats
// returns all available data formats, native and auto-convertible.
string[] dataFormats = dataObject.GetFormats();
// Get the number of data formats present in the data object, including both
// auto-convertible and native data formats.
int numberOfDataFormats = dataFormats.Length;
// To enumerate the resulting array of data formats, and take some action when
// a particular data format is found, use a code structure similar to the following.
foreach (string dataFormat in dataFormats)
{
if (dataFormat == DataFormats.Text)
{
// Take some action if/when data in the Text data format is found.
break;
}
else if(dataFormat == DataFormats.StringFormat)

MCT: Luis Dueas

Pag 166 de 473

Manual de Windows Presentation Foundation


{

// Take some action if/when data in the string data format is found.
break;

Recuperar datos de un objeto de datos


Para recuperar los datos de un objeto de datos en un formato concreto basta con llamar a uno de los mtodos
GetData y especificar el formato de datos deseado. Uno de los mtodos GetDataPresent se puede utilizar para
comprobar la presencia de un formato de datos determinado. GetData devuelve los datos de una clase Object;
segn cul sea el formato de datos, este objeto se puede convertir a un contenedor especfico del tipo.
En el ejemplo de cdigo siguiente se utiliza la sobrecarga de GetDataPresent(String) para comprobar, en primer
lugar, si est disponible un formato de datos concreto (nativo o por autoconversin). Si el formato especificado
est disponible, entonces el ejemplo recupera los datos mediante el mtodo GetData(String).
DataObject dataObject = new DataObject("Some string data to store...");
string desiredFormat = DataFormats.UnicodeText;
byte[] data = null;
// Use the GetDataPresent method to check for the presence of a desired data format.
// This particular overload of GetDataPresent looks for both native and auto-convertible
// data formats.
if (dataObject.GetDataPresent(desiredFormat))
{
// If the desired data format is present, use one of the GetData methods to retrieve
// the data from the data object.
data = dataObject.GetData(desiredFormat) as byte[];
}
Quitar datos de un objeto de datos
No es posible quitar directamente los datos de un objeto de datos. Para quitar de manera efectiva los datos de
un objeto de datos, siga los pasos que se proponen a continuacin:
1.
2.

Cree un nuevo objeto de datos que nicamente contendr los datos que desea conservar.
"Copie" los datos deseados de del objeto de datos anterior al nuevo. Para copiar los datos, utilice uno
de los mtodos GetData para recuperar una clase Object que contiene los datos sin procesar y, a
continuacin, utilice uno de los mtodos SetData para agregar los datos al nuevo objeto de datos.

3.

Reemplace el objeto de datos anterior por el nuevo.


Nota:

Los mtodos SetData nicamente agregan datos a un objeto de datos; no reemplazan esos datos, aunque
los datos y el formato de datos sean exactamente iguales que en una llamada anterior. Al llamar dos veces
a SetData para obtener los mismos datos con idntico formato de datos har que el par de datos y formato
de datos est presente dos veces en el objeto de datos.

5.2.2. Temas Cmo de Arrastrar y Colocar


En los ejemplos siguientes se muestra cmo realizar tareas comunes mediante el marco de trabajo de arrastrar
y colocar de Windows Presentation Foundation (WPF).

5.2.2.1. Cmo: Usar un Control Thumb para Habilitar la Accin de Arrastrar


En este ejemplo se muestra cmo usar un control Thumb para cambiar el tamao de un control Canvas.
Ejemplo
El control Thumb proporciona funcionalidad de arrastrar que se puede usar para mover o cambiar de tamao
los controles al supervisar los eventos DragStarted, DragDelta y DragCompleted de Thumb.
El usuario comienza una operacin de arrastrar al presionar el botn primario del mouse cuando el puntero se
pausa en el control Thumb. La operacin de arrastrar contina mientras el botn primario se mantiene
presionado. Durante la operacin de arrastrar, puede producirse el evento DragDelta ms de una vez. Cada vez
que se produce, la clase DragDeltaEventArgs proporciona el cambio de posicin que corresponde al cambio de

MCT: Luis Dueas

Pag 167 de 473

Manual de Windows Presentation Foundation


la posicin del mouse. Cuando el usuario suelta el botn primario del mouse, la operacin de arrastrar finaliza.
La operacin de arrastrar slo proporciona coordenadas nuevas; no cambia la posicin de Thumb
automticamente.
En el ejemplo siguiente se muestra un control Thumb que es el elemento secundario de un control Canvas. El
controlador de evento DragDelta proporciona lgica para mover Thumb y cambiar el tamao de Canvas. Los
controladores de eventos DragStarted y DragCompleted cambian el color de Thumb durante una operacin de
arrastrar. En el ejemplo siguiente se define Thumb.
<Thumb Name="myThumb" Canvas.Left="80" Canvas.Top="80" Background="Blue"
Width="20" Height="20" DragDelta="onDragDelta"
DragStarted="onDragStarted" DragCompleted="onDragCompleted"/>
En el ejemplo siguiente se muestra el controlador de evento DragDelta que mueve Thumb y cambia el tamao
de Canvas como respuesta a un movimiento del mouse.
void onDragDelta(object sender, DragDeltaEventArgs e)
{
//Move the Thumb to the mouse position during the drag operation
double yadjust = myCanvasStretch.Height + e.VerticalChange;
double xadjust = myCanvasStretch.Width + e.HorizontalChange;
if ((xadjust >= 0) && (yadjust >= 0))
{
myCanvasStretch.Width = xadjust;
myCanvasStretch.Height = yadjust;
Canvas.SetLeft(myThumb, Canvas.GetLeft(myThumb) + e.HorizontalChange);
Canvas.SetTop(myThumb, Canvas.GetTop(myThumb) + e.VerticalChange);
changes.Text = "Size: " + myCanvasStretch.Width.ToString() +
", " + myCanvasStretch.Height.ToString();
}
}
En el ejemplo siguiente se muestra el controlador de evento DragStarted.
Private Sub onDragStarted(ByVal sender As Object, ByVal e As DragStartedEventArgs)
myThumb.Background = Brushes.Orange
End Sub
En el ejemplo siguiente se muestra el controlador de evento DragCompleted.
Private Sub onDragCompleted(ByVal sender As Object, ByVal e As DragCompletedEventArgs)
myThumb.Background = Brushes.Blue
End Sub

5.2.2.2. Cmo: Crear un Objeto de Datos


En los ejemplos siguientes se muestran varias maneras de crear un objeto de datos mediante los constructores
proporcionados por la clase DataObject.
Ejemplo
En el ejemplo de cdigo siguiente se crea un nuevo objeto de datos y se utiliza uno de los constructores
sobrecargados (DataObject(Object)) para inicializar el objeto de datos con una cadena. En este caso, se
determina un formato de datos adecuado automticamente segn el tipo de datos almacenados y se permite la
autoconversin de los datos almacenados de manera predeterminada.
string stringData = "Some string data to store...";
DataObject dataObject = new DataObject(stringData);
El ejemplo de cdigo siguiente es una versin reducida del cdigo de ejemplo anterior.
DataObject dataObject = new DataObject("Some string data to store...");
Ejemplo
En el ejemplo de cdigo siguiente se crea un nuevo objeto de datos y se utiliza uno de los constructores
sobrecargados (DataObject(String, Object)) para inicializar el objeto de datos con una cadena y un formato de
datos especificado. En este caso, el formato de los datos lo especifica una cadena; la clase DataFormats
proporciona un conjunto de cadenas de tipo predefinidas. Se permite la autoconversin de los datos
almacenados de forma predeterminada.
string stringData = "Some string data to store...";
string dataFormat = DataFormats.UnicodeText;
DataObject dataObject = new DataObject(dataFormat, stringData);
El ejemplo de cdigo siguiente es una versin reducida del cdigo de ejemplo anterior.

MCT: Luis Dueas

Pag 168 de 473

Manual de Windows Presentation Foundation


DataObject dataObject = new DataObject(DataFormats.UnicodeText, "Some string data to
store...");
Ejemplo
En el ejemplo de cdigo siguiente se crea un nuevo objeto de datos y se utiliza uno de los constructores
sobrecargados (DataObject) para inicializar el objeto de datos con una cadena y un formato de datos
especificado. En este caso, el formato de los datos lo especifica un parmetro de Type. Se permite la
autoconversin de los datos almacenados de forma predeterminada.
string stringData = "Some string data to store...";
Type dataFormat = stringData.GetType();
DataObject dataObject = new DataObject(dataFormat, stringData);
El ejemplo de cdigo siguiente es una versin reducida del cdigo de ejemplo anterior.
DataObject dataObject = new DataObject("".GetType(), "Some string data to store...");
Ejemplo
En el ejemplo de cdigo siguiente se crea un nuevo objeto de datos y se utiliza uno de los constructores
sobrecargados (DataObject(String, Object, Boolean)) para inicializar el objeto de datos con una cadena y un
formato de datos especificado. En este caso, el formato de los datos lo especifica una cadena; la clase
DataFormats proporciona un conjunto de cadenas de tipo predefinidas. La sobrecarga de este constructor
determinado permite que el llamador especifique si se permite la autoconversin.
string stringData = "Some string data to store...";
string dataFormat = DataFormats.Text;
bool autoConvert = false;
DataObject dataObject = new DataObject(dataFormat, stringData, autoConvert);
El ejemplo de cdigo siguiente es una versin reducida del cdigo de ejemplo anterior.
DataObject dataObject = new DataObject(DataFormats.Text, "Some string data to store...",
false);

5.2.2.3. Cmo: Determinar si un Formato de Datos est Presente en un Objeto


de Datos
En los ejemplos siguientes se muestra cmo utilizar las diversas sobrecargas del mtodo GetDataPresent para
consultar si un formato de datos determinado se encuentra en un objeto de datos.
Ejemplo
En el ejemplo de cdigo siguiente, se usa la sobrecarga del mtodo GetDataPresent(String) para consultar la
presencia de un formato de datos concreto por cadena del descriptor.
DataObject dataObject = new DataObject("Some string data to store...");
// Query for the presence of Text data in the data object, by a data format descriptor
string.
// In this overload of GetDataPresent, the method will return true both for native data
format
// and when the data can automatically be converted to the specifed format.
// In this case, string data is present natively, so GetDataPresent returns false.
string textData = null;
if (dataObject.GetDataPresent(DataFormats.StringFormat))
{
textData = dataObject.GetData(DataFormats.StringFormat) as string;
}
// In this case, the Text data in the data object can be autoconverted to
// Unicode text, so GetDataPresent returns "true".
byte[] unicodeData = null;
if (dataObject.GetDataPresent(DataFormats.UnicodeText))
{
unicodeData = dataObject.GetData(DataFormats.UnicodeText) as byte[];
}
Ejemplo
En el ejemplo de cdigo siguiente, se usa la sobrecarga del mtodo GetDataPresent(Type) para consultar la
presencia de un formato de datos concreto por tipo.
DataObject dataObject = new DataObject("Some string data to store...");
// Query for the presence of String data in the data object, by type. In this overload
// of GetDataPresent, the method will return true both for native data formats
// and when the data can automatically be converted to the specifed format.
// In this case, the Text data present in the data object can be autoconverted
// to type string (also represented by DataFormats.String), so GetDataPresent returns
"true".

MCT: Luis Dueas

Pag 169 de 473

Manual de Windows Presentation Foundation


string stringData = null;
if (dataObject.GetDataPresent(typeof(string)))
{
stringData = dataObject.GetData(DataFormats.Text) as string;
}
Ejemplo
En el ejemplo siguiente, se usa la sobrecarga del mtodo GetDataPresent(String, Boolean) para consultar los
datos por cadena del descriptor y especificar cmo tratar los formatos de datos de autoconversin.
DataObject dataObject = new DataObject("Some string data to store...");
// Query for the presence of Text data in the data object, by data format descriptor
string,
// and specifying whether auto-convertible data formats are acceptable.
// In this case, Text data is present natively, so GetDataPresent returns "true".
string textData = null;
if (dataObject.GetDataPresent(DataFormats.Text, false /* Auto-convert? */))
{
textData = dataObject.GetData(DataFormats.Text) as string;
}
// In this case, the Text data in the data object can be autoconverted to
// Unicode text, but it is not available natively, so GetDataPresent returns "false".
byte[] unicodeData = null;
if (dataObject.GetDataPresent(DataFormats.UnicodeText, false /* Auto-convert? */))
{
unicodeData = dataObject.GetData(DataFormats.UnicodeText) as byte[];
}
// In this case, the Text data in the data object can be autoconverted to
// Unicode text, so GetDataPresent returns "true".
if (dataObject.GetDataPresent(DataFormats.UnicodeText, true /* Auto-convert? */))
{
unicodeData = dataObject.GetData(DataFormats.UnicodeText) as byte[];
}

5.2.2.4. Cmo: Mostrar los Formatos de Datos en un Objeto de Datos


En los ejemplos siguientes se muestra cmo utilizar las sobrecargas del mtodo GetFormats para obtener una
matriz de cadenas que representan cada formato de datos que est disponible en un objeto de datos.
Ejemplo
En el ejemplo de cdigo siguiente se utiliza la sobrecarga de GetFormats para obtener una matriz de cadenas
que representan todos los formatos de datos disponibles en un objeto de datos (tanto nativos como
autoconvertibles).
DataObject dataObject = new DataObject("Some string data to store...");
// Get an array of strings, each string denoting a data format
// that is available in the data object. This overload of GetDataFormats
// returns all available data formats, native and auto-convertible.
string[] dataFormats = dataObject.GetFormats();
// Get the number of data formats present in the data object, including both
// auto-convertible and native data formats.
int numberOfDataFormats = dataFormats.Length;
// To enumerate the resulting array of data formats, and take some action when
// a particular data format is found, use a code structure similar to the following.
foreach (string dataFormat in dataFormats)
{
if (dataFormat == DataFormats.Text)
{
// Take some action if/when data in the Text data format is found.
break;
}
else if(dataFormat == DataFormats.StringFormat)
{
// Take some action if/when data in the string data format is found.
break;
}
}
Ejemplo
En el ejemplo de cdigo siguiente se utiliza la sobrecarga de GetFormats para obtener una matriz de cadenas
que representan nicamente los formatos de datos disponibles en un objeto de datos (se filtran los formatos de
datos autoconvertibles).
DataObject dataObject = new DataObject("Some string data to store...");
// Get an array of strings, each string denoting a data format
// that is available in the data object. This overload of GetDataFormats
// accepts a Boolean parameter inidcating whether to include auto-convertible
// data formats, or only return native data formats.

MCT: Luis Dueas

Pag 170 de 473

Manual de Windows Presentation Foundation


string[] dataFormats = dataObject.GetFormats(false /* Include auto-convertible? */);
// Get the number of native data formats present in the data object.
int numberOfDataFormats = dataFormats.Length;
// To enumerate the resulting array of data formats, and take some action when
// a particular data format is found, use a code structure similar to the following.
foreach (string dataFormat in dataFormats)
{
if (dataFormat == DataFormats.Text)
{
// Take some action if/when data in the Text data format is found.
break;
}
}

5.2.2.5. Cmo: Recuperar Datos en un Formato Concreto


En los ejemplos siguientes se muestra cmo recuperar datos de un objeto de datos en un formato especificado.
Ejemplo
En el ejemplo de cdigo siguiente, se usa la sobrecarga de GetDataPresent(String) para comprobar primero si
un formato de datos especificado est disponible (nativamente o de autoconversin). Si el formato especificado
est disponible, en el ejemplo se recuperan los datos mediante el mtodo GetData(String).
DataObject dataObject = new DataObject("Some string data to store...");
string desiredFormat = DataFormats.UnicodeText;
byte[] data = null;
// Use the GetDataPresent method to check for the presence of a desired data format.
// This particular overload of GetDataPresent looks for both native and auto-convertible
// data formats.
if (dataObject.GetDataPresent(desiredFormat))
{
// If the desired data format is present, use one of the GetData methods to retrieve
// the data from the data object.
data = dataObject.GetData(desiredFormat) as byte[];
}
Ejemplo
En el cdigo de ejemplo siguiente, se usa la sobrecarga del mtodo GetDataPresent(String, Boolean) para
comprobar primero si un formato de datos especificado est disponible nativamente (se filtran los formatos de
datos de autoconversin). Si el formato especificado est disponible, en el ejemplo se recuperan los datos
mediante el mtodo GetData(String).
DataObject dataObject = new DataObject("Some string data to store...");
string desiredFormat = DataFormats.UnicodeText;
bool noAutoConvert = false;
byte[] data = null;
// Use the GetDataPresent method to check for the presence of a desired data format.
// The autoconvert parameter is set to false to filter out auto-convertible data
formats,
// returning true only if the specified data format is available natively.
if (dataObject.GetDataPresent(desiredFormat, noAutoConvert))
{
// If the desired data format is present, use one of the GetData methods to retrieve
// the data from the data object.
data = dataObject.GetData(desiredFormat) as byte[];
}

5.2.2.6. Cmo: Almacenar Varios Formatos de Datos en un Objeto de Datos


En el ejemplo siguiente se muestra cmo utilizar el mtodo SetData(String, Object) para agregar datos a un
objeto de datos en varios formatos.
DataObject dataObject = new DataObject();
string sourceData = "Some string data to store...";
// Encode the source string into Unicode byte arrays.
byte[] unicodeText = Encoding.Unicode.GetBytes(sourceData); // UTF-16
byte[] utf8Text = Encoding.UTF8.GetBytes(sourceData);
byte[] utf32Text = Encoding.UTF32.GetBytes(sourceData);
// The DataFormats class does not provide data format fields for denoting
// UTF-32 and UTF-8, which are seldom used in practice; the following strings
// will be used to identify these "custom" data formats.
string utf32DataFormat = "UTF-32";
string utf8DataFormat = "UTF-8";
// Store the text in the data object, letting the data object choose
// the data format (which will be DataFormats.Text in this case).
dataObject.SetData(sourceData);
// Store the Unicode text in the data object. Text data can be automatically

MCT: Luis Dueas

Pag 171 de 473

Manual de Windows Presentation Foundation


// converted to Unicode (UTF-16 / UCS-2) format on extraction from the data object;
// Therefore, explicitly converting the source text to Unicode is generally unnecessary,
// and is done here as an exercise only.
dataObject.SetData(DataFormats.UnicodeText, unicodeText);
// Store the UTF-8 text in the data object...
dataObject.SetData(utf8DataFormat, utf8Text);
// Store the UTF-32 text in the data object...
dataObject.SetData(utf32DataFormat, utf32Text);

6. Documentos
Windows Presentation Foundation (WPF) proporciona un conjunto verstil de componentes que permiten a los
programadores generar aplicaciones con caractersticas de documento avanzadas y una experiencia de lectura
mejorada. Adems de las mejoras en las funciones y en la calidad, Windows Presentation Foundation (WPF)
proporciona servicios de administracin simplificados para el empaquetado, la seguridad y el almacenamiento
de los documentos.

6.1. Documentos en Windows Presentation Foundation


Windows Presentation Foundation (WPF) proporciona una gama amplia de caractersticas de documentos que
permiten la creacin de contenido de alta fidelidad diseado facilitar su acceso y lectura con respecto a las
generaciones anteriores de Windows. Adems de las mejoras en las funciones y en la calidad, WPF proporciona
servicios integrados para la presentacin, empaquetado y seguridad de los documentos. En este tema se
proporciona una introduccin a los tipos y al empaquetado de documentos de WPF.
Tipos de documentos
WPF divide los documentos en dos categoras generales basndose en su uso previsto; estas categoras de
documento se denominan "documentos fijos" y "documentos dinmicos".
Los documentos fijos estn diseados para las aplicaciones que requieren una presentacin "lo que ve es lo que
imprime" (WYSIWYG) precisa, independiente del hardware de pantalla o de impresin utilizado. Los usos tpicos
para los documentos fijos incluyen la creacin de publicaciones, el procesamiento de textos y el diseo de
formularios, donde es vital que se respete el diseo de pgina original. Un documento fijo mantiene la
colocacin posicional precisa de los elementos de contenido con independencia del dispositivo de pantalla o de
impresin utilizado. Por ejemplo, una pgina de un documento fijo presentada en una pantalla de 96 ppp
aparecer exactamente igual cuando se imprima en una impresora lser de 600 ppp o en una mquina
tipogrfica fotogrfica de 4800 ppp. El diseo de la pgina permanece inalterado en todos los casos, aunque la
calidad del documento se maximiza de acuerdo con las funciones de cada dispositivo.
En comparacin, los documentos dinmicos estn diseados para optimizar su presentacin y legibilidad y son
ptimos para su uso cuando la facilidad de lectura constituye el principal escenario de consumo del documento.
En lugar de establecerse en un diseo predefinido, este tipo de documentos ajusta y recoloca dinmicamente su
contenido basndose en las variables de tiempo de ejecucin, tales como el tamao de la ventana, la resolucin
del dispositivo y las preferencias opcionales del usuario. Una pgina web constituye un ejemplo sencillo de un
documento dinmico donde se da formato al contenido de la pgina dinmicamente para ajustarlo a la ventana
activa. Los documentos dinmicos optimizan la experiencia de visualizacin y lectura del usuario, basndose en
el entorno de tiempo de ejecucin. Por ejemplo, el mismo documento dinmico cambiar su formato
dinmicamente para aportar una legibilidad ptima en una pantalla de 19 pulgadas de alta resolucin o en la
pequea pantalla de un PDA de 2 x 3 pulgadas. Adems, los documentos dinmicos tienen varias caractersticas
integradas que incluyen la bsqueda, modos de presentacin que optimizan la legibilidad y la capacidad de
cambiar el tamao y aspecto de las fuentes.
Controles de documentos y diseo del texto
.NET Framework proporciona un conjunto de controles previamente integrados que simplifican el uso de los
documentos fijos, los documentos dinmicos y el texto general dentro de la aplicacin. La presentacin de
contenido de documentos fijos se admite mediante el control DocumentViewer. Tres controles diferentes

MCT: Luis Dueas

Pag 172 de 473

Manual de Windows Presentation Foundation


admiten

la

presentacin

de

contenido

de

documentos

dinmicos:

FlowDocumentReader,

FlowDocumentPageViewer y FlowDocumentScrollViewer que se asignan a distintos escenarios del usuario


(consulte las secciones ms adelante). Otros controles WPF proporcionan diseo simplificado para admitir los
usos de texto general.
Control de documentos fijos: DocumentViewer
El control DocumentViewer est diseado para mostrar contenido FixedDocument. El control DocumentViewer
proporciona una interfaz de usuario intuitiva que ofrece compatibilidad integrada para las operaciones comunes,
tales como las operaciones de salida impresa, copia en el portapapeles, aplicacin de zoom o bsqueda de
texto. El control proporciona acceso a las pginas de contenido mediante un mecanismo de desplazamiento
conocido. Al igual que todos los controles de WPF, DocumentViewer admite el cambio de estilo completo o
parcial, lo que permite integrar el control visualmente en la prctica totalidad de las aplicaciones y los entornos.
DocumentViewer se ha diseado para mostrar el contenido en modo de slo lectura; la edicin o modificacin
de contenido no est disponible y no se admite.
Controles de documentos dinmicos
Tres controles admiten la presentacin de contenido en los documentos dinmicos: FlowDocumentReader,
FlowDocumentPageViewer y FlowDocumentScrollViewer.
FlowDocumentReader
FlowDocumentReader dispone de caractersticas que permiten al usuario elegir dinmicamente distintos modos
de visualizacin, incluido el modo de visualizacin de una sola pgina (una pgina a la vez), dos pginas a la
vez (formato de lectura de libro) y desplazamiento continuo (sin lmite). Para obtener ms informacin sobre
estos modos de visualizacin, consulte FlowDocumentReaderViewingMode. Si no necesita la capacidad de
cambiar

dinmicamente

entre

distintos

modos

de

visualizacin,

FlowDocumentPageViewer

FlowDocumentScrollViewer proporcionan visores de contenido dinmico ms ligeros que son fijos para un modo
de visualizacin concreto.
FlowDocumentPageViewer y FlowDocumentScrollViewer
FlowDocumentPageViewer muestra el contenido en el modo de visualizacin de una sola pgina, mientras que
FlowDocumentScrollViewer

muestra

el

contenido

en

modo

del

desplazamiento

continuo.

Tanto

FlowDocumentPageViewer como FlowDocumentScrollViewer son fijos para un modo de visualizacin concreto.


Puede compararlos con FlowDocumentReader, que incluye caractersticas que permiten al usuario elegir
dinmicamente entre varios modos de vista (suministrados por la enumeracin FlowDocumentReader
ViewingMode), a costa de exigir un uso ms intensivo de los recursos que FlowDocumentPageViewer o
FlowDocumentScrollViewer.
De manera predeterminada, se muestra siempre una barra de desplazamiento vertical y la barra de
desplazamiento horizontal se vuelve visible cuando es necesario. La interfaz de usuario predeterminada para
FlowDocumentScrollViewer no incluye barra de herramientas; sin embargo, se puede utilizar la propiedad
IsToolBarVisible para habilitar una barra de herramientas integrada.
Texto en la interfaz de usuario
Adems de agregar el texto a los documentos, es evidente que el texto se puede utilizar en la interfaz de
usuario de la aplicacin, como en los formularios, por ejemplo. WPF incluye varios controles para dibujar texto
en la pantalla. Cada control se destina a un escenario diferente y tiene su propia lista de caractersticas y
limitaciones. En general, el elemento TextBlock se debe usar cuando se necesita una compatibilidad de texto
limitada, como una frase breve en una interfaz de usuario (UI). Label se puede usar cuando se necesita una
compatibilidad de texto mnima.
Empaquetado de documentos

MCT: Luis Dueas

Pag 173 de 473

Manual de Windows Presentation Foundation


Las API de System.IO.Packaging proporcionan un medio eficaz de organizar los datos de la aplicacin, el
contenido de los documentos y los recursos relacionados en un contenedor nico de fcil acceso, porttil y
sencillo de distribuir. Un archivo ZIP es un ejemplo de un tipo de Package capaz de contener varios objetos en
una sola unidad. La API de empaquetado proporciona una implementacin de ZipPackage predeterminada
diseada mediante una norma basada en la especificacin Open Packaging Conventions (OPC, o Convenciones
de empaquetado abierto) con arquitectura de archivo XML y ZIP. Las API de empaquetado de WPF facilitan la
creacin de paquetes, as como el almacenamiento y acceso de objetos en su interior. Un objeto almacenado en
un Package se denomina PackagePart ("elemento"). Los paquetes tambin pueden incluir certificados digitales
firmados que se pueden utilizar para identificar al originador de un elemento y comprobar que no se haya
modificado el contenido de un paquete. Los paquetes tambin incluyen una caracterstica PackageRelationship
que permite agregar informacin adicional a un paquete o asociarla con elementos concretos sin que ello
modifique el contenido de los elementos existentes. Los servicios de empaquetado tambin admiten Microsoft
Windows Rights Management (RM).
La arquitectura de paquetes de WPF constituye los cimientos de varias tecnologas fundamentales:

Documentos XPS que cumplen XML Paper Specification (XPS).


Documentos XML de formato abierto (.docx) de Microsoft Office "12".
Formatos de almacenamiento personalizados para su propio diseo de aplicaciones.

Basndose en las API de empaquetado, XpsDocument est diseado especficamente para almacenar
documentos de contenido fijo de WPF. XpsDocument es un documento autnomo que se puede abrir en un
visor, mostrar en un control DocumentViewer, enrutar a una cola de impresin o imprimir directamente en una
impresora compatible con XPS.
Empaquetar componentes
Las API de empaquetado de WPF permiten organizar los datos y documentos de la aplicacin en una sola
unidad porttil. Un archivo ZIP es uno de los tipos ms comunes de paquetes y constituye el tipo de
empaquetado predeterminado proporcionado con WPF. Package es una clase abstracta desde la que se
implementa ZipPackage mediante una arquitectura de archivos XML de norma abierta y ZIP. De manera
predeterminada, el mtodo Open utiliza ZipPackage para crear y utilizar los archivos ZIP. Un paquete puede
contener tres tipos bsicos de elementos:
PackagePart

Contenido de aplicaciones, datos, documentos y archivos de recursos.

PackageDigitalSignature

Certificado X.509 para la identificacin, autenticacin y validacin.

PackageRelationship

Informacin agregada relacionada con el paquete o con un elemento concreto


del mismo.

PackageParts
Una clase PackagePart ("elemento") es una clase abstracta que hace referencia a un objeto que est
almacenado en una clase Package. En un archivo ZIP, los elementos del paquete corresponden a los archivos
individuales

almacenados

dentro

del

archivo

ZIP.

ZipPackagePart

proporciona

la

implementacin

predeterminada para los objetos serializables almacenados en ZipPackage. Como en un sistema de archivos, los
elementos contenidos en el paquete se almacenan con una organizacin de directorios jerrquicos o "de
carpetas". Gracias a las API de empaquetado de WPF, las aplicaciones pueden escribir, almacenar y leer varios
objetos PackagePart utilizando un solo contenedor de archivos ZIP.
PackageDigitalSignatures
Para aportar seguridad, es posible asociar PackageDigitalSignature ("firma digital") a los elementos de un
paquete. PackageDigitalSignature incorpora un 509 que proporciona dos caractersticas:
1.

Identifica y autentica al originador del elemento.

2.

Valida que no se haya modificado el elemento.

MCT: Luis Dueas

Pag 174 de 473

Manual de Windows Presentation Foundation


La firma digital no evita que se modifique un elemento, pero se produce un error en una comprobacin de la
validacin en la firma digital si el elemento se ha modificado de alguna forma. La aplicacin puede emprender la
accin adecuada; por ejemplo, bloquear la apertura del elemento o notificar al usuario que se ha modificado el
elemento y ya no es seguro.
PackageRelationships
PackageRelationship ("relacin") proporciona un mecanismo para asociar informacin adicional con el paquete o
con un elemento del mismo. Una relacin es una facilidad de nivel del paquete que permite asociar informacin
adicional a un elemento sin modificar el propio contenido del elemento. Insertar nuevos datos directamente en
el contenido del elemento no suele ser prctico en muchos casos:

No se conoce el tipo real del elemento ni su esquema de contenido.


Aunque se conozca, el esquema de contenido podra no proporcionar un medio de agregar la nueva
informacin.

El elemento puede estar firmada digitalmente o cifrada, lo que evitara cualquier modificacin.

Las relaciones de los paquetes proporcionan un medio reconocible de agregar y asociar informacin adicional a
los elementos individuales o al paquete completo. Las relaciones de los paquetes se utilizan para dos funciones
primarias:
1.

Definir las relaciones de dependencia entre un elemento y otro.

2.

Definir las relaciones de informacin que agregan notas u otros datos relacionadas con el elemento.

PackageRelationship proporciona un medio rpido y reconocible de definir dependencias y agregar otra


informacin asociada con un elemento del paquete o con el paquete en su conjunto.
Relaciones de dependencia
Las relaciones de dependencia se utilizan para describir las dependencias que un elemento establece con otros
elementos. Por ejemplo, un paquete podra contener un elemento HTML que incluya una o ms etiquetas de
imagen <img>. Las etiquetas de la imagen hacen referencia a las imgenes que se encuentran en otros
elementos que pueden estar contenidos en el propio paquete o ser externos a l (por ejemplo, accesibles a
travs de Internet). La creacin de una relacin (PackageRelationship) asociada al archivo HTML facilita y
agiliza la deteccin de los recursos dependientes y el acceso a ellos. Una aplicacin de explorador o visor puede
tener acceso directamente a las relaciones entre los elementos y comenzar inmediatamente a ensamblar los
recursos dependientes sin conocer el esquema ni analizar el documento.
Relaciones de informacin
De modo similar a una nota o anotacin, PackageRelationship se puede utilizar tambin para almacenar otros
tipos de informacin que se asociarn a un elemento sin tener que modificar el propio contenido del mismo.
Documentos XPS
Un documento XML Paper Specification (XPS) es un paquete que contiene uno o ms documentos fijos junto
con todos los recursos y la informacin necesarios para su representacin. XPS tambin es el formato de
archivo nativo de cola de impresin de Windows Vista. XpsDocument se almacena en un conjunto de datos ZIP
estndar y puede incluir una combinacin de componentes XML y binarios, tales como archivos de imagen y de
fuentes. Las relaciones PackageRelationships se utilizan para definir las dependencias entre el contenido y los
recursos que se necesitan para representar totalmente el documento. El diseo de XpsDocument proporciona
una solucin de documento nico de alta fidelidad que admite varios usos:

Lectura, escritura y almacenamiento de contenido y recursos de documentos fijos en un nico archivo


porttil y fcil de distribuir.

Presentacin de documentos con la aplicacin Visor de XPS.


Generacin de documentos en el formato de salida de cola de impresin de Windows Vista.
Enrutamiento directo de documentos a las impresoras compatibles con XPS.

MCT: Luis Dueas

Pag 175 de 473

Manual de Windows Presentation Foundation

6.2. Almacenamiento y Serializacin de Documentos


Microsoft .NET Framework proporciona un entorno eficaz para crear y mostrar documentos de gran calidad. Las
caractersticas mejoradas que admiten tanto documentos fijos como dinmicos y controles de vista avanzados,
todo ello combinado con eficaces funciones grficas 2D y 3D aportan a las aplicaciones .NET Framework un
nuevo nivel de calidad y experiencia del usuario. La posibilidad de administrar con flexibilidad una
representacin en memoria de un documento es una caracterstica clave de .NET Framework. Por su parte, la
capacidad de guardar y cargar con eficacia documentos de un almacn de datos es algo imprescindible en casi
todas las aplicaciones. El proceso de convertir un documento de una representacin en memoria interna en un
almacn de datos externo se denomina serializacin. El proceso inverso de leer un almacn de datos y volver a
crear la instancia en memoria original se denomina deserializacin.
Serializacin de documentos
En una situacin ideal, el proceso de serializar un documento a partir de su representacin en memoria y de
deserializarlo para volver a crear una instancia de l en memoria resulta transparente a la aplicacin. La
aplicacin llama a un mtodo serializador de "escritura" para guardar el documento, mientras que un mtodo
deserializador de "lectura" obtiene acceso al almacn de datos y vuelve a crear la instancia original en
memoria. Normalmente, la aplicacin no tiene que ocuparse del formato concreto en el que se almacenan los
datos, siempre que el proceso de serializacin y deserializacin vuelva a crear el documento en su forma
original.
Con frecuencia, las aplicaciones proporcionan varias opciones de serializacin que permiten al usuario guardar
documentos en un soporte diferente o con un formato diferente. Por ejemplo, una aplicacin podra ofrecer a
opciones de "Guardar como" para almacenar un documento en un archivo de disco, una base de datos o un
servicio web. De igual modo, diferentes serializadores podran almacenar el documento en diversos formatos,
tales como HTML, RTF, XML, XP o, como alternativa, en un formato de otro fabricante. Para la aplicacin, la
serializacin define una interfaz que asla los detalles del soporte de almacenamiento dentro de la
implementacin de cada serializador concreto. Adems de las ventajas de encapsular los detalles del
almacenamiento,

las

API.NET

Framework

System.Windows.Documents.Serialization

proporcionan

otras

caractersticas importantes.
Caractersticas de los serializadores de documentos de .NET Framework 3.0

El acceso directo a los objetos de documento de alto nivel (rbol lgico y visual) permite el
almacenamiento eficaz de contenido paginado, elementos 2D y 3D, imgenes, multimedia,
hipervnculos, anotaciones y otro contenido de compatibilidad.

Funcionamiento sincrnico y asincrnico.

Compatibilidad con los serializadores de complemento con funciones mejoradas:

Acceso en todo el sistema para su uso por parte de todas las aplicaciones .NET Framework.
Sencilla capacidad de detectar complementos de aplicaciones.
Implementacin, instalacin y actualizacin sencillas de complementos personalizados de
otros fabricantes.

Compatibilidad de la interfaz de usuario con la configuracin y las opciones personalizadas en


tiempo de ejecucin.

Ruta de acceso de impresin de XPS


La ruta de acceso de impresin de XPS de Microsoft .NET Framework proporciona, adems, un mecanismo
extensible para la escritura de documentos incluida su salida impresa. XPS acta como formato de archivo de
documento y, a la vez, es el formato nativo de cola de impresin para Windows Vista. Los documentos XPS se
pueden enviar directamente a las impresoras compatibles con XPS-sin que sea necesario pasarlos a un formato
intermedio.

MCT: Luis Dueas

Pag 176 de 473

Manual de Windows Presentation Foundation


Serializadores de complemento
Las

API

System.Windows.Documents.Serialization

proporcionan

compatibilidad

con

serializadores

de

complemento y serializadores vinculados que se instalan por separado con respecto a la aplicacin, se enlazan
en tiempo de ejecucin y permiten el acceso mediante el mecanismo de deteccin SerializerProvider. Los
serializadores de complemento proporcionan ventajas mejoradas de facilidad de implementacin y uso en todo
el sistema. Los serializadores vinculados tambin se pueden implementar para los entornos de confianza parcial
tales como Aplicaciones del explorador XAML (XBAPs), donde no es posible tener acceso a los serializadores de
complemento. Los serializadores vinculados, que se basan en una implementacin derivada de la clase
SerializerWriter, se compilan y vinculan directamente en la aplicacin. Tanto los serializadores de complemento
como los vinculados funcionan a travs de mtodos y eventos pblicos idnticos, lo que facilita el uso de uno o
ambos tipos de serializadores en la misma aplicacin.
Los serializadores de complemento ayudan a los programadores de aplicaciones proporcionando extensibilidad
a los nuevos diseos de almacenamiento y formatos de archivo sin tener que codificar directamente cada
formato potencial en tiempo de compilacin. Los serializadores de complemento tambin benefician a los
programadores de otros fabricantes, al constituir un medio normalizado para implementar, instalar y actualizar
complementos accesibles por el sistema para los formatos de archivo personalizados o de propietario.
Utilizar un serializador de complemento
Los serializadores de complemento son de uso sencillo. La clase SerializerProvider enumera un objeto
SerializerDescriptor para cada complemento instalado en el sistema. La propiedad IsLoadable filtra los
complementos instalados segn la configuracin actual y comprueba que la aplicacin pueda cargar y utilizar el
serializador.

SerializerDescriptor

tambin

proporciona

otras

propiedades,

tales

como

DisplayName

DefaultFileExtension, que la aplicacin puede utilizar para solicitar al usuario la seleccin de un serializador para
un formato de salida disponible. Se proporciona un serializador de complemento predeterminado para XPS con
.NET Framework, que siempre se enumera. Despus de que el usuario selecciona un formato de salida, se
utiliza el mtodo CreateSerializerWriter para crear una clase SerializerWriter para el formato concreto. A
continuacin, se puede llamar al mtodo SerializerWriter.Write para generar la secuencia de salida del
documento al almacn de datos.
En el ejemplo siguiente se muestra una aplicacin que utiliza el mtodo SerializerProvider en una propiedad
PlugInFileFilter. PlugInFileFilter enumera los complementos instalados y genera una cadena de filtro con las
opciones de archivo disponibles para SaveFileDialog.
// ------------------------ PlugInFileFilter -------------------------/// <summary>
///
Gets a filter string for installed plug-in serializers.</summary>
/// <remark>
///
PlugInFileFilter is used to set the SaveFileDialog or
///
OpenFileDialog "Filter" property when saving or opening files
///
using plug-in serializers.</remark>
private string PlugInFileFilter
{
get
{
// Create a SerializerProvider for accessing plug-in serializers.
SerializerProvider serializerProvider = new SerializerProvider();
string filter = "";
// For each loadable serializer, add its display
// name and extension to the filter string.
foreach (SerializerDescriptor serializerDescriptor in
serializerProvider.InstalledSerializers)
{
if (serializerDescriptor.IsLoadable)
{
// After the first, separate entries with a "|".
if (filter.Length > 0)
filter += "|";
// Add an entry with the plug-in name and extension.
filter += serializerDescriptor.DisplayName + " (*" +
serializerDescriptor.DefaultFileExtension + ")|*" +
serializerDescriptor.DefaultFileExtension;
}
}
// Return the filter string of installed plug-in serializers.
return filter;
}

MCT: Luis Dueas

Pag 177 de 473

Manual de Windows Presentation Foundation


}
Una vez seleccionado por el usuario un nombre del archivo de salida, en el ejemplo siguiente se muestra el uso
del mtodo CreateSerializerWriter para almacenar un documento determinado en un formato especificado.
// Create a SerializerProvider for accessing plug-in serializers.
SerializerProvider serializerProvider = new SerializerProvider();
// Locate the serializer that matches the fileName extension.
SerializerDescriptor selectedPlugIn = null;
foreach ( SerializerDescriptor serializerDescriptor in
serializerProvider.InstalledSerializers )
{
if ( serializerDescriptor.IsLoadable &&
fileName.EndsWith(serializerDescriptor.DefaultFileExtension) )
{
// The plug-in serializer and fileName extensions match.
selectedPlugIn = serializerDescriptor;
break; // foreach
}
}
// If a match for a plug-in serializer was found,
// use it to output and store the document.
if (selectedPlugIn != null)
{
Stream package = File.Create(fileName);
SerializerWriter serializerWriter =
serializerProvider.CreateSerializerWriter(selectedPlugIn,package);
IDocumentPaginatorSource idoc = flowDocument as IDocumentPaginatorSource;
serializerWriter.Write(idoc.DocumentPaginator, null);
package.Close();
return true;
}
Instalar serializadores de complemento
La clase SerializerProvider proporciona la interfaz de aplicaciones de nivel superior para la deteccin del
serializador de complemento y tener acceso a l. SerializerProvider busca y proporciona a la aplicacin una lista
de los serializadores instalados y accesibles en el sistema. Las caractersticas concretas de los serializadores
instalados se definen mediante valores de configuracin del Registro. Los serializadores de complemento se
pueden agregar al Registro mediante el mtodo RegisterSerializer; o bien, si .NET Framework no est instalado
an, el propio script de instalacin del complemento puede configurar directamente los valores del Registro. El
mtodo UnregisterSerializer se puede utilizar para quitar un complemento previamente instalado; si se prefiere,
la configuracin del Registro se puede restablecer mediante un script de desinstalacin.
Crear un serializador de complemento
Tanto los serializadores de complemento como los serializadores vinculados utilizan los mismos mtodos y
eventos pblicos expuestos; de igual modo, pueden disearse para funcionar de manera sincrnica o
asincrnica. Para crear un serializador de complemento se suelen seguir tres pasos bsicos:
1.

Implementar y depurar el serializador en primer lugar como serializador vinculado. Crear inicialmente
el serializador compilado y vinculado directamente en una aplicacin de prueba proporciona acceso
total a los puntos de interrupcin y a otros servicios de depuracin tiles para la realizacin de
pruebas.

2.

Una vez probado completamente el serializador, se agrega una interfaz ISerializerFactory para crear
un complemento. La interfaz ISerializerFactory permite el acceso pleno a todos los objetos .NET
Framework, lo que incluye el rbol lgico, los objetos UIElement, IDocumentPaginatorSource y los
elementos Visual. Por aadidura, ISerializerFactory proporciona los mismos mtodos y eventos
sincrnicos y asincrnicos utilizados por los serializadores vinculados. Puesto que los documentos
grandes pueden tardar tiempo en generarse, se recomiendan operaciones asincrnicas para mantener
la sensibilidad a las interacciones con el usuario y proporcionar una opcin de cancelacin si se
presenta cualquier problema con el almacn de datos.

3.

Una vez creado el serializador de complemento, se implementa un script de instalacin para distribuir
e instalar (y desinstalar) el complemento.

6.3. Anotaciones

MCT: Luis Dueas

Pag 178 de 473

Manual de Windows Presentation Foundation


Windows Presentation Foundation (WPF) proporciona controles de vista de documentos que permiten agregar
anotaciones al contenido de los documentos.

6.3.1. Informacin General sobre Anotaciones


Escribir notas o comentarios en documentos impresos es una actividad tan habitual que la damos casi por
sentada. Estas notas o comentarios son "anotaciones" que se agregan a los documentos para marcar la
informacin o resaltar elementos de inters, a fin de consultarlos ms adelante. Aunque escribir notas en
documentos impresos resulta fcil y es frecuente, la capacidad de agregar comentarios personales a los
documentos electrnicos suele estar muy limitada, si es que est disponible en absoluto.
En este tema se revisan varios tipos comunes de anotaciones, en particular las notas rpidas y el resaltado, y
se muestra cmo Microsoft Annotations Framework facilita estos tipos de anotaciones en las aplicaciones
gracias a los controles de vista de documentos de Windows Presentation Foundation (WPF). Los controles de
vista

de

documentos

de

WPF

que

admiten

anotaciones

incluyen

FlowDocumentReader

FlowDocumentScrollViewer, as como los controles derivados de DocumentViewerBase como DocumentViewer y


FlowDocumentPageViewer.
Notas rpidas
Una nota rpida tpica contiene informacin escrita en un papelito coloreado que se "adhiere" a un documento.
Las notas rpidas digitales proporcionan una funcionalidad similar para los documentos electrnicos, pero con la
flexibilidad agregada de incluir muchos otros tipos de contenido, como texto mecanografiado, notas
manuscritas (por ejemplo, trazos "manuscritos" de Tablet PC) o vnculos web.
La ilustracin siguiente muestra algunos ejemplos de anotaciones de resaltado, nota rpida de texto y nota
rpida manuscrita.

En el ejemplo siguiente se muestra el mtodo que puede utilizar para habilitar la compatibilidad con las
anotaciones en la aplicacin.
// ------------------------ StartAnnotations -------------------------/// <summary>
///
Enables annotations and displays all that are viewable.</summary>
private void StartAnnotations()
{
// If there is no AnnotationService yet, create one.
if (_annotService == null)
// docViewer is a document viewing control named in Window1.xaml.
_annotService = new AnnotationService(docViewer);
// If the AnnotationService is currently enabled, disable it.
if (_annotService.IsEnabled == true)
_annotService.Disable();
// Open a stream to the file for storing annotations.
_annotStream = new FileStream(_annotStorePath, FileMode.OpenOrCreate,
FileAccess.ReadWrite);
// Create an AnnotationStore using the file stream.
_annotStore = new XmlStreamStore(_annotStream);
// Enable the AnnotationService using the new store.
_annotService.Enable(_annotStore);
}// end:StartAnnotations()
Resaltados

MCT: Luis Dueas

Pag 179 de 473

Manual de Windows Presentation Foundation


Las personas utilizan mtodos creativos para llamar la atencin sobre los elementos de inters cuando marcan
a un documento impreso, como subrayar, resaltar, rodear palabras de una frase o dibujar marcas o smbolos en
el margen. Las anotaciones de resaltado de Microsoft Annotations Framework proporcionan caractersticas
parecidas para marcar la a informacin mostrada en los controles de vista de documentos de WPF.
En la ilustracin siguiente se muestra un ejemplo de una anotacin de resaltado.

Para crear una anotacin, el usuario suele seleccionar primero algn texto o elemento de inters y, a
continuacin, hacer clic con el botn secundario del mouse para mostrar un ContextMenu de opciones de
anotacin. En el ejemplo siguiente se muestra Lenguaje de marcado de aplicaciones extensible (XAML) que
puede utilizar para declarar ContextMenu con comandos enrutados a los que el usuario puede tener acceso para
crear y administrar anotaciones.
<DocumentViewer.ContextMenu>
<ContextMenu>
<MenuItem Command="ApplicationCommands.Copy" />
<Separator />
<!-- Add a Highlight annotation to a user selection. -->
<MenuItem Command="ann:AnnotationService.CreateHighlightCommand"
Header="Add Highlight" />
<!-- Add a Text Note annotation to a user selection. -->
<MenuItem Command="ann:AnnotationService.CreateTextStickyNoteCommand"
Header="Add Text Note" />
<!-- Add an Ink Note annotation to a user selection. -->
<MenuItem Command="ann:AnnotationService.CreateInkStickyNoteCommand"
Header="Add Ink Note" />
<Separator />
<!-- Remove Highlights from a user selection. -->
<MenuItem Command="ann:AnnotationService.ClearHighlightsCommand"
Header="Remove Highlights" />
<!-- Remove Text Notes and Ink Notes from a user selection. -->
<MenuItem Command="ann:AnnotationService.DeleteStickyNotesCommand"
Header="Remove Notes" />
<!-- Remove Highlights, Text Notes, Ink Notes from a selection. -->
<MenuItem Command="ann:AnnotationService.DeleteAnnotationsCommand"
Header="Remove Highlights &amp; Notes" />
</ContextMenu>
</DocumentViewer.ContextMenu>
Delimitacin de datos
Annotations Framework enlaza las anotaciones a los datos que el usuario selecciona, no slo a una posicin en
la vista de presentacin. Por consiguiente, si la vista del documento cambia, como cuando el usuario se
desplaza o cambia el tamao de la ventana de presentacin, la anotacin permanece con la seleccin de datos
a la que est enlazada. Por ejemplo, en el grfico siguiente se muestra una anotacin que el usuario ha
realizado en una seleccin de texto. Cuando la vista del documento cambia (se desplaza, cambia de tamao,
cambia de escala o se mueve de cualquier otro modo), la anotacin de resaltado se mueve a la par que la
seleccin de datos original.

Hacer coincidir las anotaciones con los objetos anotados


Puede hacer coincidir las anotaciones con los objetos anotados correspondientes. Por ejemplo, supongamos que
tiene una aplicacin de lector de documentos simple con un panel de comentarios. El panel de comentarios
podra ser un cuadro de lista que muestre el texto de una lista de anotaciones delimitadas en un documento. Si
el usuario selecciona un elemento en el cuadro de lista, la aplicacin muestra el prrafo del documento en el
que est delimitado el objeto de anotacin correspondiente.
En el ejemplo siguiente se muestra cmo implementar un controlador de eventos de este tipo de cuadro de lista
que acta como un panel de comentarios.
void annotationsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)

MCT: Luis Dueas

Pag 180 de 473

Manual de Windows Presentation Foundation


{

Annotation comment = (sender as ListBox).SelectedItem as Annotation;


if (comment != null)
{
// IAnchorInfo info;
// service is an AnnotationService object
// comment is an Annotation object
info = AnnotationHelper.GetAnchorInfo(this.service, comment);
TextAnchor resolvedAnchor = info.ResolvedAnchor as TextAnchor;
TextPointer textPointer = (TextPointer)resolvedAnchor.BoundingStart;
textPointer.Paragraph.BringIntoView();
}

}
Otro ejemplo de escenario es el de las aplicaciones que habilitan el intercambio de anotaciones y notas rpidas
entre los lectores del documento a travs del correo electrnico. Esta caracterstica permite que estas
aplicaciones naveguen por el lector hasta la pgina que contiene la anotacin intercambiada.

6.3.2. Esquema en Anotaciones


En este tema se describe la definicin del esquema XML (XSD) utilizada por Microsoft Annotations Framework
para guardar y recuperar los datos de anotaciones del usuario.
Annotations Framework serializa los datos de anotaciones de una representacin interna en un formato XML. El
formato XML utilizado para esta conversin se describe mediante el esquema XSD de Annotations Framework.
El esquema define el formato XML independiente de la implementacin que se puede utilizar para intercambiar
datos de anotaciones entre aplicaciones.
La definicin de esquema XML de Annotations Framework est compuesta de dos subesquemas.

El esquema principal de anotaciones XML (esquema principal).


El esquema base de anotaciones XML (esquema base).

El esquema principal define la estructura XML primaria de un objeto Annotation. La mayora de los elementos
XML

definidos

en

el

esquema

principal

corresponden

los

tipos

del

espacio

de

nombres

System.Windows.Annotations. El esquema principal expone tres puntos de extensin donde las aplicaciones
pueden agregar sus propios datos XML. Estos puntos de extensin incluyen Authors, ContentLocatorPart y los
elementos de contenido. (Los elementos de contenido se proporcionan en forma de una lista XmlElement.)
El esquema base descrito en este tema define las extensiones para Authors, ContentLocatorPart y los tipos de
contenido incluidos con la versin de Windows Presentation Foundation (WPF) inicial.
Esquema principal de anotaciones XML
El esquema principal de anotaciones XML define la estructura XML que se utiliza para almacenar objetos
Annotation.
<xsd:schema elementFormDefault="qualified" attributeFormDefault="unqualified"
blockDefault="#all" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://schemas.microsoft.com/windows/annotations/2003/11/core"
xmlns:anc="http://schemas.microsoft.com/windows/annotations/2003/11/core">
<!-- The Annotations element groups a number of annotations. -->
<xsd:element name="Annotations" type="anc:AnnotationsType" />
<xsd:complexType name="AnnotationsType">
<xsd:sequence>
<xsd:element name="Annotation" minOccurs="0" maxOccurs="unbounded"
type="anc:AnnotationType" />
</xsd:sequence>
</xsd:complexType>
<!-- AnnotationType defines the structure of the Annotation element. -->
<xsd:complexType name="AnnotationType">
<xsd:sequence>
<!-- List of 0 or more authors. -->
<xsd:element name="Authors" minOccurs="0" maxOccurs="1"
type="anc:AuthorListType" />
<!-- List of 0 or more anchors. -->
<xsd:element name="Anchors" minOccurs="0" maxOccurs="1"
type="anc:ResourceListType" />
<!-- List of 0 or more cargos. -->
<xsd:element name="Cargos" minOccurs="0" maxOccurs="1"
type="anc:ResourceListType" />
</xsd:sequence>

MCT: Luis Dueas

Pag 181 de 473

Manual de Windows Presentation Foundation


<!-- Unique annotation ID. -->
<xsd:attribute name="Id" type="xsd:string" use="required" />
<!-- Annotation "Type" is used to map the annotation to an annotation
component that takes care of the visual representation of the
annotation. WPF V1 recognizes three annotation types:
http://schemas.microsoft.com/windows/annotations/2003/11/base:Highlight
http://schemas.microsoft.com/windows/annotations/2003/11/base:TextStickyNote
http://schemas.microsoft.com/windows/annotations/2003/11/base:InkStickyNote
-->
<xsd:attribute name="Type" type="xsd:QName" use="required" />
<!-- Time when the annotation was last modified. -->
<xsd:attribute name="LastModificationTime" use="optional"
type="xsd:dateTime" />
<!-- Time when the annotation was created. -->
<xsd:attribute name="CreationTime" use="optional"
type="xsd:dateTime" />
</xsd:complexType>
<!-- "Authors" consists of 0 or more elements that represent an author. -->
<xsd:complexType name="AuthorListType">
<xsd:sequence minOccurs="0" maxOccurs="unbounded">
<xsd:element ref="anc:Author" />
</xsd:sequence>
</xsd:complexType>
<!-- The core schema allows any author type. Supported author types
in version 1 (V1) are described in the base schema. -->
<xsd:element name="Author" abstract="true" block="extension restriction"/>
<!-- Both annotation anchor and annotation cargo are represented by the
ResourceListType which contains 0 or more "Resource" elements. -->
<xsd:complexType name="ResourceListType">
<xsd:sequence>
<xsd:element name="Resource" minOccurs="0" maxOccurs="unbounded"
type="anc:ResourceType" />
</xsd:sequence>
</xsd:complexType>
<!-- Resource groups identification, location
and/or content of some information. -->
<xsd:complexType name="ResourceType">
<xsd:choice minOccurs="0" maxOccurs="unbounded" >
<xsd:choice>
<xsd:element name="ContentLocator" type="anc:ContentLocatorType" />
<xsd:element name="ContentLocatorGroup" type="anc:ContentLocatorGroupType" />
</xsd:choice>
<xsd:element ref="anc:Content"/>
</xsd:choice>
<!-- Unique resource identifier. -->
<xsd:attribute name="Id" type="xsd:string" use="required" />
<!-- Optional resource name. -->
<xsd:attribute name="Name" type="xsd:string" use="optional" />
</xsd:complexType>
<!-- ContentLocatorGroup contains a set of ContentLocators -->
<xsd:complexType name="ContentLocatorGroupType">
<xsd:sequence>
<xsd:element name="ContentLocator" minOccurs="1" maxOccurs="unbounded"
type="anc:ContentLocatorType" />
</xsd:sequence>
</xsd:complexType>
<!-- A ContentLocator describes the location or the identification
of particular data within some context. The ContentLocator consists
of one or more ContentLocatorParts. Each ContentLocatorPart needs to
be successively applied to the context to arrive at the data. What
"applying", "context", and "data" mean is application dependent.
-->
<xsd:complexType name="ContentLocatorType">
<xsd:sequence minOccurs="1" maxOccurs="unbounded">
<xsd:element ref="anc:ContentLocatorPart" />
</xsd:sequence>
</xsd:complexType>
<!-- A ContentLocatorPart is a set of "Item" elements. Each "Item" element
has "Name" and "Value" attributes that define a name/value pair.
ContentLocatorPart is an abstract type that must be restricted for each
concrete ContentLocatorPart definition. This restriction should define
allowed names and values for the concrete ContentLocatorPart type. That
way the application can define its own way of locating information. The
ContentLocatorPartTypes that are allowed in version 1 (V1) of WPF are
defined in the Annotations Base Schema.
-->
<xsd:element name="ContentLocatorPart" type="anc:ContentLocatorPartType"
abstract="true" />
<xsd:complexType name="ContentLocatorPartType" abstract="true"
block="restriction">
<xsd:sequence minOccurs="0" maxOccurs="unbounded">
<xsd:element name="Item" type="anc:ItemType" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ItemType" abstract="true" >
<xsd:attribute name="Name" type='xsd:string' use="required" />

MCT: Luis Dueas

Pag 182 de 473

Manual de Windows Presentation Foundation


<xsd:attribute name="Value" type='xsd:string' use="optional" />
</xsd:complexType>
<!-- Content describes the underlying content of a resource. This is an
abstract type that should be redefined for each concrete content type
through restriction. Allowed content types in WPF version 1 are
defined in the Annotations Base Schema.
-->
<xsd:element name="Content" abstract="true" block="extension restriction"/>
</xsd:schema>
Esquema base de anotaciones XML
El esquema base define la estructura XML para los tres elementos abstractos definidos en el esquema principal:
Authors, ContentLocatorPart y Contents.
<xsd:schema elementFormDefault="qualified" attributeFormDefault="unqualified"
blockDefault="#all"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://schemas.microsoft.com/windows/annotations/2003/11/base"
xmlns:anb="http://schemas.microsoft.com/windows/annotations/2003/11/base"
xmlns:anc="http://schemas.microsoft.com/windows/annotations/2003/11/core">
<xsd:import schemaLocation="AnnotationCoreV1.xsd"
namespace="http://schemas.microsoft.com/windows/annotations/2003/11/core"/>
<!-- ***** Author ***** -->
<!-- Simple DisplayName Author -->
<xsd:complexType name="StringAuthorType">
<xsd:simpleContent >
<xsd:extension base='xsd:string' />
</xsd:simpleContent>
</xsd:complexType>
<xsd:element name="StringAuthor" type="anb:StringAuthorType"
substitutionGroup="anc:Author"/>
<!-- ***** LocatorParts ***** -->
<!-- Helper types -->
<!-- CountItemNameType - helper type to define count item -->
<xsd:simpleType name="CountItemNameType">
<xsd:restriction base='xsd:string'>
<xsd:pattern value="Count" />
</xsd:restriction>
</xsd:simpleType>
<!-- NumberType - helper type to define segment count item -->
<xsd:simpleType name="NumberType">
<xsd:restriction base='xsd:string'>
<xsd:pattern value="\d*" />
</xsd:restriction>
</xsd:simpleType>
<!-- SegmentNameType: helper type to define possible segment name types -->
<xsd:simpleType name="SegmentItemNameType">
<xsd:restriction base='xsd:string'>
<xsd:pattern value="Segment\d*" />
</xsd:restriction>
</xsd:simpleType>
<!-- Flow Locator Part -->
<!-- FlowSegmentValueItemType: helper type to define flow segment values -->
<xsd:simpleType name="FlowSegmentItemValueType">
<xsd:restriction base='xsd:string'>
<xsd:pattern value=" \d*,\d*" />
</xsd:restriction>
</xsd:simpleType>
<!-- FlowItemType -->
<xsd:complexType name="FlowItemType" abstract = "true">
<xsd:complexContent>
<xsd:restriction base="anc:ItemType">
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<!-- FlowSegmentItemType -->
<xsd:complexType name="FlowSegmentItemType">
<xsd:complexContent>
<xsd:restriction base="anb:FlowItemType">
<xsd:attribute name="Name" use="required"
type="anb:SegmentItemNameType"/>
<xsd:attribute name="Value" use="required"
type="anb:FlowSegmentItemValueType"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<!-- FlowCountItemType -->
<xsd:complexType name="FlowCountItemType">
<xsd:complexContent>
<xsd:restriction base="anb:FlowItemType">
<xsd:attribute name="Name" type="anb:CountItemNameType" use="required"/>
<xsd:attribute name="Value" type="anb:NumberType" use="required"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>

MCT: Luis Dueas

Pag 183 de 473

Manual de Windows Presentation Foundation


<!-- CharacterRangeType is an extension of ContentLocatorPartType that locates
*
part of the content within a FlowDocument. CharacterRangeType contains one
*
"Item" element with name "Count" and value the number(N) of "SegmentXX"
*
elements that this ContentLocatorPart has. It also contains N "Item"
*
elements with name "SegmentXX" where XX is a number from 0 to N-1. The
*
value of each "SegmnetXX" element is a string in the form "offset, length"
*
which locates one sequence of symbols in the FlowDocument. Example:
*
<anb:CharacterRange>
*
<anc:Item Name="Count" Value="2" />
*
<anc:Item Name="Segment0" Value="5,10" />
*
<anc:Item Name="Segment1" Value="25,2" />
*
</anb:ChacterRange>
-->
<xsd:complexType name="CharacterRangeType">
<xsd:complexContent>
<xsd:extension base="anc:ContentLocatorPartType">
<xsd:sequence minOccurs="1" maxOccurs="unbounded">
<xsd:element name="Item" type="anb:FlowItemType" />
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<!-- CharacterRange element substitutes ContentLocatorPart element -->
<xsd:element name="CharacterRange" type="anb:CharacterRangeType"
substitutionGroup="anc:ContentLocatorPart"/>
<!-- Fixed LocatorPart -->
<!-- Helper type FixedItemType -->
<xsd:complexType name="FixedItemType" abstract = "true">
<xsd:complexContent>
<xsd:restriction base="anc:ItemType">
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<!-- Helper type FixedCountItemType: ContentLocatorPart items count -->
<xsd:complexType name="FixedCountItemType">
<xsd:complexContent>
<xsd:restriction base="anb:FixedItemType">
<xsd:attribute name="Name" type="anb:CountItemNameType" use="required"/>
<xsd:attribute name="Value" type="anb:NumberType" use="required"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<!-- Helper type -FixedSegmentValue: Defines possible fixed segment values -->
<xsd:simpleType name="FixedSegmentItemValueType">
<xsd:restriction base='xsd:string'>
<xsd:pattern value="\d*,\d*,\d*,\d*" />
</xsd:restriction>
</xsd:simpleType>
<!-- Helper type - FixedSegmentItemType -->
<xsd:complexType name="FixedSegmentItemType">
<xsd:complexContent>
<xsd:restriction base="anb:FixedItemType">
<xsd:attribute name="Name" use="required"
type="anb:SegmentItemNameType"/>
<xsd:attribute name="Value" use="required"
type="anb:FixedSegmentItemValueType "/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<!-- FixedTextRangeType is an extension of ContentLocatorPartType that locates
*
content within a FixedDocument. It contains one "Item" element with name
*
"Count" and value the number (N) of "Item" elements with name "SegmentXX"
*
that this ContentLocatorPart has. FixedTextRange locator part also
*
contains N "Item" elements with one attribute Name="SegmentXX" where XX is
*
a number from 0 to N-1 and one attribute "Value" in the form "X1, Y1, X2,
*
Y2". Here X1,Y1 are the coordinates of the start symbol in this segment,
*
X2,Y2 are the coordinates of the end symbol in this segment. Example:
*
*
<anb:FixedTextRange>
*
<anc:Item Name="Count" Value="2" />
*
<anc:Item Name="Segment0" Value="10,5,20,5" />
*
<anc:Item Name="Segment1" Value="25,15, 25,20" />
*
</anb:FixedTextRange>
-->
<xsd:complexType name="FixedTextRangeType">
<xsd:complexContent>
<xsd:extension base="anc:ContentLocatorPartType">
<xsd:sequence minOccurs="1" maxOccurs="unbounded">
<xsd:element name="Item" type="anb:FixedItemType" />
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<!-- FixedTextRange element substitutes ContentLocatorPart element -->
<xsd:element name="FixedTextRange" type="anb:FixedTextRangeType"

MCT: Luis Dueas

Pag 184 de 473

Manual de Windows Presentation Foundation


substitutionGroup="anc:ContentLocatorPart"/>
<!-- DataId -->
<!-- ValueItemNameType: helper type to define value item -->
<xsd:simpleType name="ValueItemNameType">
<xsd:restriction base='xsd:string'>
<xsd:pattern value="Value" />
</xsd:restriction>
</xsd:simpleType>
<!-- StringValueItemType -->
<xsd:complexType name="StringValueItemType">
<xsd:complexContent>
<xsd:restriction base="anc:ItemType">
<xsd:attribute name="Name" type="anb:ValueItemNameType" use="required"/>
<xsd:attribute name="Value" type="xsd:string" use="required"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="StringValueLocatorPartType">
<xsd:complexContent>
<xsd:extension base="anc:ContentLocatorPartType">
<xsd:sequence minOccurs="1" maxOccurs="1">
<xsd:element name="Item" type="anb:ValueItemType" />
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<!-- DataId element substitutes ContentLocatorPart and is used to locate a
*
subtree in the logical tree. Including DataId locator part in a
*
ContentLocator helps to narrow down the search for a particular content.
*
Examle of DataId ContentLocatorPart:
*
*
<anb:DataId>
*
<anc:Item Name="Value" Value="FlowDocument" />
*
</anb:DataId>
-->
<xsd:element name="DataId" type="anb: StringValueLocatorPartType "
substitutionGroup="anc:ContentLocatorPart"/>
<!-- PageNumber -->
<!-- NumberValueItemType -->
<xsd:complexType name="NumberValueItemType">
<xsd:complexContent>
<xsd:restriction base="anc:ItemType">
<xsd:attribute name="Name" type="anb:ValueItemNameType" use="required"/>
<xsd:attribute name="Value" type="anb:NumberType" use="required"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="NumberValueLocatorPartType">
<xsd:complexContent>
<xsd:extension base="anc:ContentLocatorPartType">
<xsd:sequence minOccurs="1" maxOccurs="1">
<xsd:element name="Item" type="anb:ValueItemType" />
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<-- PageNumber element substitutes ContentLocatorPart and is used to locate a
* page in a FixedDocument. PageNumber ContentLocatorPart is used in
*
conjunction with the FixedTextRange ContentLocatorPart and it shows on with
*
page are the coordinates defined in the FixedTextRange.
*
Example of a PageNumber ContentLocatorPart:
*
*
<anb:PageNumber>
*
<anc:Item Name="Value" Value="1" />
*
</anb:PageNumber>
-->
<xsd:element name="PageNumber" type="anb:NumbnerValueLocatorPartType"
substitutionGroup="anc:ContentLocatorPart"/>
<!-- ***** Content ***** -->
<!-- Highlight colors defines highlight color for annotations of type
*
Highlight or normal and active anchor colors for annotations of type
*
TextStickyNote and InkStickyNote.
-->
<xsd:complexType name="ColorsContentType">
<xsd:attribute name="Background" type='xsd:string' use="required" />
<xsd:attribute name="ActiveBackground" type='xsd:string' use="optional" />
</xsd:complexType>
<xsd:element name="Colors" type="anb:ColorsContentType"
substitutionGroup="anc:Content"/>
<!-- RTB Text contains XAML representing StickyNote Reach Text Box text.
*
Used in annotations of type TextStickyNote. -->
<xsd:complexType name="TextContentType">
<!-- See XAML schema for RTB content -->
</xsd:complexType>
<xsd:element name="Text" type="anb:TextContentType"

MCT: Luis Dueas

Pag 185 de 473

Manual de Windows Presentation Foundation


substitutionGroup="anc:Content"/>
<-- Ink contains XAML representing Sticky Note ink.
*
Used in annotations of type InkStickyNote. -->
<xsd:complexType name="InkContentType">
<!-- See XAML schema for Ink content -->
</xsd:complexType>
<xsd:element name="Ink" type="anb:InkContentType"
substitutionGroup="anc:Content"/>
<!-- SN Metadata defines StickyNote attributes as position width, height,
*
etc. Used in annotations of type TextStickyNote and InkStickyNote. -->
<xsd:complexType name="MetadataContentType">
<xsd:attribute name="Left" type='xsd:decimal' use="optional" />
<xsd:attribute name="Top" type='xsd:decimal' use="optional" />
<xsd:attribute name="Width" type='xsd:decimal' use="optional" />
<xsd:attribute name="Height" type='xsd:decimal' use="optional" />
<xsd:attribute name="XOffset" type='xsd:decimal' use="optional" />
<xsd:attribute name="YOffset" type='xsd:decimal' use="optional" />
<xsd:attribute name="ZOrder" type='xsd:decimal' use="optional" />
</xsd:complexType>
<xsd:element name="Metadata" type="anb:MetadataContentType"
substitutionGroup="anc:Content"/>
</xsd:schema>
Ejemplo de XML generado por XmlStreamStore de Annotations
En el cdigo XML siguiente se muestra la salida de un objeto XmlStreamStore de Annotations y la organizacin
de un archivo de ejemplo que contiene tres anotaciones: un resaltado, una nota rpida de texto y una nota
rpida manuscrita.
<?xml version="1.0" encoding="utf-8"?>
<anc:Annotations
xmlns:anc="http://schemas.microsoft.com/windows/annotations/2003/11/core"
xmlns:anb="http://schemas.microsoft.com/windows/annotations/2003/11/base">
<anc:Annotation Id="d308ea9b-36eb-4cc4-94d0-97634f10f7a2"
CreationTime="2006-09-13T18:28:51.4465702-07:00"
LastModificationTime="2006-09-13T18:28:51.4465702-07:00"
Type="anb:Highlight">
<anc:Anchors>
<anc:Resource Id="4f53661b-7328-4673-8e3f-c53f08b9cd94">
<anc:ContentLocator>
<anb:DataId>
<anc:Item Name="Value" Value="FlowDocument" />
</anb:DataId>
<anb:CharacterRange>
<anc:Item Name="Segment0" Value="600,609" />
<anc:Item Name="Count" Value="1" />
</anb:CharacterRange>
</anc:ContentLocator>
</anc:Resource>
</anc:Anchors>
</anc:Annotation>
<anc:Annotation Id="d7a8d271-387e-4144-9f8b-bc3c97816e5f"
CreationTime="2006-09-13T18:28:56.7903202-07:00"
LastModificationTime="2006-09-13T18:28:56.8996952-07:00"
Type="anb:TextStickyNote">
<anc:Authors>
<anb:StringAuthor>Denise Smith</anb:StringAuthor>
</anc:Authors>
<anc:Anchors>
<anc:Resource Id="dab2560e-6ebd-4ad0-80f9-483356a3be0b">
<anc:ContentLocator>
<anb:DataId>
<anc:Item Name="Value" Value="FlowDocument" />
</anb:DataId>
<anb:CharacterRange>
<anc:Item Name="Segment0" Value="787,801" />
<anc:Item Name="Count" Value="1" />
</anb:CharacterRange>
</anc:ContentLocator>
</anc:Resource>
</anc:Anchors>
<anc:Cargos>
<anc:Resource Id="ea4dbabd-b400-4cf9-8908-5716b410f9e4" Name="Meta Data">
<anb:MetaData anb:ZOrder="0" />
</anc:Resource>
</anc:Cargos>
</anc:Annotation>
<anc:Annotation Id="66803c69-b0d7-4cc3-bdff-cacc1955e806"
CreationTime="2006-09-13T18:29:03.6653202-07:00"
LastModificationTime="2006-09-13T18:29:03.7121952-07:00"
Type="anb:InkStickyNote">
<anc:Authors>
<anb:StringAuthor>Mike Nash</anb:StringAuthor>
</anc:Authors>
<anc:Anchors>
<anc:Resource Id="52251c53-8eeb-4fd7-b8f3-94e78dfc25fa">

MCT: Luis Dueas

Pag 186 de 473

Manual de Windows Presentation Foundation


<anc:ContentLocator>
<anb:DataId>
<anc:Item Name="Value" Value="FlowDocument" />
</anb:DataId>
<anb:CharacterRange>
<anc:Item Name="Segment0" Value="880,884" />
<anc:Item Name="Count" Value="1" />
</anb:CharacterRange>
</anc:ContentLocator>
</anc:Resource>
</anc:Anchors>
<anc:Cargos>
<anc:Resource Id="11e50b97-8d91-4ff9-82c3-16607b2b552b" Name="Meta Data">
<anb:MetaData anb:ZOrder="1" />
</anc:Resource>
</anc:Cargos>
</anc:Annotation>
</anc:Annotations>

6.4. Contenido Dinmico


Los elementos de contenido dinmico proporcionan las unidades de creacin para crear el contenido dinmico
apropiado para su hospedaje en un objeto FlowDocument.

6.4.1. Informacin General sobre Documentos Dinmicos


Los documentos dinmicos se han diseado para optimizar su presentacin y legibilidad. En lugar de
establecerse en un diseo predefinido, este tipo de documentos ajusta y recoloca dinmicamente su contenido
basndose en variables de tiempo de ejecucin, tales como el tamao de la ventana, la resolucin del
dispositivo y las preferencias opcionales del usuario. Adems, ofrecen caractersticas de documentos
avanzadas, como la paginacin y las columnas. En este tema se ofrece informacin general sobre los
documentos dinmicos y sobre cmo crearlos.
Qu es un documento dinmico
Un documento dinmico est diseado para "recolocar el contenido" dependiendo del tamao de la ventana, la
resolucin del dispositivo y otras variables de entorno. Adems, los documentos dinmicos tienen varias
caractersticas integradas que incluyen la bsqueda, modos de presentacin que optimizan la legibilidad y la
capacidad de cambiar el tamao y la apariencia de las fuentes. Este tipo de documentos son ptimos para su
uso cuando la facilidad de lectura constituye el principal escenario de consumo del documento. En cambio, los
documentos fijos estn diseados para tener una presentacin esttica. Los documentos fijos son tiles cuando
la fidelidad del contenido de origen resulta esencial.
La ilustracin siguiente muestra un documento dinmico de ejemplo visualizado en varias ventanas de tamaos
diferentes. A medida que el rea de presentacin cambia, el contenido se recoloca para utilizar el espacio
disponible del mejor modo posible.
Tal y como se muestra en la imagen anterior, el contenido dinmico puede incluir muchos componentes, entre
los que se incluyen prrafos, listas, imgenes, etc. Estos componentes corresponden a elementos de marcado y
a objetos del cdigo de procedimientos. Analizaremos estas clases detalladamente ms adelante en la seccin
Clases relacionadas con el flujo de esta introduccin.

MCT: Luis Dueas

Pag 187 de 473

Manual de Windows Presentation Foundation

De momento, aqu tiene un ejemplo de cdigo simple que crea un documento dinmico compuesto por un
prrafo con texto en negrita y una lista.
<!-- This simple flow document includes a paragraph with some
bold text in it and a list. -->
<FlowDocumentReader xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<FlowDocument>
<Paragraph>
<Bold>Some bold text in the paragraph.</Bold>
Some text that is not bold.
</Paragraph>
<List>
<ListItem>
<Paragraph>ListItem 1</Paragraph>
</ListItem>
<ListItem>
<Paragraph>ListItem 2</Paragraph>
</ListItem>
<ListItem>
<Paragraph>ListItem 3</Paragraph>
</ListItem>
</List>
</FlowDocument>
</FlowDocumentReader>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
namespace SDKSample
{
public partial class SimpleFlowExample : Page
{
public SimpleFlowExample()
{
Paragraph myParagraph = new Paragraph();
// Add some Bold text to the paragraph
myParagraph.Inlines.Add(new Bold(new Run("Some bold text in the paragraph.")));
// Add some plain text to the paragraph
myParagraph.Inlines.Add(new Run(" Some text that is not bold."));
// Create a List and populate with three list items.

MCT: Luis Dueas

Pag 188 de 473

Manual de Windows Presentation Foundation


List myList = new List();
// First create paragraphs to go into the list item.
Paragraph paragraphListItem1 = new Paragraph(new Run("ListItem 1"));
Paragraph paragraphListItem2 = new Paragraph(new Run("ListItem 2"));
Paragraph paragraphListItem3 = new Paragraph(new Run("ListItem 3"));
// Add ListItems with paragraphs in them.
myList.ListItems.Add(new ListItem(paragraphListItem1));
myList.ListItems.Add(new ListItem(paragraphListItem2));
myList.ListItems.Add(new ListItem(paragraphListItem3));
// Create a FlowDocument with the paragraph and list.
FlowDocument myFlowDocument = new FlowDocument();
myFlowDocument.Blocks.Add(myParagraph);
myFlowDocument.Blocks.Add(myList);
// Add the FlowDocument to a FlowDocumentReader Control
FlowDocumentReader myFlowDocumentReader = new FlowDocumentReader();
myFlowDocumentReader.Document = myFlowDocument;
this.Content = myFlowDocumentReader;
}

}
La ilustracin siguiente muestra la apariencia de este fragmento de cdigo.

En este ejemplo, el control FlowDocumentReader se utiliza para hospedar el contenido dinmico. Los elementos
Paragraph, List, ListItem y Bold se utilizan para controlar el formato del contenido, basndose en su orden en el
marcado. Por ejemplo, el elemento Bold abarca slo una parte del texto del prrafo; como resultado, slo ese
fragmento de texto est en negrita. Si ha utilizado HTML, esto le resultar familiar.
Tal y como se muestra en la ilustracin anterior, los documentos dinmicos tienen varias caractersticas
integradas:

Bsqueda: permite al usuario realizar una bsqueda de texto completo en un documento.

Modo de visualizacin: el usuario puede seleccionar su modo de visualizacin preferido, incluyendo el


modo de visualizacin de una sola pgina (una pgina a la vez), de dos pginas a la vez (formato de
lectura de libro) y de desplazamiento continuo (sin lmite).

Controles de navegacin de pginas: si el modo de visualizacin del documento utiliza pginas, los
controles de navegacin de pginas incluyen un botn que permite saltar a la pgina siguiente (flecha
abajo) o a la pgina anterior (flecha arriba), as como indicadores del nmero de pgina actual y el
nmero total de pginas. Tambin es posible pasar las pginas utilizando las teclas de direccin del
teclado.

Zoom: los controles de zoom permiten al usuario aumentar o reducir el nivel de zoom haciendo clic en
los botones con el signo ms y el signo menos, respectivamente. Los controles de zoom incluyen
tambin un control deslizante para ajustar el nivel de zoom.

Estas caractersticas se pueden modificar dependiendo del control utilizado para hospedar el contenido
dinmico. En la seccin siguiente se describen los distintos controles.
Tipos de documentos dinmicos
La presentacin y la apariencia del contenido en los documentos dinmicos dependern del objeto utilizado para
hospedar el contenido dinmico. Hay cuatro controles que permiten la visualizacin del contenido dinmico:
FlowDocumentReader, FlowDocumentPageViewer, RichTextBox y FlowDocumentScrollViewer. Estos controles se
describen brevemente a continuacin.

MCT: Luis Dueas

Pag 189 de 473

Manual de Windows Presentation Foundation


Nota: es necesario que FlowDocument hospede directamente el contenido dinmico, por lo que todos estos
controles utilizan un objeto FlowDocument para habilitar el alojamiento de dicho contenido.
FlowDocumentReader
FlowDocumentReader dispone de caractersticas que permiten al usuario seleccionar dinmicamente distintos
modos de visualizacin, incluido el modo de visualizacin de una sola pgina (una pgina a la vez), dos pginas
a la vez (formato de lectura de libro) y desplazamiento continuo (sin lmite). Si no necesita la capacidad de
cambiar dinmicamente entre distintos modos de visualizacin, FlowDocumentPageViewer y FlowDocument
ScrollViewer proporcionan visores de contenido dinmico ms ligeros que son fijos para un modo de
visualizacin concreto.
FlowDocumentPageViewer y FlowDocumentScrollViewer
FlowDocumentPageViewer muestra el contenido en el modo de visualizacin de una sola pgina, mientras que
FlowDocumentScrollViewer

muestra

el

contenido

en

modo

de

desplazamiento

continuo.

Tanto

FlowDocumentPageViewer como FlowDocumentScrollViewer son fijos para un modo de visualizacin concreto.


Puede compararlos con FlowDocumentReader, que incluye caractersticas que permiten al usuario elegir
dinmicamente entre varios modos de visualizacin (suministrados por la enumeracin FlowDocumentReader
ViewingMode), a costa de exigir un uso ms intensivo de recursos que FlowDocumentPageViewer o
FlowDocumentScrollViewer.
De manera predeterminada, se muestra siempre una barra de desplazamiento vertical y la barra de
desplazamiento horizontal se vuelve visible cuando es necesario. La interfaz de usuario predeterminada para
FlowDocumentScrollViewer no incluye barra de herramientas; sin embargo, se puede utilizar la propiedad
IsToolBarVisible para habilitar una barra de herramientas integrada.
RichTextBox
Utilice un control RichTextBox si desea que el usuario pueda editar el contenido dinmico. Por ejemplo, si desea
crear un editor que le permita al usuario manipular elementos como tablas, formatos de cursiva y negrita, etc.,
utilice RichTextBox.
Nota: el contenido dinmico existente dentro de un control RichTextBox no se comporta exactamente como el
contenido dinmico incluido en otros controles. Por ejemplo, en RichTextBox no hay ninguna columna, por lo
que no existe un comportamiento de cambio de tamao automtico. Adems, las caractersticas normalmente
integradas de contenido dinmico como la bsqueda, el modo de visualizacin, la navegacin de pginas y el
zoom no estn disponibles dentro de un elemento RichTextBox.
Crear contenido dinmico
El contenido dinmico puede ser complejo, estando compuesto por varios elementos como texto, imgenes,
tablas e incluso clases derivadas de UIElement como los controles. Los puntos siguientes resultan esenciales
para entender cmo se crea el contenido de flujo dinmico:

Clases relacionadas con el flujo: cada una de las clases utilizadas en el contenido dinmico tiene un
propsito concreto. Adems, la relacin jerrquica entre las clases dinmicas le ayuda a entender
cmo se utilizan. Por ejemplo, las clases derivadas de la clase Block se utilizan para contener otros
objetos, mientras que las clases derivadas de Inline contienen objetos que se muestran.

Esquema de contenido: un documento dinmico puede requerir un nmero sustancial de elementos


anidados. El esquema de contenido especifica las posibles relaciones de elemento primario/secundario
entre los elementos.

Clases relacionadas con el contenido dinmico


El diagrama siguiente muestra los objetos ms utilizados con el contenido dinmico:

MCT: Luis Dueas

Pag 190 de 473

Manual de Windows Presentation Foundation

En lo que al contenido dinmico se refiere, hay dos categoras importantes:


1.

Clases derivadas de bloque: tambin denominadas "elementos de contenido de bloque" o


simplemente "elementos de bloque". Los elementos que heredan de la clase Block se pueden utilizar
para agrupar elementos bajo un elemento primario comn o para aplicar atributos comunes a un
grupo.

2.

Clases derivadas inline: tambin denominadas "elementos de contenido inline " o simplemente
"elementos inline". Los elementos que heredan de Inline estn incluidos dentro de un elemento de
bloque o de otro elemento inline. Los elementos inline se utilizan a menudo como contenedor directo
del contenido que se representa en la pantalla. Por ejemplo, un elemento Paragraph (elemento de
bloque) puede contener un elemento Run (elemento inline), pero es Run el que contiene en realidad el
texto que se representa en la pantalla.

A continuacin se describe brevemente cada una de las clases de estas dos categoras.
Clases derivadas de bloque
Paragraph
Paragraph se utiliza normalmente para agrupar el contenido en un prrafo. El uso ms sencillo y comn de
Paragraph es crear un prrafo de texto.
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Paragraph>
Some paragraph text.
</Paragraph>
</FlowDocument>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
namespace SDKSample
{
public partial class ParagraphExample : Page
{
public ParagraphExample()
{
// Create paragraph with some text.
Paragraph myParagraph = new Paragraph();
myParagraph.Inlines.Add(new Run("Some paragraph text."));
// Create a FlowDocument and add the paragraph to it.
FlowDocument myFlowDocument = new FlowDocument();
myFlowDocument.Blocks.Add(myParagraph);
this.Content = myFlowDocument;
}
}
}
Sin embargo, tambin puede contener otros elementos inline derivados, como comprobar a continuacin.
Seccin
Section slo se utiliza para contener otros elementos derivados de Block. No aplica ningn formato
predeterminado a los elementos que contiene. Sin embargo, cualquier conjunto de valores establecidos en un
elemento Section se aplica a sus elementos secundarios. Asimismo, una seccin tambin le permite recorrer en

MCT: Luis Dueas

Pag 191 de 473

Manual de Windows Presentation Foundation


iteracin mediante programacin su coleccin secundaria. Section se utiliza de una manera similar a la etiqueta
< DIV > en HTML.
En el ejemplo siguiente, se definen tres prrafos debajo de un elemento Section. La seccin tiene un valor de
propiedad Background de Rojo, por lo que el color de fondo del prrafo tambin es rojo.
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- By default, Section applies no formatting to elements contained
within it. However, in this example, the section has a Background
property value of "Red", therefore, the three paragraphs (the block)
inside the section also have a red background. -->
<Section Background="Red">
<Paragraph>
Paragraph 1
</Paragraph>
<Paragraph>
Paragraph 2
</Paragraph>
<Paragraph>
Paragraph 3
</Paragraph>
</Section>
</FlowDocument>
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Documents;
namespace SDKSample
{
public partial class SectionExample : Page
{
public SectionExample()
{
// Create three paragraphs
Paragraph myParagraph1 = new Paragraph(new Run("Paragraph 1"));
Paragraph myParagraph2 = new Paragraph(new Run("Paragraph 2"));
Paragraph myParagraph3 = new Paragraph(new Run("Paragraph 3"));
// Create a Section and add the three paragraphs to it.
Section mySection = new Section();
mySection.Background = Brushes.Red;
mySection.Blocks.Add(myParagraph1);
mySection.Blocks.Add(myParagraph2);
mySection.Blocks.Add(myParagraph3);
// Create a FlowDocument and add the section to it.
FlowDocument myFlowDocument = new FlowDocument();
myFlowDocument.Blocks.Add(mySection);
this.Content = myFlowDocument;
}
}
}
BlockUIContainer
BlockUIContainer permite incrustar elementos UIElement (por ejemplo, un elemento Button) en contenido
dinmico derivado de bloque. InlineUIContainer se utiliza para incrustar elementos UIElement en contenido
dinmico derivado inline. BlockUIContainer y InlineUIContainer son importantes porque no hay ninguna otra
manera de utilizar un elemento UIElement en el contenido dinmico a menos que est incluido en uno de estos
dos elementos.
El ejemplo siguiente muestra cmo utilizar el elemento BlockUIContainer para hospedar los objetos UIElement
dentro del contenido dinmico.
<FlowDocument ColumnWidth="400">
<Section Background="GhostWhite">
<Paragraph>
A UIElement element may be embedded directly in flow content
by enclosing it in a BlockUIContainer element.
</Paragraph>
<BlockUIContainer>
<Button>Click me!</Button>
</BlockUIContainer>
<Paragraph>
The BlockUIContainer element may host no more than one top-level
UIElement. However, other UIElements may be nested within the
UIElement contained by an BlockUIContainer element. For example,
a StackPanel can be used to host multiple UIElement elements within
a BlockUIContainer element.

MCT: Luis Dueas

Pag 192 de 473

Manual de Windows Presentation Foundation


</Paragraph>
<BlockUIContainer>
<StackPanel>
<Label Foreground="Blue">Choose a value:</Label>
<ComboBox>
<ComboBoxItem IsSelected="True">a</ComboBoxItem>
<ComboBoxItem>b</ComboBoxItem>
<ComboBoxItem>c</ComboBoxItem>
</ComboBox>
<Label Foreground ="Red">Choose a value:</Label>
<StackPanel>
<RadioButton>x</RadioButton>
<RadioButton>y</RadioButton>
<RadioButton>z</RadioButton>
</StackPanel>
<Label>Enter a value:</Label>
<TextBox>
A text editor embedded in flow content.
</TextBox>
</StackPanel>
</BlockUIContainer>
</Section>
</FlowDocument>
En la ilustracin siguiente se muestra cmo se representa este ejemplo.

List
List se utiliza para crear una lista con vietas o numrica. Establezca la propiedad MarkerStyle en un valor de
enumeracin TextMarkerStyle para determinar el estilo de la lista. En el siguiente ejemplo se muestra cmo
crear una lista sencilla.
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<List>
<ListItem>
<Paragraph>
List Item 1
</Paragraph>
</ListItem>
<ListItem>
<Paragraph>
List Item 2
</Paragraph>
</ListItem>
<ListItem>
<Paragraph>
List Item 3
</Paragraph>
</ListItem>
</List>
</FlowDocument>
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Documents;
namespace SDKSample
{
public partial class ListExample : Page
{
public ListExample()
{

MCT: Luis Dueas

Pag 193 de 473

Manual de Windows Presentation Foundation

// Create three paragraphs


Paragraph myParagraph1 = new Paragraph(new Run("List
Paragraph myParagraph2 = new Paragraph(new Run("List
Paragraph myParagraph3 = new Paragraph(new Run("List
// Create the ListItem elements for the List and add
// paragraphs to them.
ListItem myListItem1 = new ListItem();
myListItem1.Blocks.Add(myParagraph1);
ListItem myListItem2 = new ListItem();
myListItem2.Blocks.Add(myParagraph2);
ListItem myListItem3 = new ListItem();
myListItem3.Blocks.Add(myParagraph3);
// Create a List and add the three ListItems to it.
List myList = new List();
myList.ListItems.Add(myListItem1);
myList.ListItems.Add(myListItem2);
myList.ListItems.Add(myListItem3);
// Create a FlowDocument and add the section to it.
FlowDocument myFlowDocument = new FlowDocument();
myFlowDocument.Blocks.Add(myList);
this.Content = myFlowDocument;

Item 1"));
Item 2"));
Item 3"));
the

}
}
Nota: List es el nico elemento dinmico que utiliza ListItemCollection para administrar los elementos
secundarios.
Table
Table se utiliza para crear una tabla. Table es similar al elemento Grid, pero tiene ms funciones, por lo que
requiere un mayor consumo de recursos. Dado que Grid es un elemento UIElement, no se puede utilizar en el
contenido dinmico a menos que est incluido en un elemento BlockUIContainer o InlineUIContainer.
Clases derivadas inline
Run
Run se utiliza para contener texto sin formato. Podra pensarse que los objetos Run se utilizan extensivamente
en el contenido dinmico; sin embargo, en el marcado no es necesario utilizar los elementos Run de forma
explcita. Por ejemplo, en el marcado siguiente, el primer elemento Paragraph especifica explcitamente el
elemento Run, mientras que el segundo no lo hace. Ambos prrafos generan el mismo resultado.
<Paragraph>
<Run>Paragraph that explicitly uses the Run element.</Run>
</Paragraph>
<Paragraph>
This Paragraph omits the the Run element in markup. It renders
the same as a Paragraph with Run used explicitly.
</Paragraph>
Nota: es necesario utilizar Run al crear o manipular documentos dinmicos mediante cdigo.
Span
Span agrupa otros elementos de contenido inline. No se aplica ninguna representacin inherente al contenido
dentro de un elemento Span. Sin embargo, los elementos que heredan de Span, incluyendo Hyperlink, Bold,
Italic y Underline, aplican formato al texto.
A continuacin se muestra un ejemplo de un objeto Span utilizado para incluir contenido inline, incluyendo
texto, un elemento Bold y un elemento Button.
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Paragraph>
Text before the Span. <Span Background="Red">Text within the Span is
red and <Bold>this text is inside the Span-derived element Bold.</Bold>
A Span can contain more then text, it can contain any inline content. For
example, it can contain a
<InlineUIContainer>
<Button>Button</Button>
</InlineUIContainer>
or other UIElement, a Floater, a Figure, etc.</Span>
</Paragraph>
</FlowDocument>
La captura de pantalla siguiente muestra cmo se representa este ejemplo.

MCT: Luis Dueas

Pag 194 de 473

Manual de Windows Presentation Foundation

InlineUIContainer
InlineUIContainer permite incrustar elementos UIElement (por ejemplo, un control como Button) en un
elemento de contenido Inline. Este elemento es el equivalente inline al elemento BlockUIContainer descrito
anteriormente. A continuacin se muestra un ejemplo que utiliza InlineUIContainer para insertar un control
Button inline en un elemento Paragraph.
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Paragraph>
Text to precede the button...
<!-- Set the BaselineAlignment property to "Bottom"
so that the Button aligns properly with the text. -->
<InlineUIContainer BaselineAlignment="Bottom">
<Button>Button</Button>
</InlineUIContainer>
Text to follow the button...
</Paragraph>
</FlowDocument>
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Documents;
namespace SDKSample
{
public partial class InlineUIContainerExample : Page
{
public InlineUIContainerExample()
{
Run run1 = new Run(" Text to precede the button... ");
Run run2 = new Run(" Text to follow the button... ");
// Create a new button to be hosted in the paragraph.
Button myButton = new Button();
myButton.Content = "Click me!";
// Create a new InlineUIContainer to contain the Button.
InlineUIContainer myInlineUIContainer = new InlineUIContainer();
// Set the BaselineAlignment property to "Bottom" so that the
// Button aligns properly with the text.
myInlineUIContainer.BaselineAlignment = BaselineAlignment.Bottom;
// Asign the button as the UI container's child.
myInlineUIContainer.Child = myButton;
// Create the paragraph and add content to it.
Paragraph myParagraph = new Paragraph();
myParagraph.Inlines.Add(run1);
myParagraph.Inlines.Add(myInlineUIContainer);
myParagraph.Inlines.Add(run2);
// Create a FlowDocument and add the paragraph to it.
FlowDocument myFlowDocument = new FlowDocument();
myFlowDocument.Blocks.Add(myParagraph);
this.Content = myFlowDocument;
}
}
}
Nota: no es necesario utilizar InlineUIContainer explcitamente en el marcado. Si lo omite, se crear de todas
formas un elemento InlineUIContainer al compilar el cdigo.
Figure y Floater
Figure y Floater se utilizan para incrustar contenido en documentos dinmicos con propiedades de posicin que
se pueden personalizar de forma independiente del flujo de contenido primario. Los elementos Figure o Floater
se utilizan a menudo para resaltar o recalcar partes de contenido, hospedar imgenes auxiliares u otro
contenido dentro del flujo de contenido principal, o bien para insertar contenido de relacin indirecta, como los
anuncios.
El ejemplo siguiente muestra cmo incrustar un elemento Figure en un prrafo de texto.
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Paragraph>
<Figure

MCT: Luis Dueas

Pag 195 de 473

Manual de Windows Presentation Foundation


Width="300" Height="100"
Background="GhostWhite" HorizontalAnchor="PageLeft" >
<Paragraph FontStyle="Italic" Background="Beige" Foreground="DarkGreen" >
A Figure embeds content into flow content with placement properties
that can be customized independently from the primary content flow
</Paragraph>
</Figure>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy
nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi
enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis
nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure.
</Paragraph>
</FlowDocument>
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Documents;
namespace SDKSample
{
public partial class FigureExample : Page
{
public FigureExample()
{
// Create strings to use as content.
string strFigure = "A Figure embeds content into flow content with" +
" placement properties that can be customized" +
" independently from the primary content flow";
string strOther = "Lorem ipsum dolor sit amet, consectetuer adipiscing" +
" elit, sed diam nonummy nibh euismod tincidunt ut laoreet" +
" dolore magna aliquam erat volutpat. Ut wisi enim ad" +
" minim veniam, quis nostrud exerci tation ullamcorper" +
" suscipit lobortis nisl ut aliquip ex ea commodo consequat." +
" Duis autem vel eum iriure.";
// Create a Figure and assign content and layout properties to it.
Figure myFigure = new Figure();
myFigure.Width = new FigureLength(300);
myFigure.Height = new FigureLength(100);
myFigure.Background = Brushes.GhostWhite;
myFigure.HorizontalAnchor = FigureHorizontalAnchor.PageLeft;
Paragraph myFigureParagraph = new Paragraph(new Run(strFigure));
myFigureParagraph.FontStyle = FontStyles.Italic;
myFigureParagraph.Background = Brushes.Beige;
myFigureParagraph.Foreground = Brushes.DarkGreen;
myFigure.Blocks.Add(myFigureParagraph);
// Create the paragraph and add content to it.
Paragraph myParagraph = new Paragraph();
myParagraph.Inlines.Add(myFigure);
myParagraph.Inlines.Add(new Run(strOther));
// Create a FlowDocument and add the paragraph to it.
FlowDocument myFlowDocument = new FlowDocument();
myFlowDocument.Blocks.Add(myParagraph);
this.Content = myFlowDocument;
}
}
}
En la ilustracin siguiente se muestra cmo se representa este ejemplo.

Los elementos Figure y Floater son distintos y se utilizan en situaciones diferentes.


Elemento Figure:

Se puede determinar su posicin: puede establecer sus puntos de anclaje horizontales y verticales
para acoplarlo con respecto a la pgina, el contenido, la columna o el prrafo. Tambin puede utilizar
sus propiedades HorizontalOffset y VerticalOffset para especificar desplazamientos arbitrarios.

Se puede ajustar su tamao a varias columnas: puede establecer el alto y el ancho del elemento
Figure en mltiplos de los valores de alto o ancho de la pgina, el contenido o la columna. Observe que
en el caso de la pgina y del contenido, no se permiten mltiplos mayores que 1. Por ejemplo, puede
establecer el ancho de Figure en "0,5 page", "0,25 content" o "2 Column". Tambin puede establecer
el alto y el ancho en valores absolutos de pxel.

MCT: Luis Dueas

Pag 196 de 473

Manual de Windows Presentation Foundation

No pagina: si el contenido de un elemento Figure no cabe en Figure, se representar el contenido que


quepa y se perder el contenido restante.

Elemento Floater:

No se puede determinar su posicin y se representar en cualquier espacio que est disponible. No se


puede establecer el desplazamiento ni los puntos de anclaje de un elemento Floater.

No se puede ajustar su tamao a ms de una columna: de forma predeterminada, Floater ajusta su


tamao a una columna. Su propiedad Width puede establecerse en un valor absoluto de pxel, pero si
este valor es mayor que el ancho de una columna, se omite y el tamao del elemento Floater se ajusta
a una columna. Su tamao puede establecerse en menos de una columna configurando el ancho de
pxel correcto, pero el tamao no es relativo a la columna, por lo que "0,5Column" no es una expresin
vlida para el ancho de Floater. El elemento Floater no tiene una propiedad que especifique el alto: su
alto no puede establecerse ya que depende del contenido.

Floater pagina: si su contenido en el ancho especificado se extiende ms all del alto de una columna,
el elemento Floater se parte y salta a la siguiente columna, la siguiente pgina, etc.

Figure se presta especialmente para contenido independiente cuyo tamao y posicin desea controlar, ya que le
da la seguridad de que el contenido se ajustar al tamao especificado. Floater se presta especialmente para
contenido que fluye ms libremente y de manera similar al contenido de la pgina principal, pero que es
independiente de este ltimo.
LineBreak
LineBreak origina un salto de lnea en el contenido dinmico. El siguiente ejemplo muestra el uso de LineBreak.
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Paragraph>
Before the LineBreak in Paragraph.
<LineBreak />
After the LineBreak in Paragraph.
<LineBreak/><LineBreak/>
After two LineBreaks in Paragraph.
</Paragraph>
<Paragraph>
<LineBreak/>
</Paragraph>
<Paragraph>
After a Paragraph with only a LineBreak in it.
</Paragraph>
</FlowDocument>
La captura de pantalla siguiente muestra cmo se representa este ejemplo.

Elementos de coleccin dinmica


En muchos de los ejemplos anteriores, los elementos BlockCollection y InlineCollection se utilizan para generar
contenido dinmico mediante programacin. Por ejemplo, para agregar elementos a un objeto Paragraph,
puede utilizar la sintaxis siguiente:

myParagraph.Inlines.Add(new Run("Some text"));

Esto agrega un elemento Run al elemento InlineCollection del objeto Paragraph. Esto es lo mismo que el
elemento Run implcito incluido en un objeto Paragraph en el marcado:

<Paragraph>
Some Text

MCT: Luis Dueas

Pag 197 de 473

Manual de Windows Presentation Foundation


</Paragraph>

Como ejemplo de uso del elemento BlockCollection, el ejemplo siguiente crea un nuevo elemento Section y, a
continuacin, utiliza el mtodo Add para agregar un nuevo elemento Paragraph al contenido de Section.
Section secx = new Section();
secx.Blocks.Add(new Paragraph(new Run("A bit of text content...")));
Adems de agregar elementos a una coleccin dinmica, tambin se pueden quitar. En el siguiente ejemplo se
elimina el ltimo elemento Inline de Span.
spanx.Inlines.Remove(spanx.Inlines.LastInline);
En el siguiente ejemplo se borra todo el contenido (los elementos Inline) de Span.
spanx.Inlines.Clear();
Al trabajar con contenido dinmico mediante programacin, probablemente realizar un uso extensivo de estas
colecciones.
Que un elemento dinmico utilice un elemento InlineCollection (Inlines) o un elemento BlockCollection (Blocks)
para contener sus elementos secundarios depender de qu tipo de elementos secundarios (Block o Inline)
puedan ser incluidos en el elemento primario. Las reglas de contencin para los elementos de contenido
dinmico se resumen en el esquema de contenido de la seccin siguiente.
Nota: existe un tercer tipo de coleccin utilizado con el contenido dinmico, ListItemCollection, pero esta
coleccin slo se usa con un elemento List. Adems, hay varias colecciones que se utilizan con Table.
Esquema de contenido
Dado el nmero de elementos de contenido dinmico diferentes que existen, puede resultar muy complicado
efectuar un seguimiento del tipo de elementos secundarios que puede contener un elemento. El diagrama
siguiente resume las reglas de contencin para los elementos dinmicos. Las flechas representan las posibles
relaciones entre elementos primarios/secundarios.

Como se puede ver en el diagrama anterior, los elementos secundarios permitidos para un elemento no estn
determinados necesariamente por el hecho de que ste sea un elemento Block o Inline. Por ejemplo, un objeto
Span (un elemento Inline) slo puede tener elementos secundarios Inline, mientras que un objeto Figure
(tambin un elemento Inline) slo puede tener elementos secundarios Block. Por consiguiente, un diagrama es
til para determinar rpidamente qu elemento puede incluirse en otro. Como ejemplo, utilicemos el diagrama
para determinar cmo construir el contenido dinmico de un elemento RichTextBox.
1. Un objeto RichTextBox debe contener un elemento FlowDocument, que a su vez debe contener un objeto
derivado de Block. A continuacin se muestra el segmento correspondiente del diagrama anterior.

MCT: Luis Dueas

Pag 198 de 473

Manual de Windows Presentation Foundation

Llegados a este punto, esta es la apariencia que podra tener el marcado.


<RichTextBox>
<FlowDocument>
<!-- One or more Block-derived object -->
</FlowDocument>
</RichTextBox>
2. Segn el diagrama, hay varios elementos Block entre los que elegir, incluyendo Paragraph, Section, Table,
List y BlockUIContainer. Supongamos que deseamos un elemento Table. Segn el diagrama anterior, un objeto
Table incluye un elemento TableRowGroup que contiene elementos TableRow, que a su vez contienen
elementos TableCell que incluyen un objeto derivado de Block. A continuacin se muestra el segmento
correspondiente para el elemento Table tomado del diagrama anterior.

A continuacin se muestra el marcado correspondiente.


<RichTextBox>
<FlowDocument>
<Table>
<TableRowGroup>
<TableRow>
<TableCell>
<!-- One or more Block-derived object -->
</TableCell>
</TableRow>
</TableRowGroup>
</Table>
</FlowDocument>
</RichTextBox>
3. De nuevo, se requieren uno o varios elementos Block debajo de un elemento TableCell. Para facilitar el
proceso, coloquemos texto dentro de la celda. Podemos hacerlo utilizando un objeto Paragraph con un
elemento Run. A continuacin se muestran los segmentos correspondientes del diagrama que demuestran que
un objeto Paragraph puede aceptar un elemento Inline y que un objeto Run (un elemento Inline) slo puede
aceptar texto sin formato.

A continuacin se muestra el ejemplo completo en el marcado.


<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<RichTextBox>
<FlowDocument>
<!-- Normally a table would have multiple rows and multiple
cells but this code is for demonstration purposes.-->
<Table>
<TableRowGroup>
<TableRow>
<TableCell>
<Paragraph>
<!-- The schema does not actually require
explicit use of the Run tag in markup. It
is only included here for clarity. -->
<Run>Paragraph in a Table Cell.</Run>
</Paragraph>
</TableCell>
</TableRow>
</TableRowGroup>
</Table>
</FlowDocument>
</RichTextBox>
</Page>
Personalizar el texto
Normalmente, el tipo de contenido ms abundante en un documento dinmico es el texto. Aunque los objetos
descritos anteriormente se pueden utilizar para controlar la mayora de los aspectos de la representacin del
texto, hay otros mtodos para personalizar el texto que se describen en esta seccin.

MCT: Luis Dueas

Pag 199 de 473

Manual de Windows Presentation Foundation


Decoraciones de texto
Las decoraciones de texto le permiten aplicar efectos de subrayado, de lnea alta, de lnea base y de tachado al
texto (vea las imgenes siguientes). Estas decoraciones se agregan utilizando la propiedad TextDecorations
expuesta por varios objetos, como Inline, Paragraph, TextBlock y TextBox.
En el ejemplo siguiente se muestra cmo establecer la propiedad TextDecorations de un objeto Paragraph.
<FlowDocument ColumnWidth="200">
<Paragraph TextDecorations="Strikethrough">
This text will render with the strikethrough effect.
</Paragraph>
</FlowDocument>
Paragraph parx = new Paragraph(new Run("This text will render with the strikethrough
effect."));
parx.TextDecorations = TextDecorations.Strikethrough;
En la ilustracin siguiente se muestra cmo se representa este ejemplo.

Las ilustraciones siguientes muestran cmo se representan las decoraciones Lnea alta, Lnea base y
Subrayado, respectivamente.

Tipografa
La propiedad Typography la expone la mayora del contenido relacionado con el flujo, como TextElement,
FlowDocument, TextBlock y TextBox. Esta propiedad se utiliza para controlar caractersticas/variaciones
tipogrficas del texto (por ejemplo, maysculas pequeas o grandes, generacin de superndices y subndices,
etc.).
En el ejemplo siguiente se muestra cmo establecer el atributo Typography usando Paragraph como elemento
de ejemplo.
<Paragraph
TextAlignment="Left"
FontSize="18"
FontFamily="Palatino Linotype"
Typography.NumeralStyle="OldStyle"
Typography.Fraction="Stacked"
Typography.Variants="Inferior">
<Run>
This text has some altered typography characteristics. Note
that use of an open type font is necessary for most typographic
properties to be effective.
</Run>
<LineBreak/><LineBreak/>
<Run>
0123456789 10 11 12 13
</Run>
<LineBreak/><LineBreak/>
<Run>
1/2 2/3 3/4
</Run>
</Paragraph>
En la ilustracin siguiente se muestra cmo se representa este ejemplo.

En contraste, en la ilustracin siguiente se muestra cmo se representa un ejemplo similar con propiedades
tipogrficas predeterminadas.

MCT: Luis Dueas

Pag 200 de 473

Manual de Windows Presentation Foundation

En el ejemplo siguiente se muestra cmo establecer la propiedad Typography mediante programacin.


Paragraph par = new Paragraph();
Run runText = new Run(
"This text has some altered typography characteristics. Note" +
"that use of an open type font is necessary for most typographic" +
"properties to be effective.");
Run runNumerals = new Run("0123456789 10 11 12 13");
Run runFractions = new Run("1/2 2/3 3/4");
par.Inlines.Add(runText);
par.Inlines.Add(new LineBreak());
par.Inlines.Add(new LineBreak());
par.Inlines.Add(runNumerals);
par.Inlines.Add(new LineBreak());
par.Inlines.Add(new LineBreak());
par.Inlines.Add(runFractions);
par.TextAlignment = TextAlignment.Left;
par.FontSize = 18;
par.FontFamily = new FontFamily("Palatino Linotype");
par.Typography.NumeralStyle = FontNumeralStyle.OldStyle;
par.Typography.Fraction = FontFraction.Stacked;
par.Typography.Variants = FontVariants.Inferior;

6.4.2. Informacin General sobre el Modelo de Contenido de TextElement


En esta informacin general del modelo de contenido se describe el contenido compatible con TextElement. La
clase Paragraph es un tipo de TextElement. Un modelo de contenido describe qu objetos o elementos se
pueden contener en otros. En esta informacin general se resume el modelo de contenido utilizado para los
objetos derivados de TextElement.
Diagrama del modelo de contenido
En el diagrama siguiente se resume el modelo de contenido para las clases derivadas de TextElement, y
tambin cmo encajan en este modelo otras clases que no son TextElement.

Como se puede observar en el diagrama anterior, los elementos secundarios que se permiten para un elemento
no vienen determinados necesariamente por el hecho de si una clase se deriva de la clase Block o de una clase
Inline. Por ejemplo, Span (una clase derivada de Inline) nicamente puede tener elementos secundarios Inline,
pero una clase Figure (tambin derivada de Inline) nicamente puede tener elementos secundarios Block. Por
consiguiente, un diagrama es til para determinar rpidamente qu elemento puede incluirse en otro. Como

MCT: Luis Dueas

Pag 201 de 473

Manual de Windows Presentation Foundation


ejemplo, utilicemos el diagrama para determinar cmo construir el contenido dinmico de un elemento
RichTextBox.
1.

Un objeto RichTextBox debe contener un elemento FlowDocument, que a su vez debe contener un
objeto derivado de Block. A continuacin, se muestra el segmento correspondiente del diagrama
anterior.

Llegados a este punto, ste es el aspecto que podra tener el marcado.


<RichTextBox>
<FlowDocument>
<!-- One or more Block-derived object -->
</FlowDocument>
</RichTextBox>
2.

Segn el diagrama, hay varios elementos Block entre los que elegir, incluidos Paragraph, Section,
Table, List y BlockUIContainer (vea las clases derivadas de Block en el diagrama anterior).
Supongamos que deseamos un elemento Table. Segn el diagrama anterior, un objeto Table incluye
un elemento TableRowGroup que contiene elementos TableRow, que a su vez contienen elementos
TableCell que incluyen un objeto derivado de Block. A continuacin se muestra el segmento
correspondiente para el elemento Table tomado del diagrama anterior.

A continuacin, se muestra el marcado correspondiente.


<RichTextBox>
<FlowDocument>
<Table>
<TableRowGroup>
<TableRow>
<TableCell>
<!-- One or more Block-derived object -->
</TableCell>
</TableRow>
</TableRowGroup>
</Table>
</FlowDocument>
</RichTextBox>
3.

De nuevo, se requieren uno o ms elementos Block debajo de un elemento TableCell. Para facilitar el
proceso, coloquemos texto dentro de la celda. Podemos hacerlo utilizando un objeto Paragraph con un
elemento Run. A continuacin se muestran los segmentos correspondientes del diagrama que
demuestran que un objeto Paragraph puede aceptar un elemento Inline y que un objeto Run (un
elemento Inline) slo puede aceptar texto sin formato.

A continuacin se muestra el ejemplo completo en el marcado.


<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<RichTextBox>
<FlowDocument>
<!-- Normally a table would have multiple rows and multiple
cells but this code is for demonstration purposes.-->
<Table>
<TableRowGroup>
<TableRow>
<TableCell>
<Paragraph>
<!-- The schema does not actually require
explicit use of the Run tag in markup. It
is only included here for clarity. -->
<Run>Paragraph in a Table Cell.</Run>
</Paragraph>
</TableCell>
</TableRow>
</TableRowGroup>

MCT: Luis Dueas

Pag 202 de 473

Manual de Windows Presentation Foundation


</Table>
</FlowDocument>
</RichTextBox>
</Page>
Trabajar mediante programacin con contenido de TextElement
El contenido de un elemento TextElement est compuesto por colecciones; en consecuencia, para manipular
mediante programacin el contenido de los objetos TextElement se trabaja con estas colecciones. Hay tres
colecciones diferentes utilizadas por las clases derivadas de TextElement:

InlineCollection: representa una coleccin de elementos Inline. InlineCollection define el contenido


secundario admitido de los elementos Paragraph, Span y TextBlock.

BlockCollection: representa una coleccin de elementos Block. BlockCollection define el contenido


secundario admitido de los elementos FlowDocument, Section, ListItem, TableCell, Floater y Figure.

ListItemCollection: elemento de contenido dinmico que representa un elemento de contenido


concreto en una List ordenada o sin ordenar.

Puede manipular estas colecciones (agregar o quitar elementos) utilizando las propiedades respectivas de
Inlines, Blocks y ListItems. En los ejemplos siguientes se muestra cmo manipular el contenido de un objeto
Span mediante la propiedad Inlines.
Nota:
El objeto Table utiliza varias colecciones para manipular su contenido, pero no se abordan aqu.
En el ejemplo siguiente se crea un nuevo objeto Span y, a continuacin, se utiliza el mtodo Add para agregar
dos series de texto como elementos de contenido secundarios de Span.
Span spanx = new Span();
spanx.Inlines.Add(new Run("A bit of text content..."));
spanx.Inlines.Add(new Run("A bit more text content..."));
En el ejemplo siguiente se crea un nuevo elemento Run y se inserta al principio de Span.
Run runx = new Run("Text to insert...");
spanx.Inlines.InsertBefore(spanx.Inlines.FirstInline, runx);
En el siguiente ejemplo se elimina el ltimo elemento Inline de Span.
spanx.Inlines.Remove(spanx.Inlines.LastInline);
En el siguiente ejemplo se borra todo el contenido (los elementos Inline) de Span.
spanx.Inlines.Clear();
Tipos que comparten este modelo de contenido
Los tipos siguientes heredan de la clase TextElement y se utilizan para mostrar el contenido descrito en esta
informacin general.
Bold, Figure, Floater, Hyperlink, InlineUIContainer, Italic, LineBreak, List, ListItem, Paragraph, Run, Section,
Span, Table, Underline.
Observe que esta lista incluye nicamente los tipos no abstractos distribuidos con Windows SDK. Puede utilizar
otros tipos que heredan de TextElement.

6.4.3. Informacin General sobre Tablas


Table es un elemento de nivel de bloque que admite la presentacin basada en cuadrcula de contenido de
documentos dinmicos. La flexibilidad de este elemento lo hace muy til, pero tambin ms complicado de
entender y utilizar correctamente.
Fundamentos de tablas
En qu se diferencian las tablas de las cuadrculas?
Table y Grid comparten funcionalidades comunes, pero cada elemento es ms apropiado para escenarios
diferentes. Table se ha diseado para su uso en contenido dinmico. Las cuadrculas son ms apropiadas para

MCT: Luis Dueas

Pag 203 de 473

Manual de Windows Presentation Foundation


formularios (bsicamente, en cualquier lugar excepto en el contenido dinmico). Dentro de un FlowDocument,
una Table admite los comportamientos del contenido dinmico, como la paginacin, el ajuste dinmico de
columnas y la seleccin de contenido, mientras que Grid, no. Por su parte, Grid es ms apropiada fuera de un
FlowDocument por numerosas razones; una de ellas es que Grid agrega elementos basndose en un ndice de
fila y columna, y Table, no. El elemento Grid permite la disposicin en capas del contenido secundario, lo que
permite que haya ms de un elemento dentro de una sola "celda". Table no admite la disposicin en capas. Los
elementos secundarios de Grid se pueden colocar de manera absoluta en relacin con el rea de los lmites de
la "celda". Table no admite esta caracterstica. Finalmente, Grid requiere menos los recursos que Table, por lo
que puede ser conveniente utilizar Grid para mejorar el rendimiento.
Estructura bsica de las tablas
Table proporciona una presentacin basada en cuadrcula que est compuesto de columnas (representadas por
elementos TableColumn) y filas (representadas por elementos TableRow). Los elementos TableColumn no
hospedan contenido; simplemente definen las columnas y sus caractersticas. Los elementos TableRow se
deben hospedar en un elemento TableRowGroup, que define una agrupacin de filas para la tabla. Los
elementos TableCell, que incluyen el contenido real que la tabla va a presentar, se deben hospedar en un
elemento TableRow. TableCell slo puede contener elementos que se derivan de Block. Entre los elementos
secundarios vlidos de TableCell se incluyen:

BlockUIContainer
List
Paragraph
Section
Table

Nota:
Los elementos TableCell no pueden hospedar directamente contenido de texto.
Nota:
Table es similar al elemento Grid pero tiene ms funciones y, por consiguiente, requiere mayor consumo de
recursos.
En el ejemplo siguiente se define una tabla simple de 2 x 3, con XAML.
<!-Table is a Block element, and as such must be hosted in a container
for Block elements. FlowDocument provides such a container.
-->
<FlowDocument>
<Table>
<!-This table has 3 columns, each described by a TableColumn
element nested in a Table.Columns collection element.
-->
<Table.Columns>
<TableColumn />
<TableColumn />
<TableColumn />
</Table.Columns>
<!-This table includes a single TableRowGroup which hosts 2 rows,
each described by a TableRow element.
-->
<TableRowGroup>
<!-Each of the 2 TableRow elements hosts 3 cells, described by
TableCell elements.
-->
<TableRow>
<TableCell>
<!-TableCell elements may only host elements derived from Block.
In this example, Paragaph elements serve as the ultimate content
containers for the cells in this table.
-->
<Paragraph>Cell at Row 1 Column 1</Paragraph>
</TableCell>

MCT: Luis Dueas

Pag 204 de 473

Manual de Windows Presentation Foundation


<TableCell>
<Paragraph>Cell
</TableCell>
<TableCell>
<Paragraph>Cell
</TableCell>
</TableRow>
<TableRow>
<TableCell>
<Paragraph>Cell
</TableCell>
<TableCell>
<Paragraph>Cell
</TableCell>
<TableCell>
<Paragraph>Cell
</TableCell>
</TableRow>
</TableRowGroup>
</Table>
</FlowDocument>

at Row 1 Column 2</Paragraph>


at Row 1 Column 3</Paragraph>

at Row 2 Column 1</Paragraph>


at Row 2 Column 2</Paragraph>
at Row 2 Column 3</Paragraph>

En la ilustracin siguiente se muestra cmo se representa este ejemplo.

Contencin de tablas
Table se deriva del elemento Block y cumple las reglas comunes para los elementos de nivel de bloque, Block.
Cualquiera de los elementos siguientes puede contener un elemento Table:

FlowDocument
TableCell
ListBoxItem
ListViewItem
Section
Floater
Figure

Agrupaciones de filas
El elemento TableRowGroup proporciona una manera de agrupar filas arbitrariamente dentro de una tabla;
cada fila de una tabla debe pertenecer a una agrupacin de filas. Las filas dentro de un grupo de filas
comparten a menudo una finalidad comn y se les puede asignar un estilo como grupo. Una costumbre habitual
para las agrupaciones de filas es separar las filas que tienen una finalidad especial, como ttulos, encabezados y
filas del pie de pgina, del contenido principal incluido en la tabla.
En el ejemplo siguiente se utiliza XAML para definir una tabla con filas de encabezado y pie de pgina con
estilo.
<Table>
<Table.Resources>
<!-- Style for header/footer rows. -->
<Style x:Key="headerFooterRowStyle" TargetType="{x:Type TableRowGroup}">
<Setter Property="FontWeight" Value="DemiBold"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Background" Value="LightGray"/>
</Style>
<!-- Style for data rows. -->
<Style x:Key="dataRowStyle" TargetType="{x:Type TableRowGroup}">
<Setter Property="FontSize" Value="12"/>
<Setter Property="FontStyle" Value="Italic"/>
</Style>
</Table.Resources>
<Table.Columns>
<TableColumn/> <TableColumn/> <TableColumn/> <TableColumn/>
</Table.Columns>
<!-- This TableRowGroup hosts a header row for the table. -->
<TableRowGroup Style="{StaticResource headerFooterRowStyle}">
<TableRow>
<TableCell/>
<TableCell><Paragraph>Gizmos</Paragraph></TableCell>
<TableCell><Paragraph>Thingamajigs</Paragraph></TableCell>
<TableCell><Paragraph>Doohickies</Paragraph></TableCell>

MCT: Luis Dueas

Pag 205 de 473

Manual de Windows Presentation Foundation


</TableRow>
</TableRowGroup>
<!-- This TableRowGroup hosts the main data rows for the table. -->
<TableRowGroup Style="{StaticResource dataRowStyle}">
<TableRow>
<TableCell><Paragraph Foreground="Blue">Blue</Paragraph></TableCell>
<TableCell><Paragraph>1</Paragraph></TableCell>
<TableCell><Paragraph>2</Paragraph></TableCell>
<TableCell><Paragraph>3</Paragraph> </TableCell>
</TableRow>
<TableRow>
<TableCell><Paragraph Foreground="Red">Red</Paragraph></TableCell>
<TableCell><Paragraph>1</Paragraph></TableCell>
<TableCell><Paragraph>2</Paragraph></TableCell>
<TableCell><Paragraph>3</Paragraph></TableCell>
</TableRow>
<TableRow>
<TableCell><Paragraph Foreground="Green">Green</Paragraph></TableCell>
<TableCell><Paragraph>1</Paragraph></TableCell>
<TableCell><Paragraph>2</Paragraph></TableCell>
<TableCell><Paragraph>3</Paragraph></TableCell>
</TableRow>
</TableRowGroup>
<!-- This TableRowGroup hosts a footer row for the table. -->
<TableRowGroup Style="{StaticResource headerFooterRowStyle}">
<TableRow>
<TableCell><Paragraph>Totals</Paragraph></TableCell>
<TableCell><Paragraph>3</Paragraph></TableCell>
<TableCell><Paragraph>6</Paragraph></TableCell>
<TableCell>
<Table></Table>
</TableCell>
</TableRow>
</TableRowGroup>
</Table>
En la ilustracin siguiente se muestra cmo se representa este ejemplo.

Prioridad de representacin del fondo


Los elementos de tabla se representan en el orden siguiente (orden z ascendente). Este orden no se puede
modificar. Por ejemplo, no existe una propiedad de orden z para estos elementos que se pueda utilizar para
invalidar el orden establecido.
1.

Table

2.

TableColumn

3.

TableRowGroup

4.

TableRow

5.

TableCell

Estudie el ejemplo siguiente, en el que se definen los colores de fondo para cada uno de estos elementos
dentro de una tabla.
<Table Background="Yellow">
<Table.Columns>
<TableColumn/>
<TableColumn Background="LightGreen"/>
<TableColumn/>
</Table.Columns>
<TableRowGroup>
<TableRow>
<TableCell/><TableCell/><TableCell/>
</TableRow>
</TableRowGroup>
<TableRowGroup Background="Tan">
<TableRow>
<TableCell/><TableCell/><TableCell/>
</TableRow>
<TableRow Background="LightBlue">
<TableCell/><TableCell Background="Purple"/><TableCell/>
</TableRow>
<TableRow>
<TableCell/><TableCell/><TableCell/>
</TableRow>
</TableRowGroup>
<TableRowGroup>

MCT: Luis Dueas

Pag 206 de 473

Manual de Windows Presentation Foundation


<TableRow>
<TableCell/><TableCell/><TableCell/>
</TableRow>
</TableRowGroup>
</Table>
En la ilustracin siguiente se muestra cmo se representa este ejemplo (mostrando nicamente los colores de
fondo).

Abarcar filas o columnas


Las celdas de una tabla se pueden configurar para abarcar varias filas o columnas utilizando los atributos
RowSpan o ColumnSpan, respectivamente.
Estudie el ejemplo siguiente, en el que una celda abarca tres columnas.
<Table>
<Table.Columns>
<TableColumn/>
<TableColumn/>
<TableColumn/>
</Table.Columns>
<TableRowGroup>
<TableRow>
<TableCell ColumnSpan="3" Background="Cyan">
<Paragraph>This cell spans all three columns.</Paragraph>
</TableCell>
</TableRow>
<TableRow>
<TableCell Background="LightGray"><Paragraph>Cell 1</Paragraph></TableCell>
<TableCell Background="LightGray"><Paragraph>Cell 2</Paragraph></TableCell>
<TableCell Background="LightGray"><Paragraph>Cell 3</Paragraph></TableCell>
</TableRow>
</TableRowGroup>
</Table>
En la ilustracin siguiente se muestra cmo se representa este ejemplo.

Generar una tabla mediante cdigo


En los ejemplos siguientes se muestra cmo crear una Table y rellenarla con contenido mediante programacin.
El contenido de la tabla se distribuye en cinco filas (representadas por objetos TableRow contenidos en un
objeto RowGroups) y seis columnas (representadas por objetos TableColumn). Las filas se utilizan para
distintos fines de presentacin, e incluyen una fila de ttulo utilizada para dar ttulo a toda la tabla, una fila de
encabezado para describir las columnas de datos de la tabla, y una fila de pie de pgina con informacin de
resumen. Observe que los conceptos de filas de "ttulo", "encabezado" y "pie de pgina" no son inherentes a la
tabla; se trata simplemente de filas con caractersticas diferentes. Las celdas de la tabla incluyen el contenido
real, que puede estar compuesto de texto, imgenes o casi cualquier otro elemento de la interfaz de usuario
(UI).
En primer lugar, se crea un FlowDocument para hospedar Table. Tambin se crea una nueva Table y se agrega
al contenido de FlowDocument.
' Create the parent FlowDocument...
flowDoc = New FlowDocument()
' Create the Table...
table1 = New Table()
' ...and add it to the FlowDocument Blocks collection.
flowDoc.Blocks.Add(table1)
' Set some global formatting properties for the table.
table1.CellSpacing = 10
table1.Background = Brushes.White
A continuacin, se crean seis objetos TableColumn y se agregan a la coleccin Columns de la tabla, tras
aplicarles formato.
Nota:

MCT: Luis Dueas

Pag 207 de 473

Manual de Windows Presentation Foundation

Observe que la coleccin Columns de la tabla utiliza la indizacin estndar basada en cero.
' Create 6 columns and add them to the table's Columns collection.
Dim numberOfColumns = 6
Dim x
For x = 0 To numberOfColumns
table1.Columns.Add(new TableColumn())
' Set alternating background colors for the middle colums.
If x Mod 2 = 0 Then
table1.Columns(x).Background = Brushes.Beige
Else
table1.Columns(x).Background = Brushes.LightSteelBlue
End If
Next x
Luego, se crea una fila de ttulo y se agrega a la tabla tras aplicarle formato. La fila de ttulo contiene una sola
celda que abarca las seis columnas de la tabla.
' Create and add an empty TableRowGroup to hold the table's Rows.
table1.RowGroups.Add(new TableRowGroup())
' Add the first (title) row.
table1.RowGroups(0).Rows.Add(new TableRow())
' Alias the current working row for easy reference.
Dim currentRow As New TableRow()
currentRow = table1.RowGroups(0).Rows(0)
' Global formatting for the title row.
currentRow.Background = Brushes.Silver
currentRow.FontSize = 40
currentRow.FontWeight = System.Windows.FontWeights.Bold
' Add the header row with content,
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("2004 Sales Project"))))
' and set the row to span all 6 columns.
currentRow.Cells(0).ColumnSpan = 6
Despus, se crea una fila de encabezado y se agrega a la tabla, y se crean y rellenan con contenido las celdas
de la fila de encabezado.
' Add the second (header) row.
table1.RowGroups(0).Rows.Add(new TableRow())
currentRow = table1.RowGroups(0).Rows(1)
' Global formatting for the header row.
currentRow.FontSize = 18
currentRow.FontWeight = FontWeights.Bold
' Add cells with content to the second row.
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new

Run("Product"))))
Run("Quarter 1"))))
Run("Quarter 2"))))
Run("Quarter 3"))))
Run("Quarter 4"))))
Run("TOTAL"))))

En este momento, se crea una fila de datos y se agrega a la tabla, y se crean y rellenan con contenido las
celdas de esta fila. Generar esta fila es similar a generar la fila de encabezado, con un formato ligeramente
diferente.
' Add the third row.
table1.RowGroups(0).Rows.Add(new TableRow())
currentRow = table1.RowGroups(0).Rows(2)
' Global formatting for the row.
currentRow.FontSize = 12
currentRow.FontWeight = FontWeights.Normal
' Add cells with content to the third row.
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
' Bold the first cell.
currentRow.Cells(0).FontWeight = FontWeights.Bold

Run("Widgets"))))
Run("$50,000"))))
Run("$55,000"))))
Run("$60,000"))))
Run("$65,000"))))
Run("$230,000"))))

Por ltimo, se crea una fila de pie de pgina, se agreg y se le da formato. Al igual que la fila de ttulo, el pie de
pgina contiene una sola celda que abarca las seis columnas de la tabla.
table1.RowGroups(0).Rows.Add(new TableRow())
currentRow = table1.RowGroups(0).Rows(4)
' Global formatting for the footer row.
currentRow.Background = Brushes.LightGray
currentRow.FontSize = 18
currentRow.FontWeight = System.Windows.FontWeights.Normal
' Add the header row with content,
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Projected 2004 Revenue:
$810,000"))))
' and set the row to span all 6 columns.

MCT: Luis Dueas

Pag 208 de 473

Manual de Windows Presentation Foundation


currentRow.Cells(0).ColumnSpan = 6

6.4.4. Temas "Cmo..." de Elementos de Contenido Dinmico


En los temas de esta seccin se describe cmo realizar tareas comunes mediante diversos elementos de
contenido dinmico y las caractersticas relacionadas.

6.4.4.1. Cmo: Ajustar el Espaciado entre Prrafos


En este ejemplo se muestra cmo ajustar o eliminar el espaciado entre los prrafos en el contenido dinmico.
En el contenido dinmico, el espacio adicional que aparece entre los prrafos es el resultado de los mrgenes
establecidos para estos prrafos; as pues, el espaciado entre los prrafos se puede controlar ajustando los
mrgenes de esos prrafos. Para eliminar totalmente el espaciado adicional entre dos prrafos, establezca los
mrgenes de los prrafos en 0. Para conseguir un espaciado uniforme entre los prrafos en un objeto
FlowDocument completo, se utilizan estilos para establecer un valor de margen uniforme para todos los
prrafos contenidos en el objeto FlowDocument.
Es importante tener en cuenta que los mrgenes de dos prrafos adyacentes se "contraern" para respetar el
mayor de los dos mrgenes, en lugar de duplicarlo. As pues, si dos prrafos adyacentes tienen, mrgenes de
20 y 40 pxeles, respectivamente, el espacio resultante entre los prrafos ser de 40 pxeles, el mayor de los
dos valores de margen.
Ejemplo
En el ejemplo siguiente se utilizan estilos para establecer el margen para todos los elementos Paragraph de un
objeto FlowDocument en 0, lo que, en realidad, elimina el espaciado adicional entre los prrafos de
FlowDocument.
<FlowDocument>
<FlowDocument.Resources>
<!-- This style is used to set the margins for all
-->
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"/>
</Style>
</FlowDocument.Resources>
<Paragraph>
Spacing between paragraphs is caused by margins
margins
will "collapse" to the larger of the two margin
</Paragraph>
<Paragraph>
To eliminate extra spacing between two paragraphs,
</Paragraph>
</FlowDocument>

paragraphs in the FlowDocument to 0.

set on the paragraphs.

Two adjacent

widths, rather than doubling up.


just set the paragraph margins to 0.

6.4.4.2. Cmo: Generar una Tabla mediante Programacin


En los ejemplos siguientes se muestra cmo crear una Table y rellenarla con contenido mediante programacin.
El contenido de la tabla se distribuye en cinco filas (representadas por objetos TableRow contenidos en un
objeto RowGroups) y seis columnas (representadas por objetos TableColumn ). Las filas se utilizan para
distintos fines de presentacin, e incluyen una fila de ttulo utilizada para dar ttulo a toda la tabla, una fila de
encabezado para describir las columnas de datos de la tabla, y una fila de pie de pgina con informacin de
resumen. Observe que los conceptos de filas de "ttulo", "encabezado" y "pie de pgina" no son inherentes a la
tabla; se trata simplemente de filas con caractersticas diferentes. Las celdas de la tabla incluyen el contenido
real, que puede estar compuesto de texto, imgenes o casi cualquier otro elemento de la interfaz de usuario
(UI).
Ejemplo
En primer lugar, se crea un FlowDocument para hospedar Table. Tambin se crea una nueva Table y se agrega
al contenido de FlowDocument.

MCT: Luis Dueas

Pag 209 de 473

Manual de Windows Presentation Foundation


' Create the parent FlowDocument...
flowDoc = New FlowDocument()
' Create the Table...
table1 = New Table()
' ...and add it to the FlowDocument Blocks collection.
flowDoc.Blocks.Add(table1)
' Set some global formatting properties for the table.
table1.CellSpacing = 10
table1.Background = Brushes.White
A continuacin, se crean seis objetos TableColumn y se agregan a la coleccin Columns de la tabla, tras
aplicarles formato.
Nota:
Observe que la coleccin Columns de la tabla utiliza la indizacin estndar basada en cero.
' Create 6 columns and add them to the table's Columns collection.
Dim numberOfColumns = 6
Dim x
For x = 0 To numberOfColumns
table1.Columns.Add(new TableColumn())
' Set alternating background colors for the middle colums.
If x Mod 2 = 0 Then
table1.Columns(x).Background = Brushes.Beige
Else
table1.Columns(x).Background = Brushes.LightSteelBlue
End If
Next x
Luego, se crea una fila de ttulo y se agrega a la tabla tras aplicarle formato. La fila de ttulo contiene una sola
celda que abarca las seis columnas de la tabla.
' Create and add an empty TableRowGroup to hold the table's Rows.
table1.RowGroups.Add(new TableRowGroup())
' Add the first (title) row.
table1.RowGroups(0).Rows.Add(new TableRow())
' Alias the current working row for easy reference.
Dim currentRow As New TableRow()
currentRow = table1.RowGroups(0).Rows(0)
' Global formatting for the title row.
currentRow.Background = Brushes.Silver
currentRow.FontSize = 40
currentRow.FontWeight = System.Windows.FontWeights.Bold
' Add the header row with content,
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("2004 Sales Project"))))
' and set the row to span all 6 columns.
currentRow.Cells(0).ColumnSpan = 6
Despus, se crea una fila de encabezado y se agrega a la tabla, y se crean y rellenan con contenido las celdas
de la fila de encabezado.
' Add the second (header) row.
table1.RowGroups(0).Rows.Add(new TableRow())
currentRow = table1.RowGroups(0).Rows(1)
' Global formatting for the header row.
currentRow.FontSize = 18
currentRow.FontWeight = FontWeights.Bold
' Add cells with content to the second row.
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new

Run("Product"))))
Run("Quarter 1"))))
Run("Quarter 2"))))
Run("Quarter 3"))))
Run("Quarter 4"))))
Run("TOTAL"))))

En este momento, se crea una fila de datos y se agrega a la tabla, y se crean y rellenan con contenido las
celdas de esta fila. Generar esta fila es similar a generar la fila de encabezado, con un formato ligeramente
diferente.
' Add the third row.
table1.RowGroups(0).Rows.Add(new TableRow())
currentRow = table1.RowGroups(0).Rows(2)
' Global formatting for the row.
currentRow.FontSize = 12
currentRow.FontWeight = FontWeights.Normal
' Add cells with content to the third row.
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new
currentRow.Cells.Add(new TableCell(new Paragraph(new

MCT: Luis Dueas

Run("Widgets"))))
Run("$50,000"))))
Run("$55,000"))))
Run("$60,000"))))
Run("$65,000"))))
Run("$230,000"))))

Pag 210 de 473

Manual de Windows Presentation Foundation


' Bold the first cell.
currentRow.Cells(0).FontWeight = FontWeights.Bold
Por ltimo, se crea una fila de pie de pgina, se agreg y se le da formato. Al igual que la fila de ttulo, el pie de
pgina contiene una sola celda que abarca las seis columnas de la tabla.
table1.RowGroups(0).Rows.Add(new TableRow())
currentRow = table1.RowGroups(0).Rows(4)
' Global formatting for the footer row.
currentRow.Background = Brushes.LightGray
currentRow.FontSize = 18
currentRow.FontWeight = System.Windows.FontWeights.Normal
' Add the header row with content,
currentRow.Cells.Add(new TableCell(new Paragraph(new Run("Projected 2004 Revenue:
$810,000"))))
' and set the row to span all 6 columns.
currentRow.Cells(0).ColumnSpan = 6

6.4.4.3. Cmo: Cambiar la Propiedad FlowDirection de Contenido mediante


Programacin
En este ejemplo se muestra cmo cambiar mediante programacin la propiedad FlowDirection de un control
FlowDocumentReader.
Ejemplo
Se crean dos elementos Button, cada uno de los cuales representa uno de los valores posibles de FlowDirection.
Cuando se hace clic en un botn, el valor de la propiedad asociada se aplica al contenido de un control
FlowDocumentReader llamado tf1. El valor de propiedad se escribe tambin en un control TextBlock llamado
txt1.
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,10">
<Button Click="LR">LeftToRight</Button>
<Button Click="RL">RightToLeft</Button>
</StackPanel>
<TextBlock Name="txt1" DockPanel.Dock="Bottom" Margin="0,50,0,0"/>
<FlowDocumentReader>
<FlowDocument FontFamily="Arial" Name="tf1">
<Paragraph>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit,
sed diam nonummy nibh euismod tincidunt ut laoreet dolore
magna aliquam erat volutpat. Ut wisi enim ad minim veniam,
quis nostrud exerci tation ullamcorper suscipit lobortis nisl
ut aliquip ex ea commodo consequat. Duis autem vel eum iriure.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed
diam nonummy nibh euismod tincidunt ut laoreet dolore magna
aliquam erat volutpat. Ut wisi enim ad minim veniam, quis
nostrud exerci tation ullamcorper suscipit lobortis nisl ut
uliquip ex ea commodo consequat. Duis autem vel eum iriure.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed
diam nonummy nibh euismod tincidunt ut laoreet dolore magna
aliquam erat volutpat. Ut wisi enim ad minim veniam, quis
nostrud exerci tation ullamcorper suscipit lobortis nisl ut
aliquip ex ea commodo consequat. Duis autem vel eum iriure.
</Paragraph>
<Paragraph>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed
diam nonummy nibh euismod tincidunt ut laoreet dolore magna
aliquam erat volutpat. Ut wisi enim ad minim veniam, quis
nostrud exerci tation ullamcorper suscipit lobortis nisl ut
aliquip ex ea commodo consequat. Duis autem vel eum iriure.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed
diam nonummy nibh euismod tincidunt ut laoreet dolore magna
aliquam erat volutpat. Ut wisi enim ad minim veniam, quis
nostrud exerci tation ullamcorper suscipit lobortis nisl ut
aliquip ex ea commodo consequat. Duis autem vel eum iriure.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed
diam nonummy nibh euismod tincidunt ut laoreet dolore magna
aliquam erat volutpat. Ut wisi enim ad minim veniam, quis
nostrud exerci tation ullamcorper suscipit lobortis nisl ut
aliquip ex ea commodo consequat. Duis autem vel eum iriure.
</Paragraph>
</FlowDocument>
</FlowDocumentReader>
Los eventos asociados a los clics del botn definidos ms arriba se administran en un archivo de cdigo
subyacente C#.
private void LR(object sender, RoutedEventArgs e)
{

MCT: Luis Dueas

Pag 211 de 473

Manual de Windows Presentation Foundation


tf1.FlowDirection = FlowDirection.LeftToRight;
txt1.Text = "FlowDirection is now " + tf1.FlowDirection;
}
private void RL(object sender, RoutedEventArgs e)
{
tf1.FlowDirection = FlowDirection.RightToLeft;
txt1.Text = "FlowDirection is now " + tf1.FlowDirection;
}

6.4.4.4. Cmo: Cambiar la Propiedad TextWrapping mediante Programacin


Ejemplo
En el ejemplo de cdigo siguiente se muestra cmo cambiar el valor de la propiedad TextWrapping mediante
programacin utilizando Microsoft Visual Basic .NET.
Se colocan cuatro botones dentro de un elemento StackPanel en Lenguaje de marcado de aplicaciones
extensible (XAML). El evento Click de cada botn se corresponde con un procedimiento Sub definido en cdigo
de Microsoft Visual Basic .NET, a continuacin. Despus de invocar el valor TextWrapping asociado a cada
evento, el objeto TextBlock identificado por la propiedad Name txt2 se actualiza para reflejar el cambio en la
propiedad.
<StackPanel Orientation="Horizontal" Margin="0,0,0,20">
<Button Name="btn1" Background="Silver" Width="100" Click="Wrap">Wrap</Button>
<Button Name="btn2" Background="Silver" Width="100" Click="NoWrap">NoWrap</Button>
<Button Name="btn4" Background="Silver" Width="100"
Click="WrapWithOverflow">WrapWithOverflow</Button>
</StackPanel>
<TextBlock Name="txt2" TextWrapping="Wrap" Margin="0,0,0,20" Foreground="Black">
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit amet,
consectetuer adipiscing elit.Lorem ipsum dolor sit aet, consectetuer adipiscing elit.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
</TextBlock>
Los eventos asociados a los clics efectuados en Button definidos anteriormente se controlan en procedimientos
Sub de Microsoft Visual Basic .NET. Cada evento Click cambia el valor de la propiedad TextWrapping al valor
especificado en el procedimiento y actualiza el elemento txt1 para reflejar el cambio en la propiedad.
Private Sub Wrap(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
txt2.TextWrapping = System.Windows.TextWrapping.Wrap
txt1.Text = "The TextWrap property is currently set to Wrap."
End Sub
Private Sub NoWrap(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
txt2.TextWrapping = System.Windows.TextWrapping.NoWrap
txt1.Text = "The TextWrap property is currently set to NoWrap."
End Sub
Private Sub WrapWithOverflow(ByVal sender As Object, ByVal e As
System.Windows.RoutedEventArgs)
txt2.TextWrapping = System.Windows.TextWrapping.WrapWithOverflow
txt1.Text = "The TextWrap property is currently set to WrapWithOverflow."
End Sub

6.4.4.5. Cmo: Definir una Tabla con XAML


En el ejemplo siguiente se muestra cmo definir un objeto Table mediante Lenguaje de marcado de
aplicaciones extensible (XAML). La tabla del ejemplo tiene cuatro columnas (representas por elementos
TableColumn) y varias filas (representadas por elementos TableRow) que contienen datos y, adems, la
informacin de ttulo, encabezado y pie de pgina. Las filas deben estar contenidas en un elemento
TableRowGroup. Cada fila de la tabla consta de una o ms celdas (representadas por elementos TableCell). El
contenido de la celda de una tabla debe estar incluido en un elemento Block; en este caso se utilizan elementos
Paragraph. La tabla tambin hospeda un hipervnculo (representado por el elemento Hyperlink) en la fila del pie
de pgina.
Ejemplo
<FlowDocumentReader>
<FlowDocument>
<Table CellSpacing="5">
<Table.Columns>
<TableColumn/>
<TableColumn/>
<TableColumn/>
<TableColumn/>
</Table.Columns>

MCT: Luis Dueas

Pag 212 de 473

Manual de Windows Presentation Foundation


<TableRowGroup>
<!-- Title row for the table. -->
<TableRow Background="SkyBlue">
<TableCell ColumnSpan="4" TextAlignment="Center">
<Paragraph FontSize="24pt" FontWeight="Bold">Planetary Information
</Paragraph>
</TableCell>
</TableRow>
<!-- Header row for the table. -->
<TableRow Background="LightGoldenrodYellow">
<TableCell><Paragraph FontSize="14pt" FontWeight="Bold">Planet</Paragraph>
</TableCell>
<TableCell><Paragraph FontSize="14pt" FontWeight="Bold">Mean Distance from
Sun</Paragraph></TableCell>
<TableCell><Paragraph FontSize="14pt" FontWeight="Bold">Mean Diameter
</Paragraph></TableCell>
<TableCell><Paragraph FontSize="14pt" FontWeight="Bold">Approximate Mass
</Paragraph></TableCell>
</TableRow>
<!-- Sub-title row for the inner planets. -->
<TableRow>
<TableCell ColumnSpan="4"><Paragraph FontSize="14pt" FontWeight="Bold">The
Inner Planets</Paragraph></TableCell>
</TableRow>
<!-- Four data rows for the inner planets. -->
<TableRow>
<TableCell><Paragraph>Mercury</Paragraph></TableCell>
<TableCell><Paragraph>57,910,000 km</Paragraph></TableCell>
<TableCell><Paragraph>4,880 km</Paragraph></TableCell>
<TableCell><Paragraph>3.30e23 kg</Paragraph></TableCell>
</TableRow>
<TableRow Background="lightgray">
<TableCell><Paragraph>Venus</Paragraph></TableCell>
<TableCell><Paragraph>108,200,000 km</Paragraph></TableCell>
<TableCell><Paragraph>12,103.6 km</Paragraph></TableCell>
<TableCell><Paragraph>4.869e24 kg</Paragraph></TableCell>
</TableRow>
<TableRow>
<TableCell><Paragraph>Earth</Paragraph></TableCell>
<TableCell><Paragraph>149,600,000 km</Paragraph></TableCell>
<TableCell><Paragraph>12,756.3 km</Paragraph></TableCell>
<TableCell><Paragraph>5.972e24 kg</Paragraph></TableCell>
</TableRow>
<TableRow Background="lightgray">
<TableCell><Paragraph>Mars</Paragraph></TableCell>
<TableCell><Paragraph>227,940,000 km</Paragraph></TableCell>
<TableCell><Paragraph>6,794 km</Paragraph></TableCell>
<TableCell><Paragraph>6.4219e23 kg</Paragraph></TableCell>
</TableRow>
<!-- Sub-title row for the outter planets. -->
<TableRow>
<TableCell ColumnSpan="4"><Paragraph FontSize="14pt" FontWeight="Bold">The
Major Outer Planets</Paragraph></TableCell>
</TableRow>
<!-- Four data rows for the major outter planets. -->
<TableRow>
<TableCell><Paragraph>Jupiter</Paragraph></TableCell>
<TableCell><Paragraph>778,330,000 km</Paragraph></TableCell>
<TableCell><Paragraph>142,984 km</Paragraph></TableCell>
<TableCell><Paragraph>1.900e27 kg</Paragraph></TableCell>
</TableRow>
<TableRow Background="lightgray">
<TableCell><Paragraph>Saturn</Paragraph></TableCell>
<TableCell><Paragraph>1,429,400,000 km</Paragraph></TableCell>
<TableCell><Paragraph>120,536 km</Paragraph></TableCell>
<TableCell><Paragraph>5.68e26 kg</Paragraph></TableCell>
</TableRow>
<TableRow>
<TableCell><Paragraph>Uranus</Paragraph></TableCell>
<TableCell><Paragraph>2,870,990,000 km</Paragraph></TableCell>
<TableCell><Paragraph>51,118 km</Paragraph></TableCell>
<TableCell><Paragraph>8.683e25 kg</Paragraph></TableCell>
</TableRow>
<TableRow Background="lightgray">
<TableCell><Paragraph>Neptune</Paragraph></TableCell>
<TableCell><Paragraph>4,504,000,000 km</Paragraph></TableCell>
<TableCell><Paragraph>49,532 km</Paragraph></TableCell>
<TableCell><Paragraph>1.0247e26 kg</Paragraph></TableCell>
</TableRow>
<!-- Footer row for the table. -->
<TableRow>
<TableCell ColumnSpan="4"><Paragraph FontSize="10pt" FontStyle="Italic">
Information from the
<Hyperlink NavigateUri="http://encarta.msn.com/encnet/refpages/artcenter.aspx">
Encarta</Hyperlink>web site.
</Paragraph></TableCell>

MCT: Luis Dueas

Pag 213 de 473

Manual de Windows Presentation Foundation


</TableRow>
</TableRowGroup>
</Table>
</FlowDocument>
</FlowDocumentReader>

6.4.4.6. Cmo: Modificar la Tipografa de Texto


En el ejemplo siguiente se muestra cmo establecer el atributo Typography usando Paragraph como elemento
de ejemplo.
Ejemplo
<Paragraph
TextAlignment="Left"
FontSize="18"
FontFamily="Palatino Linotype"
Typography.NumeralStyle="OldStyle"
Typography.Fraction="Stacked"
Typography.Variants="Inferior">
<Run>
This text has some altered typography characteristics. Note
that use of an open type font is necessary for most typographic
properties to be effective.
</Run>
<LineBreak/><LineBreak/>
<Run>
0123456789 10 11 12 13
</Run>
<LineBreak/><LineBreak/>
<Run>
1/2 2/3 3/4
</Run>
</Paragraph>
En la ilustracin siguiente se muestra cmo se representa este ejemplo.

En contraste, en la ilustracin siguiente se muestra cmo se representa un ejemplo similar con propiedades
tipogrficas predeterminadas.

En el ejemplo siguiente se muestra cmo establecer la propiedad Typography mediante programacin.


Paragraph par = new Paragraph();
Run runText = new Run(
"This text has some altered typography characteristics. Note" +
"that use of an open type font is necessary for most typographic" +
"properties to be effective.");
Run runNumerals = new Run("0123456789 10 11 12 13");
Run runFractions = new Run("1/2 2/3 3/4");
par.Inlines.Add(runText);
par.Inlines.Add(new LineBreak());
par.Inlines.Add(new LineBreak());
par.Inlines.Add(runNumerals);
par.Inlines.Add(new LineBreak());
par.Inlines.Add(new LineBreak());
par.Inlines.Add(runFractions);
par.TextAlignment = TextAlignment.Left;
par.FontSize = 18;
par.FontFamily = new FontFamily("Palatino Linotype");
par.Typography.NumeralStyle = FontNumeralStyle.OldStyle;
par.Typography.Fraction = FontFraction.Stacked;
par.Typography.Variants = FontVariants.Inferior;

MCT: Luis Dueas

Pag 214 de 473

Manual de Windows Presentation Foundation

6.4.4.7. Cmo: Habilitar el Recorte de Texto


En este ejemplo se muestran el uso y los efectos de los valores disponibles en la enumeracin TextTrimming.
Ejemplo
En el ejemplo siguiente se define un elemento TextBlock con el atributo TextTrimming establecido.
<TextBlock
Name="myTextBlock"
Margin="20" Background="LightGoldenrodYellow"
TextTrimming="WordEllipsis" TextWrapping="NoWrap"
FontSize="14">
One<LineBreak/>
two two<LineBreak/>
Three Three Three<LineBreak/>
four four four four<LineBreak/>
Five Five Five Five Five<LineBreak/>
six six six six six six<LineBreak/>
Seven Seven Seven Seven Seven Seven Seven
</TextBlock>
A continuacin se muestra cmo establecer la propiedad TextTrimming correspondiente mediante cdigo.
myTextBlock.TextTrimming = TextTrimming.CharacterEllipsis;
En la actualidad existen tres opciones para recortar texto: CharacterEllipsis, WordEllipsis y None.
Cuando TextTrimming se establece en CharacterEllipsis, el texto se recorta y se contina con puntos
suspensivos en el carcter situado ms prximo al borde de recorte. Este valor suele recortar el texto de modo
que se ajuste ms al lmite de recorte, pero puede dar lugar al recorte parcial de palabras. En la ilustracin
siguiente se muestra el efecto de este valor en un objeto TextBlock similar al definido anteriormente.

Cuando TextTrimming se establece en WordEllipsis, el texto se recorta y se contina con puntos suspensivos
al final de la primera palabra completa ms prxima al borde de recorte. Este valor no mostrar palabras
parcialmente recortadas, pero no suele recortar el texto tan cerca del borde de recorte como el valor
CharacterEllipsis. En la ilustracin siguiente se muestra el efecto de este valor en el objeto TextBlock definido
anteriormente.

Cuando TextTrimming se establece en None, no se recorta el texto. En este caso, el texto se corta
simplemente en el lmite del contenedor de texto primario. En la ilustracin siguiente se muestra el efecto de
este valor en un objeto TextBlock similar al definido anteriormente.

6.4.4.8. Cmo: Insertar un Elemento en Texto mediante Programacin


En el ejemplo siguiente se muestra cmo utilizar dos objetos TextPointer para especificar un intervalo dentro
del texto al que se va a aplicar un elemento Span.
Ejemplo
using System;
using System.Windows;

MCT: Luis Dueas

Pag 215 de 473

Manual de Windows Presentation Foundation


using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Documents;
namespace SDKSample
{
public partial class InsertInlineIntoTextExample : Page
{
public InsertInlineIntoTextExample()
{
// Create a paragraph with a short sentence
Paragraph myParagraph = new Paragraph(new Run("Neptune has 72 times Earth's
volume..."));
// Create two TextPointers that will specify the text range the Span will cover
TextPointer myTextPointer1 = myParagraph.ContentStart.GetPositionAtOffset(10);
TextPointer myTextPointer2 = myParagraph.ContentEnd.GetPositionAtOffset(-5);
// Create a Span that covers the range between the two TextPointers.
Span mySpan = new Span(myTextPointer1, myTextPointer2);
mySpan.Background = Brushes.Red;
// Create a FlowDocument with the paragraph as its initial content.
FlowDocument myFlowDocument = new FlowDocument(myParagraph);
this.Content = myFlowDocument;
}
}
}
En la ilustracin siguiente se muestra cmo queda este ejemplo.

6.4.4.9. Cmo: Manipular Elementos de Contenido Dinmico mediante la


Propiedad Blocks
En estos ejemplos se muestran algunas de las operaciones ms comunes que se pueden realizar en elementos
de contenido dinmico mediante la propiedad Blocks. Esta propiedad se utiliza para agregar y quitar elementos
de BlockCollection. Los elementos de contenido dinmico que presentan la propiedad Blocks son:

Figure
Floater
ListItem
Section
TableCell

En estos ejemplos se utiliza Section como elemento de contenido dinmico, pero estas tcnicas son aplicables a
todos los elementos que hospedan una coleccin de elementos de contenido dinmico.
Ejemplo
En el ejemplo siguiente se crea un nuevo objeto Section y, a continuacin, se utiliza el mtodo Add para
agregar un nuevo prrafo al contenido de Section.
Section secx = new Section();
secx.Blocks.Add(new Paragraph(new Run("A bit of text content...")));
En el ejemplo siguiente se crea un nuevo elemento Paragraph y se inserta al principio de Section.
Paragraph parx = new Paragraph(new Run("Text to insert..."));
secx.Blocks.InsertBefore(secx.Blocks.FirstBlock, parx);
En el ejemplo siguiente se obtiene el nmero de elementos Block de nivel superior contenidos en Section.
int countTopLevelBlocks = secx.Blocks.Count;
En el siguiente ejemplo se elimina el ltimo elemento Block de Section.
secx.Blocks.Remove(secx.Blocks.LastBlock);
En el siguiente ejemplo se borra todo el contenido (los elementos Block) de Section.
secx.Blocks.Clear();

6.4.4.10. Cmo: Manipular Elementos de Contenido Dinmico mediante la


Propiedad Inlines
En estos ejemplos se muestran algunas de las operaciones ms comunes que se pueden realizar en elementos
de contenido dinmico insertados (y en los contenedores de tales elementos, como TextBlock) mediante la

MCT: Luis Dueas

Pag 216 de 473

Manual de Windows Presentation Foundation


propiedad Inlines. Esta propiedad se utiliza para agregar y quitar elementos de InlineCollection. Los elementos
de contenido dinmico que presentan la propiedad Inlines son:

Bold
Hyperlink
Italic
Paragraph
Span
Underline

En estos ejemplos se utiliza Span como elemento de contenido dinmico, pero estas tcnicas son aplicables a
todos los elementos o controles que hospedan una coleccin InlineCollection.
Ejemplo
En el ejemplo siguiente se crea un nuevo objeto Span y, a continuacin, utiliza el mtodo Add para agregar dos
series de texto como elementos de contenido secundarios de Span.
Span spanx = new Span();
spanx.Inlines.Add(new Run("A bit of text content..."));
spanx.Inlines.Add(new Run("A bit more text content..."));
En el ejemplo siguiente se crea un nuevo elemento Run y se inserta al principio de Span.
Run runx = new Run("Text to insert...");
spanx.Inlines.InsertBefore(spanx.Inlines.FirstInline, runx);
En el ejemplo siguiente se obtiene el nmero de elementos Inline de nivel superior contenidos en Span.
int countTopLevelInlines = spanx.Inlines.Count;
En el siguiente ejemplo se elimina el ltimo elemento Inline de Span.
spanx.Inlines.Remove(spanx.Inlines.LastInline);
En el siguiente ejemplo se borra todo el contenido (los elementos Inline) de Span.
spanx.Inlines.Clear();

6.4.4.11. Cmo: Manipular un Objeto FlowDocument mediante la Propiedad


Blocks
En estos ejemplos se muestran algunas de las operaciones ms comunes que se pueden realizar en un objeto
FlowDocument mediante la propiedad Blocks.
Ejemplo
En el ejemplo siguiente se crea un nuevo objeto FlowDocument y se anexa un nuevo elemento Paragraph a
FlowDocument.
FlowDocument flowDoc = new FlowDocument(new Paragraph(new Run("A bit of text
content...")));
flowDoc.Blocks.Add(new Paragraph(new Run("Text to append...")));
En el ejemplo siguiente se crea un nuevo elemento Paragraph y se inserta al principio de FlowDocument.
Paragraph p = new Paragraph(new Run("Text to insert..."));
flowDoc.Blocks.InsertBefore(flowDoc.Blocks.FirstBlock, p);
En el ejemplo siguiente se obtiene el nmero de elementos Block de nivel superior contenidos en FlowDocumen
int countTopLevelBlocks = flowDoc.Blocks.Count;
En el siguiente ejemplo se elimina el ltimo elemento Block de FlowDocument.
flowDoc.Blocks.Remove(flowDoc.Blocks.LastBlock);
En el siguiente ejemplo se borra todo el contenido (los elementos Block) de FlowDocument.
flowDoc.Blocks.Clear();

6.4.4.12. Cmo: Manipular las Columnas de una Tabla mediante la Propiedad


Columns
En este ejemplo se muestran algunas de las operaciones ms comunes que se pueden realizar en las columnas
de una tabla mediante la propiedad Columns.

MCT: Luis Dueas

Pag 217 de 473

Manual de Windows Presentation Foundation


Ejemplo
En el ejemplo siguiente se crea una nueva tabla y, a continuacin, se utiliza el mtodo Add para agregar
columnas a la coleccin Columns de la tabla.
Table tbl = new Table();
int columnsToAdd = 4;
for (int x = 0; x < columnsToAdd; x++) tbl.Columns.Add(new TableColumn());
En el ejemplo siguiente se inserta un nuevo objeto TableColumn. La nueva columna se inserta en la posicin de
ndice 0, lo que la convierte en la nueva primera columna de la tabla.
Nota:
La coleccin TableColumnCollection utiliza la indizacin estndar basada en cero.
tbl.Columns.Insert(0, new TableColumn());
En el ejemplo siguiente se tiene acceso a algunas propiedades arbitrarias de las columnas de la coleccin
TableColumnCollection, y se hace referencia a las columnas concretas por su ndice.
tbl.Columns[0].Width = new GridLength(20);
tbl.Columns[1].Background = Brushes.AliceBlue;
tbl.Columns[2].Width = new GridLength(20);
tbl.Columns[3].Background = Brushes.AliceBlue;
En el ejemplo siguiente se obtiene el nmero de columnas hospedadas por la tabla en este momento.
int columns = tbl.Columns.Count;
En el ejemplo siguiente se quita una columna en particular por su referencia.
tbl.Columns.Remove(tbl.Columns[3]);
En el ejemplo siguiente se quita una columna en particular por su ndice.
tbl.Columns.RemoveAt(2);
En el ejemplo siguiente se quitan todas las columnas de la coleccin de columnas de la tabla.
tbl.Columns.Clear();

6.4.4.13. Cmo: Manipular Grupos de Filas de una Tabla mediante la Propiedad


RowGroups
En este ejemplo se muestran algunas de las operaciones ms comunes que se pueden realizar en los grupos de
filas de una tabla mediante la propiedad RowGroups.
Ejemplo
En el ejemplo siguiente se crea una nueva tabla y, a continuacin, se utiliza el mtodo Add para agregar
columnas a la coleccin RowGroups de la tabla.
Table tbl = new Table();
int rowGroupsToAdd = 4;
for (int x = 0; x < rowGroupsToAdd; x++) tbl.RowGroups.Add(new TableRowGroup());
En el ejemplo siguiente se inserta un nuevo objeto TableRowGroup. La nueva columna se inserta en la posicin
de ndice 0, lo que la convierte en el primer grupo de filas de la tabla.
Nota:
La coleccin TableRowGroupCollection utiliza la indizacin estndar basada en cero.
tbl.RowGroups.Insert(0, new TableRowGroup());
En el ejemplo siguiente se agregan varias filas a un objeto TableRowGroup determinado (especificado por su
ndice) de la tabla.
int rowsToAdd = 10;
for (int x = 0; x < rowsToAdd; x++) tbl.RowGroups[0].Rows.Add(new TableRow());
En el ejemplo siguiente se tiene acceso a algunas propiedades arbitrarias de las filas del primer grupo de filas
de la tabla.
// Alias the working TableRowGroup for ease in referencing.
TableRowGroup trg = tbl.RowGroups[0];
trg.Rows[0].Background = Brushes.CornflowerBlue;
trg.Rows[1].FontSize = 24;

MCT: Luis Dueas

Pag 218 de 473

Manual de Windows Presentation Foundation


trg.Rows[2].ToolTip = "This row's tooltip";
En el ejemplo siguiente se agregan varias celdas a un objeto TableRow determinado (especificado por su ndice)
de la tabla.
int cellsToAdd = 10;
for (int x = 0; x < cellsToAdd; x++)
tbl.RowGroups[0].Rows[0].Cells.Add(new TableCell(new Paragraph(new Run("Cell"+(x+1)))));
En el ejemplo siguiente se tiene acceso a algunos mtodos y propiedades arbitrarios de las celdas de la primera
fila del primer grupo de filas.
// Alias the working for for ease in referencing.
TableRow row = tbl.RowGroups[0].Rows[0];
row.Cells[0].Background = Brushes.PapayaWhip;
row.Cells[1].FontStyle = FontStyles.Italic;
// This call clears all of the content from this cell.
row.Cells[2].Blocks.Clear();
En el ejemplo siguiente se devuelve el nmero de elementos TableRowGroup hospedados por la tabla.
int rowGroups = tbl.RowGroups.Count;
En el ejemplo siguiente se quita un grupo de filas en particular por su referencia.
tbl.RowGroups.Remove(tbl.RowGroups[0]);
En el ejemplo siguiente se quita un grupo de filas en particular por su ndice.
tbl.RowGroups.RemoveAt(0);
En el ejemplo siguiente se quitan todos los grupos de filas de la coleccin de grupos de filas de la tabla.
tbl.RowGroups.Clear();

6.4.4.14. Cmo: Usar Elementos de Contenido Dinmico


En el ejemplo siguiente se muestra el uso declarativo de varios elementos de contenido dinmico y los atributos
asociados. Los elementos y atributos que se muestran son, entre otros:

Elemento Bold
Atributo BreakPageBefore
Atributo FontSize
Elemento Italic
Elemento LineBreak
Elemento List
Elemento ListItem
Elemento Paragraph
Elemento Run
Elemento Section
Elemento Span
Atributo Variants (superndice y subndice)
Elemento Underline

Ejemplo
<FlowDocument
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Paragraph FontSize="18">Flow Format Example</Paragraph>
<Paragraph>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy
nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi
enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis
nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure.
</Paragraph>
<Paragraph>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh
euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim
ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl
ut aliquip ex ea commodo consequat. Duis autem vel eum iriure.
</Paragraph>
<Paragraph FontSize="18">More flow elements</Paragraph>
<Paragraph FontSize="15">Inline, font type and weight, and a List</Paragraph>
<List>
<ListItem><Paragraph>ListItem 1</Paragraph></ListItem>
<ListItem><Paragraph>ListItem 2</Paragraph></ListItem>
<ListItem><Paragraph>ListItem 3</Paragraph></ListItem>
<ListItem><Paragraph>ListItem 4</Paragraph></ListItem>

MCT: Luis Dueas

Pag 219 de 473

Manual de Windows Presentation Foundation


<ListItem><Paragraph>ListItem 5</Paragraph></ListItem>
</List>
<Paragraph><Bold>Bolded</Bold></Paragraph>
<Paragraph><Underline>Underlined</Underline></Paragraph>
<Paragraph><Bold><Underline>Bolded and Underlined</Underline></Bold></Paragraph>
<Paragraph><Italic>Italic</Italic></Paragraph>
<Paragraph><Span>The Span element, no inherent rendering</Span></Paragraph>
<Paragraph><Run>The Run element, no inherent rendering</Run></Paragraph>
<Paragraph FontSize="15">Subscript, Superscript</Paragraph>
<Paragraph>
<Run Typography.Variants="Superscript">This text is Superscripted.</Run>
This text isn't.
</Paragraph>
<Paragraph>
<Run Typography.Variants="Subscript">This text is Subscripted.</Run>
This text isn't.
</Paragraph>
<Paragraph>
If a font does not support a particular form (such as Superscript)
a default font form will be displayed.
</Paragraph>
<Paragraph FontSize="15">Blocks, breaks, paragraph</Paragraph>
<Section><Paragraph>A block section of text</Paragraph></Section>
<Section><Paragraph>Another block section of text</Paragraph></Section>
<Paragraph><LineBreak/></Paragraph>
<Section><Paragraph>... and another section, preceded by a
LineBreak</Paragraph></Section>
<Section BreakPageBefore="True"/>
<Section><Paragraph>... and another section, preceded by a
PageBreak</Paragraph></Section>
<Paragraph>Finally, a paragraph. Note the break between this paragraph ...
</Paragraph>
<Paragraph TextIndent="25">... and this paragraph, and also the left
indention.</Paragraph>
<Paragraph><LineBreak/></Paragraph>
</FlowDocument>

6.4.4.15. Cmo: Usar Atributos de Separacin de Columnas de FlowDocument


En este ejemplo se muestra cmo utilizar las caractersticas de separacin de columnas de FlowDocument.
Ejemplo
En el ejemplo siguiente se define un objeto FlowDocument y se establecen los atributos ColumnGap,
ColumnRuleBrush y ColumnRuleWidth. FlowDocument contiene un prrafo nico de contenido de ejemplo.
<FlowDocumentReader>
<FlowDocument
ColumnGap="20.0"
ColumnRuleBrush="DodgerBlue"
ColumnRuleWidth="5.0"
ColumnWidth="140.0">
<Paragraph Background="AntiqueWhite" TextAlignment="Left">
This paragraph has the background set to antique white to make its
boundaries obvious.
The column gap is the space between columns; this FlowDocument will
have a column gap of 20 device-independend pixels. The column rule
is a vertical line drawn in the column gap, and is used to visually
separate columns; this FlowDocument a Dodger-blue column rule that
is 5 pixels wide.
The column rule and column gap both take space between columns. In
this case, a column gap width of 20 plus a column rule of width of 5
results in the space between columns being 25 pixels wide, 5 pixels
for the column rule, and 10 pixels of column gap on either side of the column rule.
</Paragraph>
</FlowDocument>
</FlowDocumentReader>
En la ilustracin siguiente se muestran los efectos de los atributos ColumnGap, ColumnRuleBrush y
ColumnRuleWidth en un FlowDocument representado.

MCT: Luis Dueas

Pag 220 de 473

Manual de Windows Presentation Foundation

6.5. Tipografa
Windows Presentation Foundation (WPF) incluye compatibilidad con la presentacin enriquecida de contenido de
texto. En WPF se representa el texto utilizando Microsoft ClearType, que mejora su claridad y legibilidad. WPF
tambin admite las fuentes OpenType, que ofrecen funciones adicionales a las definidas por el formato
TrueType.

6.5.1. Tipografa en Windows Presentation Foundation


En este tema se introducen las caractersticas tipogrficas principales de WPF. Estas caractersticas incluyen
mejoras en la calidad y el rendimiento de la representacin de texto, compatibilidad con la tipografa de
OpenType, texto internacional mejorado, compatibilidad de fuentes mejorada y nuevas interfaces de
programacin de aplicaciones (API) de texto.
Mejora de la calidad y el rendimiento del texto
En WPF, el texto se representa mediante Microsoft ClearType, que mejora su claridad y legibilidad. ClearType es
una tecnologa de software desarrollada por Microsoft que mejora la legibilidad del texto en las pantallas de
cristal lquido (LCD) existentes, tales como las de los equipos porttiles, los Pocket PC o los monitores de
pantalla plana. ClearType utiliza una representacin con unidades menores que los pxeles, que permite
mostrar la forma real del texto con mayor fidelidad y alinear los caracteres con respecto a una parte
fraccionaria de un pxel. Esta resolucin adicional aumenta la nitidez de los detalles diminutos en la
presentacin del texto, lo que facilita mucho su lectura durante espacios de tiempo prolongados. Otra mejora
de ClearType en WPF es el suavizado (anti-aliasing) en la direccin del eje y, que suaviza las partes superior e
inferior de las curvas superficiales de los caracteres de texto.
Texto con suavizado (anti-aliasing) de ClearType en la direccin del eje y

La canalizacin de representacin de texto completa puede acelerarse mediante hardware en el WPF, siempre
que el equipo cumpla los requisitos mnimos de hardware necesarios. La representacin que no se puede llevar
a cabo mediante hardware se realiza mediante software. La aceleracin de hardware afecta a todas las fases de
la canalizacin de representacin de texto, desde el almacenamiento de glifos individuales hasta la aplicacin
del algoritmo de mezcla de ClearType a la salida final mostrada, pasando por la composicin de glifos en
ejecuciones de glifos y la aplicacin de efectos.
Diagrama de la canalizacin de representacin de texto

MCT: Luis Dueas

Pag 221 de 473

Manual de Windows Presentation Foundation


Adems, el texto animado, ya sea mediante caracteres o glifos, aprovecha al mximo la capacidad de hardware
de grficos del WPF. De este modo se suaviza la animacin de texto.
Tipografa enriquecida
El formato de fuente OpenType es una extensin del formato de fuente TrueType. Microsoft y Adobe
desarrollaron conjuntamente el formato de fuente OpenType, que proporciona una completa seleccin de
caractersticas tipogrficas avanzadas. El objeto Typography expone muchas de las caractersticas avanzadas
de las fuentes OpenType, tales como alternativas estilsticas y letras floreadas. Windows SDK proporciona un
conjunto de fuentes OpenType de ejemplo diseadas con caractersticas enriquecidas, tales como las fuentes
Pericles y Pescadero.
La fuente OpenType Pericles contiene glifos adicionales que proporcionan alternativas estilsticas al conjunto
estndar de glifos. En el texto siguiente se muestran glifos de alternativas estilsticas.
Texto que utiliza alternativas estilsticas de glifos OpenType

Las letras floreadas son glifos decorativos en los que se utilizan adornos detallados que suelen asociarse a la
caligrafa. El texto siguiente muestra glifos normales y floreados de la fuente Pescadero.
Texto que utiliza glifos OpenType normales y floreados

Compatibilidad mejorada con el texto internacional


WPF proporciona compatibilidad mejorada con el texto internacional mediante las caractersticas siguientes:

Interlineado automtico en todos los sistemas de escritura, mediante la medicin adaptable.


Compatibilidad general con el texto internacional.
Saltos de lnea, guiones y justificacin basados en el idioma.

Compatibilidad mejorada de fuentes


WPF proporciona compatibilidad mejorada con las fuentes mediante las caractersticas siguientes:

Unicode para todo el texto. Ya no se requiere un juego de caracteres ni una pgina de cdigos para
regir el comportamiento y la seleccin de fuentes.

El comportamiento de las fuentes es independiente de la configuracin global, como la configuracin


regional del sistema.

Tipos FontWeight, FontStretch, y FontStyle independientes para definir una clase FontFamily. De este
modo se proporciona mayor flexibilidad que en la programacin de Win32, donde se utilizan
combinaciones booleanas de cursiva y negrita para definir una familia de fuentes.

El control de la direccin de escritura (horizontal o vertical) es independiente del nombre de la fuente.

Los vnculos y el retroceso de fuente se incluyen en un archivo XML porttil, mediante la tecnologa de
fuentes compuestas. Las fuentes compuestas permiten la construccin de fuentes multilinges de
intervalo completo. Las fuentes compuestas tambin proporcionan un mecanismo que evita la
presentacin de glifos ausentes. Para obtener ms informacin, vea los comentarios de la clase
FontFamily.

Las fuentes internacionales se generan a partir de fuentes compuestas, mediante un grupo de fuentes
de un solo idioma. De este modo se ahorran costos de recursos al desarrollar fuentes para varios
idiomas.

Las fuentes compuestas se incrustan en el documento para proporcionar portabilidad de los


documentos. Para obtener ms informacin, vea los comentarios de la clase FontFamily.

MCT: Luis Dueas

Pag 222 de 473

Manual de Windows Presentation Foundation


Nuevas interfaces de programacin de aplicaciones (API) de texto
El WPF proporciona varias API de texto que los programadores pueden usar al incluir texto en sus aplicaciones.
Estas API se agrupan en tres categoras:

Diseo e interfaz de usuario. Controles de texto comunes de la interfaz grfica de usuario (GUI).
Dibujo de texto ligero. Permite dibujar texto directamente en los objetos.
Formato de texto avanzado. Permite implementar un motor de texto personalizado.

Diseo e interfaz de usuario


En el nivel superior de funcionalidad, las API de texto proporcionan controles comunes de la interfaz de usuario
(UI), tales como Label, TextBlock y TextBox. Estos controles proporcionan los elementos bsicos de la interfaz
de usuario en una aplicacin, adems de constituir una manera fcil de presentar el texto e interactuar con l.
Los controles como RichTextBox y PasswordBox permiten un control ms avanzado o especializado del texto.
Por su parte, las clases como TextRange, TextSelection y TextPointer resultan de gran utilidad para manipular
el texto. Estos controles de la interfaz de usuario proporcionan propiedades como FontFamily, FontSize y
FontStyle, que permiten controlar la fuente que se utiliza para representar el texto.
Utilizar efectos de mapa de bits, transformaciones y efectos de texto
El WPF permite crear usos visualmente interesantes de texto mediante caractersticas tales como efectos de
mapa de bits, transformaciones y efectos de texto. En el ejemplo siguiente se muestra un tipo tpico de efecto
de sombra paralela aplicado al texto.
Texto con sombra paralela

En el ejemplo siguiente se muestra un efecto de sombra paralela y ruido aplicado al texto.


Texto con sombra paralela y ruido

En el ejemplo siguiente se muestra un efecto de resplandor exterior aplicado al texto.


Texto con efecto de resplandor exterior

En el ejemplo siguiente se muestra un efecto de desenfoque aplicado al texto.


Texto con efecto de desenfoque

En el ejemplo siguiente se muestra la segunda lnea de texto ajustada a una escala del 150% a lo largo del eje
X y la tercera lnea de texto ajustada a una escala del 150% a lo largo del eje Y.
Texto al que se ha aplicado ScaleTransform

En el ejemplo siguiente se muestra el texto sesgado a lo largo del eje X.


Texto al que se ha aplicado SkewTransform

TextEffect es un objeto auxiliar que permite tratar el texto como uno o ms grupos de caracteres en una
cadena de texto. En el ejemplo siguiente, tomado de Ejemplo TextEffect, se muestra un carcter individual al
que se aplica una rotacin. Cada carcter gira de manera independiente a intervalos de 1 segundo.

MCT: Luis Dueas

Pag 223 de 473

Manual de Windows Presentation Foundation


Ejemplo de animacin de efecto de texto giratorio

Utilizar documentos dinmicos


Adems de los controles comunes de la interfaz de usuario, el WPF proporciona un control de diseo para la
presentacin de texto, el elemento FlowDocument. El elemento FlowDocument, junto con el elemento
DocumentViewer, proporciona un control para grandes cantidades de texto con requisitos de diseo variables.
Los controles de diseo proporcionan acceso a la tipografa avanzada por medio del objeto Typography y de las
propiedades relacionadas con fuentes de otros controles de la interfaz de usuario.
En el ejemplo siguiente se muestra el contenido de texto hospedado en FlowDocumentReader, que admite
bsquedas, navegacin, paginacin y ajuste de contenidos.
Texto hospedado en un objeto FlowDocumentReader

Dibujo de texto ligero


Puede dibujar texto directamente en objetos de WPF mediante el mtodo DrawText del objeto DrawingContext.
Para utilizar este mtodo, cree un objeto FormattedText. Este objeto permite dibujar texto de varias lneas, en
el que se puede dar formato a cada carcter individualmente. La funcionalidad del objeto FormattedText
contiene gran parte de la funcionalidad de los marcadores de DrawText de la API de Win32. Adems, el objeto
FormattedText contiene funcionalidades tales como la compatibilidad con los puntos suspensivos, que permite
mostrar puntos suspensivos cuando el texto supera los lmites establecidos. En el ejemplo siguiente se muestra
texto al que se han aplicado varios formatos, incluido un degradado lineal en las palabras segunda y tercera.
Texto mostrado mediante el objeto FormattedText

Puede convertir el texto con formato en objetos Geometry, lo que permite crear otros tipos de texto
visualmente interesante. Por ejemplo, podra crear un objeto Geometry basado en el contorno de una cadena
de texto.
Aplicacin de un contorno al texto mediante un pincel de degradado lineal

En los ejemplos siguientes se muestran varias maneras de crear efectos visuales interesantes modificando el
trazo, el relleno y el resaltado del texto convertido.
Ejemplo de cmo establecer el trazo y el relleno en diferentes colores

MCT: Luis Dueas

Pag 224 de 473

Manual de Windows Presentation Foundation


Ejemplo de un pincel de imagen aplicado al trazo

Ejemplo de un pincel de imagen aplicado al trazo y al resaltado

Formato de texto avanzado


En el nivel ms avanzado de las API de texto, el WPF ofrece la capacidad de crear un diseo de texto
personalizado

mediante

el

objeto

TextFormatter

otros

tipos

en

el

espacio

de

nombres

System.Windows.Media.TextFormatting. El objeto TextFormatter y las clases asociadas permiten implementar


un diseo de texto personalizado que admite su propia definicin de formatos de caracteres, estilos de prrafo,
reglas de salto de lnea y otras caractersticas de diseo para el texto internacional. Hay muy pocos casos en
los que es conveniente invalidar la implementacin predeterminada de la compatibilidad de diseo de texto del
WPF. Sin embargo, si va a crear un control o aplicacin de edicin de texto, quizs necesite una
implementacin diferente que la predeterminada del WPF.
A diferencia de una API de texto tradicional, TextFormatter interacta con un cliente de diseo de texto a travs
de un conjunto de mtodos de devolucin de llamada. Necesita que el cliente proporcione estos mtodos en
una implementacin de la clase TextSource. En el diagrama siguiente se muestra la interaccin de diseo de
texto entre la aplicacin cliente y TextFormatter.
Interaccin entre la aplicacin y TextFormatter

Recursos
Una tcnica til para obtener informacin sobre las caractersticas de OpenType es utilizar XamlPad para crear
un marcado que experimente con el uso de diversas propiedades tipogrficas.
Probar fuentes OpenType con XamlPad

6.5.2. Informacin General sobre ClearType


En este tema se proporciona informacin general sobre la tecnologa Microsoft ClearType de Windows
Presentation Foundation (WPF).

MCT: Luis Dueas

Pag 225 de 473

Manual de Windows Presentation Foundation


Informacin general sobre la tecnologa
ClearType es una tecnologa de software desarrollada por Microsoft que mejora la legibilidad del texto en las
pantallas LCD existentes, como las de los equipos porttiles, las de los Pocket PC o los monitores de pantalla
plana. ClearType funciona teniendo acceso a los elementos de las bandas de color verticales individuales de
cada pxel de una pantalla LCD. Antes de ClearType, el menor nivel de detalle que un equipo poda mostrar era
un solo pxel; sin embargo, gracias a la ejecucin de ClearType en un monitor LCD, ahora se puede mostrar
caractersticas del texto con el ancho de una fraccin de pxel. Esta resolucin adicional aumenta la nitidez de
los detalles diminutos en la presentacin del texto, lo que facilita mucho su lectura durante espacios de tiempo
prolongados.
La tecnologa ClearType disponible en Windows Presentation Foundation (WPF) es la ltima generacin de
ClearType, y presenta varias mejoras sobre la versin de Interfaz de dispositivo grfico de Microsoft Windows
(GDI).
Posicin de subpxel
Una mejora significativa con respecto a la versin anterior de ClearType es el uso de la posicin de subpxel. Al
contrario que la implementacin de ClearType de GDI, la tecnologa ClearType de Windows Presentation
Foundation (WPF) permite que un glifos se inicie dentro de un pxel, no slo en el lmite inicial del pxel. Gracias
a esta resolucin adicional al colocar los glifos, el espaciado y las proporciones de los glifos son ms precisos y
coherentes.
En los dos ejemplos siguientes se muestra cmo pueden comenzar los glifos en el lmite de cualquier subpxel
cuando se utiliza la posicin de subpxel. El ejemplo de la izquierda se ha representado utilizando la versin
anterior del representador ClearType, que no empleaba la posicin de subpxel. El ejemplo de la derecha se ha
representado mediante la nueva versin del representador ClearType, que utiliza la posicin de subpxel.
Observe que cada letra e y l de la imagen de la derecha se representa de un modo ligeramente distinto, porque
cada una se inicia en un subpxel diferente. Al observar el texto con su tamao normal en la pantalla, esta
diferencia no es apreciable debido al alto contraste de la imagen de glifo. Esto slo es posible gracias al filtrado
sofisticado del color incorporado en ClearType.
Texto mostrado con versiones anteriores y posteriores de ClearType

En los dos ejemplos siguientes se compara la salida del representador ClearType anterior con la nueva versin
del procesador ClearType. La posicin de subpxel, que se muestra a la derecha, mejora en gran medida el
espaciado de los tipos en la pantalla, sobre todo en los tamaos pequeos donde la diferencia entre un subpxel
y un pxel entero representa una proporcin significativa de ancho del glifo. Observe que el espaciado entre las
letras es ms uniforme en la segunda imagen. Se aumenta significativamente la ventaja acumulativa de la
posicin de subpxel para el aspecto global de una pantalla de texto, lo que representa una evolucin
significativa en la tecnologa ClearType.

MCT: Luis Dueas

Pag 226 de 473

Manual de Windows Presentation Foundation


Texto con versiones anteriores y posteriores de ClearType

Suavizado en la direccin del eje Y


Otra mejora de ClearType en Windows Presentation Foundation (WPF) es el suavizado (anti-aliasing) en la
direccin del eje Y. La tecnologa ClearType de GDI sin suavizado (anti-aliasing) en la direccin del eje Y
proporciona una mejor resolucin en el eje X, pero no en el eje Y. En las partes superior e inferior de las curvas
poco marcadas, los bordes escalonados reducen su legibilidad.
En el ejemplo siguiente se muestra el efecto de no aplicar ningn suavizado en la direccin del eje Y. En este
caso, los bordes escalonados de la parte superior e inferior de la letra se aprecian con claridad.
Texto con bordes escalonados en curvas poco marcadas

ClearType en Windows Presentation Foundation (WPF) proporciona el suavizado en el nivel de la direccin del
eje Y para suavizar los bordes escalonados. Es particularmente importante para mejorar la legibilidad en los
idiomas del este asitico, cuyos ideogramas tienen una cantidad casi igual de curvas horizontales y verticales
poco marcadas.
En el ejemplo siguiente se muestra el efecto del suavizado en la direccin del eje Y. En este caso, las partes
superior e inferior de la letra muestran una curva poco marcada.
Texto con suavizado (anti-aliasing) de ClearType en la direccin del eje y

Aceleracin de hardware
La tecnologa ClearType de Windows Presentation Foundation (WPF) puede aprovechar la aceleracin de
hardware para mejor el rendimiento y reducir la carga de la CPU, as como los requisitos de memoria del
sistema. ClearType utiliza los sombreadores de pxeles y la memoria de vdeo de la tarjeta grfica para
proporcionar una representacin ms rpida del texto, en especial cuando se utiliza la animacin.
ClearType no modifica en Windows Presentation Foundation (WPF) los valores de ClearType para todo el
sistema. Al deshabilitar ClearType en Windows, se establece el suavizado (anti-aliasing) de Windows
Presentation Foundation (WPF) en el modo de escala de grises. Adems, la tecnologa ClearType de Windows
Presentation Foundation (WPF) no modifica los valores de ClearType Tuner PowerToy.

MCT: Luis Dueas

Pag 227 de 473

Manual de Windows Presentation Foundation


Una de las decisiones de diseo de la arquitectura de Windows Presentation Foundation (WPF) es que el diseo
independiente de la resolucin ofrezca una mejor compatibilidad con los monitores de PPP de alta resolucin,
cada vez ms utilizados. Como consecuencia, Windows Presentation Foundation (WPF) no admite la
representacin de texto suavizado ni los mapas de bits de algunas de las fuentes del este asitico, porque
dependen de la resolucin.

6.5.3. Configuracin del Registro de ClearType


En este tema se proporciona informacin general sobre la configuracin del Registro de WPF para Microsoft
ClearType utilizada por las aplicaciones de WPF.
Informacin general sobre la tecnologa
Las aplicaciones de WPF que representan texto en un dispositivo de pantalla utilizan las caractersticas
ClearType para proporcionar una experiencia de lectura mejorada. ClearType es una tecnologa de software
desarrollada por Microsoft que mejora la legibilidad del texto en las pantallas LCD existentes (como las de los
equipos porttiles, los Pocket PC y los monitores de pantalla plana). ClearType funciona teniendo acceso a los
elementos de las bandas de color verticales individuales de cada pxel de una pantalla LCD.
El texto que se representa con ClearType puede presentar diferencias significativas al verlo en los distintos
dispositivos de pantalla. Por ejemplo, algunos monitores implementan los elementos de bandas de color en el
orden azul, verde y rojo, en lugar de en el orden ms comn, rojo, verde y azul (RGB).
El texto representado con ClearType tambin puede resultar muy diferente cuando lo leen personas con
diversos niveles de sensibilidad al color. Algunas personas son capaces de detectar pequeas variaciones de
color mejor que otras.
En cada uno de estos casos, es preciso modificar las caractersticas de ClearType a fin de proporcionar la mejor
experiencia de lectura a cada persona.
Valores del Registro
WPF especifica cuatro valores del Registro para controlar las caractersticas de ClearType:
Valor

Descripcin

Nivel de ClearType

Describe el nivel de claridad del color de ClearType.

Nivel gamma

Describe el nivel del componente de color de pxel para un dispositivo de


pantalla.

Estructura de pxeles

Describe la organizacin de los pxeles para un dispositivo de pantalla.

Nivel de contraste del


texto

Describe el nivel de contraste para el texto mostrado.

Cualquier utilidad de configuracin externa que pueda hacer referencia a los valores del Registro identificados
de WPF para ClearType puede tener acceso a estos valores. Estos valores se pueden crear o modificar tambin
mediante el acceso directo a ellos utilizando el Editor del Registro de Windows.
Si los valores del Registro de WPF para ClearType no se establecen (que es el estado predeterminado), la
aplicacin WPF consulta los valores de suavizado de fuentes en la informacin de parmetros de sistema de
Windows.
ClearType Level
El nivel de ClearType permite ajustar la representacin de texto basndose en la sensibilidad y percepcin del
color de una persona. Para algunas personas, la representacin de texto que utiliza el mximo nivel de
ClearType no da lugar a la mejor experiencia de lectura.

MCT: Luis Dueas

Pag 228 de 473

Manual de Windows Presentation Foundation


El nivel de ClearType es un valor entero que comprendido entre 0 y 100. El nivel predeterminado es 100, que
significa que ClearType utiliza la mxima capacidad de los elementos de bandas de color del dispositivo de
pantalla. Sin embargo, un nivel de ClearType de 0 representa el texto en formato de escala de grises.
Estableciendo el nivel de ClearType en un valor comprendido entre 0 y 100, puede crear un nivel intermedio
adecuado para la sensibilidad al color de la persona.
Valor del Registro
La ubicacin de valor del Registro correspondiente al nivel de ClearType es un valor de usuario individual que
corresponde al nombre de un dispositivo de pantalla concreto:
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Avalon.Graphics\<displayName>
Para cada nombre de dispositivo de pantalla de un usuario, se define un valor DWORD de ClearTypeLevel. En la
captura de pantalla siguiente se muestra el valor en el Editor del Registro para el nivel de ClearType.

Nota:
Las aplicaciones de WPF representan el texto en uno de estos dos modos: con y sin ClearType. Cuando el
texto se representa sin ClearType, se denomina representacin en escala de grises.
Nivel gamma
El nivel gamma hace referencia a la relacin no lineal entre el valor y la luminancia de un pxel. El valor gamma
debe corresponder a las caractersticas fsicas del dispositivo de pantalla; de lo contrario, la representacin
puede aparecer distorsionada. Por ejemplo, el texto puede aparecer demasiado ancho o estrecho, o puede
suceder que aparezcan franjas de color en los bordes de las astas de los glifos.
El nivel gamma es un valor entero comprendido entre 1000 y 2200. El nivel predeterminado es 1900.
Valor del Registro
La ubicacin de valor del Registro correspondiente al nivel gamma es un valor del equipo local que corresponde
a un nombre de dispositivo de pantalla concreto:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Avalon.Graphics\<displayName>
Para cada nombre de dispositivo de pantalla de un usuario, se define un valor DWORD de GammaLevel. En la
captura de pantalla siguiente se muestra el valor en el Editor del Registro para el nivel gamma.

Estructura de pxeles
La estructura de pxeles describe el tipo de pxeles que constituyen un dispositivo de pantalla. La estructura de
pxeles se define como uno de estos tres tipos:
Tipo

Valor

Descripcin

Plana

El dispositivo de pantalla no tiene ninguna estructura de pxeles. Esto significa que las
fuentes de iluminacin para cada color se expanden por igual sobre el rea de pxeles, lo

MCT: Luis Dueas

Pag 229 de 473

Manual de Windows Presentation Foundation

que se denomina representacin en escala de grises. As es como funciona un dispositivo


de pantalla estndar. ClearType nunca se aplica al texto representado.
RGB

El dispositivo de pantalla tiene pxeles compuestos de tres bandas en el orden siguiente:


rojo, verde y azul. ClearType se aplica al texto representado.

BGR

El dispositivo de pantalla tiene pxeles compuestos de tres bandas en el orden siguiente:


azul, verde y rojo. ClearType se aplica al texto representado. Observe que el orden es el
contrario que en el tipo RGB.

La estructura de pxeles corresponde a un valor entero comprendido entre 0 y 2. El nivel predeterminado es 0,


que representan una estructura de pxeles plana.
Valor del Registro
La ubicacin del valor del Registro correspondiente a la estructura de pxeles es un valor del equipo local que
corresponde a un nombre de dispositivo de pantalla concreto:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Avalon.Graphics\<displayName>
Para cada nombre de dispositivo de pantalla de un usuario, se define un valor DWORD de PixelStructure. En la
captura de pantalla siguiente se muestra el valor en el Editor del Registro para la estructura de pxeles.

Nivel de contraste del texto


El nivel de contraste del texto permite ajustar la representacin del texto basndose en el ancho de las astas de
los glifos. El nivel de contraste del texto es un valor entero comprendido entre 0 y 6. Cuanto mayor es el valor
entero, ms ancho es el asta. El nivel predeterminado es 1.
Valor del Registro
La ubicacin de valor del Registro correspondiente al nivel de contraste del texto es un valor de usuario
individual que corresponde al nombre de un dispositivo de pantalla concreto:
HKEY_CURRENT_USER\Software\Microsoft\Avalon.Graphics\<displayName>
Para cada nombre de dispositivo de pantalla de un usuario, se define un valor DWORD de TextContrastLevel. En
la captura de pantalla siguiente se muestra el valor en el Editor del Registro para el nivel de contraste del texto.

6.5.4. Dibujar Texto con Formato


En este tema se proporciona informacin general sobre las caractersticas del objeto FormattedText. Este objeto
proporciona control de bajo nivel para dibujar texto en aplicaciones de Windows Presentation Foundation
(WPF).
Informacin general sobre la tecnologa
El objeto FormattedText permite dibujar texto con varias lneas, en el que se puede dar formato a cada carcter
individualmente. En el ejemplo siguiente se muestra texto con varios formatos.

MCT: Luis Dueas

Pag 230 de 473

Manual de Windows Presentation Foundation


Texto mostrado mediante el mtodo FormattedText

Nota:
Para los programadores que migran desde API de Win32, la tabla de la seccin Migracin de Win32
enumera los marcadores DrawText de Win32 y el equivalente aproximado en Windows Presentation
Foundation (WPF).
Razones para utilizar el texto con formato
WPF incluye varios controles para dibujar texto en la pantalla. Cada control se destina a un escenario diferente
y tiene su propia lista de caractersticas y limitaciones. En general, el elemento TextBlock se debe usar cuando
se necesita una compatibilidad de texto limitada, como una frase breve en una interfaz de usuario (UI). Label
se puede usar cuando se necesita una compatibilidad de texto mnima.
El objeto FormattedText proporciona ms caractersticas de formato de texto que los controles de texto de
Windows Presentation Foundation (WPF) y puede ser til en casos donde se desea utilizar el texto como
elemento decorativo.
Adems, el objeto FormattedText es til para crear objetos orientados a texto derivados de DrawingVisual.
DrawingVisual es una clase de dibujo ligera que se utiliza para representar formas, imgenes o texto.
Utilizar el objeto FormattedText
Para crear texto con formato, llame al constructor FormattedText para crear un objeto FormattedText. Una vez
creada la cadena de texto con formato inicial, puede aplicarle varios estilos de formato.
Utilice la propiedad MaxTextWidth para restringir el texto a un ancho especfico. El texto se ajustar
automticamente para evitar superar el ancho especificado. Utilice la propiedad MaxTextHeight para restringir
el texto a un alto especfico. El texto mostrar puntos suspensivos, "". para el texto que supera el alto
especificado.
Texto mostrado al que se ha aplicado el ajuste de lnea y los puntos suspensivos

Puede aplicar varios estilos de formato a uno o ms caracteres. Por ejemplo, podra llamar a los mtodos
SetFontSize y SetForegroundBrush para cambiar el formato de los cinco primeros caracteres del texto.
En el ejemplo de cdigo siguiente se crea un objeto FormattedText y, a continuacin, se aplican varios estilos
de formato al texto.
protected override void OnRender(DrawingContext drawingContext)
{
string testString = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed
do eiusmod tempor";
// Create the initial formatted text string.
FormattedText formattedText = new FormattedText(testString,
CultureInfo.GetCultureInfo("en-us"),FlowDirection.LeftToRight,
new Typeface("Verdana"),32,Brushes.Black);
// Set a maximum width and height. If the text overflows these values, an ellipsis
"..." appears.
formattedText.MaxTextWidth = 300; formattedText.MaxTextHeight = 240;
// Use a larger font size beginning at the first (zero-based) character and
continuing for 5 characters.
// The font size is calculated in terms of points -- not as device-independent
pixels.
formattedText.SetFontSize(36 * (96.0 / 72.0), 0, 5);

MCT: Luis Dueas

Pag 231 de 473

Manual de Windows Presentation Foundation


// Use a Bold font weight beginning at the 6th character and continuing for 11
characters.
formattedText.SetFontWeight(FontWeights.Bold, 6, 11);
// Use a linear gradient brush beginning at the 6th character and continuing for 11
characters.
formattedText.SetForegroundBrush(new LinearGradientBrush(Colors.Orange,Colors.Teal,
90.0), 6, 11);
// Use an Italic font style beginning at the 28th character and continuing for 28
characters.
formattedText.SetFontStyle(FontStyles.Italic, 28, 28);
// Draw the formatted text string to the DrawingContext of the control.
drawingContext.DrawText(formattedText, new Point(10, 0));
}
Unidad de medida del tamao de fuente
Como sucede con otros objetos de texto en las aplicaciones de Windows Presentation Foundation (WPF), el
objeto FormattedText utiliza los pxeles independientes del dispositivo como unidad de medida. Sin embargo, la
mayora de las aplicaciones de Win32 utilizan los puntos como unidad de medida. Si desea utilizar el texto
visualizado en unidades de puntos en las aplicaciones de Windows Presentation Foundation (WPF), es preciso
convertir los unidades independientes de dispositivo (1/96 de pulgada por unidad) en puntos. En el ejemplo de
cdigo siguiente se muestra cmo realizar esta conversin.
// The font size is calculated in terms of points -- not as device-independent pixels.
formattedText.SetFontSize(36 * (96.0 / 72.0), 0, 5);
Convertir el texto con formato en una geometra
Puede convertir el texto con formato en objetos Geometry, lo que permite crear otros tipos de texto
visualmente interesante. Por ejemplo, podra crear un objeto Geometry basado en el contorno de una cadena
de texto.
Aplicacin de un contorno al texto mediante un pincel de degradado lineal

En los ejemplos siguientes se muestran varias maneras de crear efectos visuales interesantes modificando el
trazo, el relleno y el resaltado del texto convertido.
Ejemplo de cmo establecer el trazo y el relleno en diferentes colores

Ejemplo de un pincel de imagen aplicado al trazo

Ejemplo de un pincel de imagen aplicado al trazo y al resaltado

Cuando se convierte el texto en un objeto Geometry, deja de ser una coleccin de caracteres; no se pueden
modificar los caracteres de la cadena de texto. Sin embargo, se puede cambiar la apariencia del texto
convertido modificando sus propiedades de trazo y relleno. El trazo se refiere al contorno del texto convertido;
el relleno se refiere al rea situada dentro del contorno del texto convertido.
Tambin puede convertir el texto con formato en un objeto PathGeometry y utiliza el objeto para resaltar el
texto. Por ejemplo, puede aplicar una animacin al objeto PathGeometry para que la animacin siga el contorno
del texto con formato.
En el ejemplo siguiente se muestra texto con formato convertido en un objeto PathGeometry. Una elipse
animada sigue el trazado de los trazos del texto representado.
Esfera que sigue la geometra de trazado del texto

Puede crear otros usos interesantes para el texto con formato una vez convertido en un objeto PathGeometry.
Por ejemplo, puede mostrar en l un clip de vdeo.

MCT: Luis Dueas

Pag 232 de 473

Manual de Windows Presentation Foundation


Vdeo mostrado en la geometra de trazado del texto

Migracin de Win32
Las caractersticas de FormattedText para dibujar texto son similares a las de la funcin DrawText de Win32.
Para los programadores que migran desde la API de Win32, se enumeran en la tabla siguiente los marcadores
de DrawText de Win32 y su equivalente aproximado en Windows Presentation Foundation (WPF).

Marcador de DrawText

Equivalente
de WPF

DT_BOTTOM

Height

Utilice la propiedad Height para calcular la posicin


de 'y' correcta de DrawText de Win32.

DT_CALCRECT

Height, Width

Utilice las propiedades Height y Width para calcular


el rectngulo de salida.

DT_CENTER

TextAlignment

Utilice la propiedad TextAlignment con el valor


establecido en Center.

DT_EDITCONTROL

Ninguno

No se necesita. El ancho del espacio y la


representacin de la ltima lnea son iguales que en
el control de edicin del marco de trabajo.

DT_END_ELLIPSIS

Trimming

Utilice la propiedad Trimming con el valor


CharacterEllipsis.
Utilice WordEllipsis para obtener DT_END_ELLIPSIS
de Win32 con los puntos suspensivos finales de
DT_WORD_ELIPSIS; en este caso, los puntos
suspensivos slo se muestran en las palabras que no
caben en una sola lnea.

DT_EXPAND_TABS

Ninguno

No se necesita. Las tabulaciones se expanden


automticamente
a
cada
4
ems,
que
es
aproximadamente el ancho de 8 caracteres
independientes del lenguaje.

DT_EXTERNALLEADING

Ninguno

No se necesita. El espacio externo siempre se incluye


en el interlineado. Utilice la propiedad LineHeight
para crear el interlineado definido por el usuario.

DT_HIDEPREFIX

Ninguno

No se admite. Quite el signo '&' de la cadena antes


de construir el objeto FormattedText.

DT_LEFT

TextAlignment

sta es la alineacin predeterminada del texto.


Utilice la propiedad TextAlignment con el valor
establecido en Left. (Slo WPF)

DT_MODIFYSTRING

Ninguno

No se admite.

DT_NOCLIP

VisualClip

El recorte no se realiza automticamente. Si desea


recortar texto, utilice la propiedad VisualClip.

DT_NOFULLWIDTHCHARBREAK

Ninguno

No se admite.

DT_NOPREFIX

Ninguno

No se necesita. El carcter '&' en las cadenas se trata

MCT: Luis Dueas

Notas

Pag 233 de 473

Manual de Windows Presentation Foundation

siempre como un carcter normal.


DT_PATHELLIPSIS

Ninguno

Utilice la propiedad
WordEllipsis.

Trimming

DT_PREFIX

Ninguno

No se admite. Si desea utilizar el subrayado para el


texto, como en una tecla de aceleracin o vnculo,
utilice el mtodo SetTextDecorations.

DT_PREFIXONLY

Ninguno

No se admite.

DT_RIGHT

TextAlignment

Utilice la propiedad TextAlignment con el valor


establecido en Right. (Slo WPF)

DT_RTLREADING

FlowDirection

Establezca la propiedad FlowDirection en RightToLeft.

DT_SINGLELINE

Ninguno

No se requiere. Los objetos FormattedText se


comportan como un control de una sola lnea, a
menos que se establezca la propiedad MaxTextWidth
o que el texto contenga un retorno de carro o salto
de lnea.

DT_TABSTOP

Ninguno

No se admiten las posiciones de tabulacin definidas


por el usuario.

DT_TOP

Height

No se necesita. La justificacin superior es la


predeterminada. Se pueden definir otros valores de
posicin vertical utilizando la propiedad Height para
calcular la posicin apropiada de 'y' en un DrawText
de Win32.

DT_VCENTER

Height

Utilice la propiedad Height para calcular la posicin


de 'y' correcta de DrawText de Win32.

DT_WORDBREAK

Ninguno

No se necesita. La ruptura de palabras se efecta


automticamente en los objetos FormattedText. No
se puede deshabilitar.

DT_WORD_ELLIPSIS

Trimming

Utilice la propiedad
WordEllipsis.

Trimming

con

con

el

el

valor

valor

6.5.5. Formato de Texto Avanzado


Windows Presentation Foundation (WPF) proporciona un conjunto robusto de API para incluir texto en las
aplicaciones. Las API de diseo y interfaz de usuario (UI), como TextBlock, proporcionan los elementos de uso
ms comn y general para la presentacin de texto. Las API de dibujo, como GlyphRunDrawing y
FormattedText, proporcionan medios para incluir texto con formato en dibujos. En el nivel ms avanzado, WPF
proporciona un motor de formato de texto extensible para controlar todos los aspectos de la presentacin de
texto, como la administracin de almacenes de texto, de formatos de ejecucin de texto y de objetos
incrustados.
En este tema se proporciona una introduccin al formato de texto en WPF. Se centra en la implementacin del
cliente y en el uso del motor de formato de texto de WPF.
Formato de texto avanzado
El diseo de texto y los controles de interfaz de usuario de WPF proporcionan propiedades de formato que
permiten incluir con facilidad texto con formato en una aplicacin. Estos controles exponen varias propiedades
para administrar la presentacin de texto, incluidos su tipo de letra, tamao y color. En circunstancias
normales, estos controles pueden administrar la mayora de la presentacin de texto en la aplicacin. Sin

MCT: Luis Dueas

Pag 234 de 473

Manual de Windows Presentation Foundation


embargo, algunos escenarios avanzados requieren control de almacenamiento de texto y presentacin del
texto. WPF proporciona un motor de formato de texto extensible para este propsito.
Las caractersticas de formato de texto avanzado de WPF son un motor de formato de texto, un almacn de
texto, ejecuciones de texto y propiedades de formato. El motor de formato de texto, TextFormatter, crea lneas
de texto que se van a utilizar para la presentacin. Para ello, se inicia el proceso de formato de lneas y se
llama al mtodo FormatLine del formateador de texto. El formateador de texto recupera las ejecuciones de
texto del almacn de texto llamando al mtodo GetTextRun del almacn. A continuacin, el formateador de
texto convierte los objetos TextRun en objetos TextLine y se entregan a la aplicacin para inspeccin o
presentacin.
Utilizar el formateador de texto
TextFormatter es el motor de formato de texto de WPF, que proporciona servicios para dar formato a las lneas
de texto e introducir saltos de lnea. El formateador de texto puede administrar distintos formatos de caracteres
de texto y estilos de prrafo, e incluye compatibilidad con el diseo de texto internacional.
A diferencia de la API de texto tradicional, TextFormatter interacta con un cliente de diseo de texto a travs
de un conjunto de mtodos de devolucin de llamada. Necesita que el cliente proporcione estos mtodos en
una implementacin de la clase TextSource. En el diagrama siguiente se muestra la interaccin de diseo de
texto entre la aplicacin cliente y TextFormatter.
Interaccin entre la aplicacin y TextFormatter

El formateador de texto se utiliza para recuperar lneas del texto con formato del almacn de texto, que es una
implementacin de TextSource. Para ello, se crea en primer lugar una instancia del formateador de texto
utilizando el mtodo Create. Este mtodo crea una instancia del formateador de texto y establece los valores
mximos de alto y ancho de lnea. Una vez creada una instancia del formateador de texto, se inicia el proceso
de creacin de lneas llamando al mtodo FormatLine. TextFormatter vuelve a llamar al origen del texto para
recuperar los parmetros de texto y formato correspondientes a las ejecuciones de texto que componen una
lnea.
En el ejemplo siguiente se muestra el proceso de dar formato a un almacn de texto. Se utiliza el objeto
TextFormatter para recuperar lneas de texto del almacn de texto y, a continuacin, dar formato a la lnea de
texto para dibujar en DrawingContext.
Implementar el almacn de texto de cliente
Al extender el motor de formato de texto, se hace necesario implementar y administrar todos los aspectos del
almacn de texto. No es una labor trivial. El almacn de texto es responsable de realizar el seguimiento de las
propiedades de las ejecuciones de texto, las propiedades de prrafo, los objetos incrustados y otro contenido
similar. Adems, proporciona objetos TextRun individuales al formateador de texto, que este utiliza para crear
objetos TextLine.
Para administrar la virtualizacin del almacn de texto, este ltimo debe derivarse de TextSource. TextSource
define el mtodo utilizado por el formateador de texto para recuperar ejecuciones de texto del almacn de
texto. GetTextRun es el mtodo utilizado por el formateador de texto para recuperar las ejecuciones de texto

MCT: Luis Dueas

Pag 235 de 473

Manual de Windows Presentation Foundation


utilizadas para dar formato a las lneas. El formateador de texto realiza repetidamente la llamada a GetTextRun
hasta que se cumple una de las condiciones siguientes:

Se devuelve un TextEndOfLine o una subclase.

El ancho acumulado de las ejecuciones de texto supera el ancho de lnea mximo especificado en la
llamada para crear el formateador de texto o bien en la llamada al mtodo FormatLine del formateador
de texto.

Se devuelve una secuencia Unicode de nueva lnea, como "CF", "LF" o "CRLF".

Proporcionar ejecuciones de texto


El ncleo del proceso de formato de texto es la interaccin entre el formateador de texto y el almacn de texto.
La implementacin de TextSource proporciona al formateador de texto los objetos TextRun y las propiedades
con las que dar formato a las ejecuciones de texto. El mtodo GetTextRun, al que llama el formateador de
texto, administra esta interaccin.
En la tabla siguiente se muestran algunos de los objetos TextRun predefinidos.
Tipo de TextRun

Uso

TextCharacters

Ejecucin de texto especializada que se usa para devolver una representacin de


glifos de caracteres al formateador de textos.

TextEmbeddedObject

Ejecucin de texto especializada que se usa para proporcionar contenido en el que


se efectan de manera global las pruebas de posicionamiento, la medicin y el
dibujo, como un botn o una imagen dentro del texto.

TextEndOfLine

Ejecucin de texto especializada que se usa para marcar el fin de una lnea.

TextEndOfParagraph

Ejecucin de texto especializada que se usa para marcar el fin de un prrafo.

TextEndOfSegment

Ejecucin de texto especializada que se usa para marcar el fin de un segmento,


como el final del mbito afectado por la ejecucin anterior de TextModifier.

TextHidden

Ejecucin de texto especializada que se usa para marcar un intervalo de


caracteres ocultos.

TextModifier

Ejecucin de texto especializada que se usa para modificar las propiedades de las
ejecuciones de texto de su mbito. El mbito se extiende hasta la siguiente
ejecucin de texto TextEndOfSegment coincidente, o hasta el siguiente
TextEndOfParagraph.

Se pueden crear subclases de cualquiera de los objetos TextRun predefinidos. Esto permite que el origen de
texto proporcione al formateador de texto ejecuciones de texto que incluyen datos personalizados.
En el siguiente ejemplo se muestra un mtodo GetTextRun. Este almacn de texto devuelve objetos TextRun al
formateador de texto para procesarlos.
Nota:
En este ejemplo, el almacn de texto proporciona las mismas propiedades de texto a todo el texto. Los
almacenes de texto avanzados deben implementar su propia administracin de intervalos, a fin de permitir
que caracteres individuales tengan propiedades diferentes.
Especificar propiedades de formato
Se da formato a los objetos TextRun utilizando propiedades proporcionadas por el almacn del texto. Estas
propiedades se proporcionan en dos tipos, TextParagraphProperties y TextRunProperties. TextParagraph
Properties administra propiedades de inclusin de prrafo como TextAlignment y FlowDirection. TextRun
Properties son propiedades que pueden ser diferentes para cada ejecucin de texto de un prrafo, como un
pincel de primer plano, Typeface y el tamao de fuente. Para implementar los tipos de propiedades de prrafos

MCT: Luis Dueas

Pag 236 de 473

Manual de Windows Presentation Foundation


y ejecuciones de texto personalizados, la aplicacin debe crear clases que se deriven de TextParagraph
Properties y TextRunProperties, respectivamente.

6.5.6. Fuentes en WPF


Windows Presentation Foundation (WPF) ofrece compatibilidad para la presentacin de texto con fuentes
OpenType. Un paquete de ejemplo de fuentes OpenType est incluido con Windows SDK.

6.5.6.1. Caractersticas de las Fuentes OpenType


En este tema se proporciona informacin general sobre algunas de las caractersticas clave de la tecnologa de
fuentes OpenType en Windows Presentation Foundation (WPF).
Formato de fuente OpenType
El formato de fuente OpenType es una extensin del formato de fuente TrueType, agregando compatibilidad
para datos de fuentes PostScript. Microsoft y Adobe Corporation desarrollaron conjuntamente el formato de
fuente OpenType. Las fuentes OpenType y los servicios del sistema operativo compatibles con fuentes
OpenType ofrecen a los usuarios una manera simple de instalar y utilizar las fuentes, tanto si las fuentes
contienen contornos TrueType o contornos CFF (PostScript).
El formato de fuente OpenType afronta los siguientes retos para el programador:

Mayor compatibilidad multiplataforma.


Mejor compatibilidad con juegos de caracteres internacionales.
Mejor proteccin para los datos de fuente.
Tamaos de archivo menores para hacer la distribucin de la fuente ms eficaz.
Mayor compatibilidad para el control tipogrfico avanzado.

Nota:
El SDK de Windows contiene un conjunto fuentes OpenType de muestra que puede utilizar con aplicaciones
Windows Presentation Foundation (WPF). Estas fuentes ofrecen la mayora de las caractersticas ilustradas
en el resto de este tema.
Extensiones tipogrficas avanzadas
Las tablas tipogrficas avanzadas (tablas de diseo OpenType) extienden la funcionalidad de fuentes con
contornos TrueType o CFF. Las fuentes de diseo OpenType contienen informacin adicional que extiende las
funciones de las fuentes para permitir tipografa internacional de alta calidad. La mayora de las fuentes
OpenType exponen solamente un subconjunto de las caractersticas OpenType disponibles. Las fuentes
OpenType ofrecen las caractersticas siguientes.

Asignacin enriquecida entre caracteres y glifos que admiten ligaduras, formas posicionales,
alternativas y otras sustituciones de fuentes.

Compatibilidad para la ubicacin en dos dimensiones y la asociacin de glifos.

Informacin explcita de alfabeto e idioma contenida en fuente, para que las aplicaciones de
procesamiento de textos puedan ajustar su comportamiento en consecuencia.

Las tablas de diseo OpenType se describen con ms detalle en la seccin "Tablas de archivos de fuentes" de la
especificacin de OpenType.
El resto de esta informacin general presenta la amplitud y flexibilidad de algunas de las caractersticas de
OpenType de inters visual que exponen las propiedades del objeto Typography.
Variantes
Las variantes se utilizan para representar diferentes estilos tipogrficos, tales como superndices y subndices.

MCT: Luis Dueas

Pag 237 de 473

Manual de Windows Presentation Foundation


Superndices y subndices
La propiedad Variants permite establecer valores de superndice y subndice para una fuente OpenType.
El texto siguiente muestra superndices para la fuente Palatino Linotype.
Texto que utiliza superndices OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen superndices para la fuente Palatino Linotype,
mediante propiedades del objeto Typography.
<Paragraph FontFamily="Palatino Linotype">
2<Run Typography.Variants="Superscript">3</Run>
14<Run Typography.Variants="Superscript">th</Run>
</Paragraph>
En el texto siguiente se muestran subndices para la fuente Palatino Linotype.
Texto que utiliza subndices OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen subndices para la fuente Palatino Linotype,
mediante propiedades del objeto Typography.
<Paragraph FontFamily="Palatino Linotype">
H<Run Typography.Variants="Subscript">2</Run>O
Footnote<Run Typography.Variants="Subscript">4</Run>
</Paragraph>
Usos decorativos de superndices y subndices
Tambin puede utilizar superndices y subndices para crear efectos decorativos con texto en maysculas y
minsculas. El texto siguiente muestra texto en superndices y subndices para la fuente Palatino Linotype.
Observe que no las maysculas no se ven afectadas.
Texto que utiliza superndices y subndices OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen superndices y subndices para la fuente
Palatino Linotype, mediante propiedades del objeto Typography.
<Paragraph FontFamily="Palatino Linotype" Typography.Variants="Superscript">
Chapter One
</Paragraph>
<Paragraph FontFamily="Palatino Linotype" Typography.Variants="Subscript">
Chapter One
</Paragraph>
Maysculas
Las maysculas son un conjunto de formatos tipogrficos que representan el texto en glifos con estilo de
mayscula. Normalmente, cuando el texto se representa todo en maysculas, el espaciado entre las letras
puede parecer demasiado apretado, y el peso y proporcin de las letras demasiado pesado. OpenType admite
varios formatos de estilo para las maysculas, entre los que estn las versales, las maysculas pequeas, los
ttulos y el espaciado de maysculas. Estos formatos permiten controlar el aspecto de las maysculas.
En el texto siguiente se muestran las letras maysculas estndar de la fuente Pescadero, seguidas de letras con
estilo "SmallCaps" y "AllSmallCaps". En este caso, se utiliza el mismo tamao de fuente para las tres palabras.
Texto que utiliza maysculas OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen las maysculas para Ejemplo Using OpenType
Fonts, mediante las propiedades del objeto Typography. Cuando se utiliza el formato "SmallCaps", se omite
cualquier letra mayscula inicial.
<Paragraph FontFamily="Pescadero" FontSize="48">
<Run>CAPITALS</Run>

MCT: Luis Dueas

Pag 238 de 473

Manual de Windows Presentation Foundation


<Run Typography.Capitals="SmallCaps">Capitals</Run>
<Run Typography.Capitals="AllSmallCaps">Capitals</Run>
</Paragraph>
Maysculas de ttulos
Las maysculas de ttulos son ms ligeras en cuanto a peso y proporcin, y estn diseadas para proporcionar
una apariencia ms elegante que las maysculas normales. Las maysculas de ttulos se utilizan normalmente
en tamaos de fuente mayores como encabezados. El texto siguiente muestra maysculas normales y de ttulos
para la fuente Pescadero. Observe los anchos de pie ms estrechos del texto de la segunda lnea.
Texto que utiliza maysculas de ttulos OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen las maysculas de ttulos para la fuente
Pescadero, mediante las propiedades del objeto Typography.
<Paragraph FontFamily="Pescadero">
<Run Typography.Capitals="Titling">chapter one</Run>
</Paragraph>
Espaciado de maysculas
El espaciado de maysculas es una caracterstica que permite proporcionar ms espaciado cuando se utiliza
maysculas en todo el texto. Las letras maysculas estn diseadas para combinarse con letras minsculas. El
espaciado que parece correcto entre una letra mayscula y una minscula puede parecer demasiado apretado
cuando se utilizan todas las letras maysculas. El texto siguiente muestra el espaciado normal y de maysculas
para la fuente Pescadero.
Texto que utiliza espaciado de maysculas OpenType

En el ejemplo de marcado siguiente se muestra cmo se define el espaciado de maysculas para la fuente
Pescadero, mediante las propiedades del objeto Typography.
<Paragraph FontFamily="Pescadero">
<Run Typography.CapitalSpacing="True">CHAPTER ONE</Run>
</Paragraph>
Ligaduras
Las ligaduras son dos o ms glifos que forman un glifo nico para crear texto ms legible o atractivo. Las
fuentes OpenType admiten cuatro tipos de ligaduras:

Ligaduras estndar. Diseadas para mejorar la legibilidad. Las ligaduras estndar incluyen "fi", "fl" y
"ff".

Ligaduras contextuales. Diseadas para mejorar la legibilidad mediante un mejor comportamiento de


la unin entre los caracteres que constituyen la ligadura.

Ligaduras discrecionales. Diseadas para ser ornamentales, no especficamente por legibilidad.

Ligaduras histricas. Diseadas para ser histricas, no especficamente por legibilidad.

En el texto siguiente se muestran los glifos de ligadura estndar para la fuente Pericles.
Texto que utiliza ligaduras estndar OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen los glifos de ligadura estndar para la fuente
Pericles, mediante propiedades del objeto Typography.
<Paragraph FontFamily="Pericles" Typography.StandardLigatures="True">
<Run Typography.StylisticAlternates="1">FI</Run>
<Run Typography.StylisticAlternates="1">FL</Run>

MCT: Luis Dueas

Pag 239 de 473

Manual de Windows Presentation Foundation


<Run Typography.StylisticAlternates="1">TH</Run>
<Run Typography.StylisticAlternates="1">TT</Run>
<Run Typography.StylisticAlternates="1">TV</Run>
<Run Typography.StylisticAlternates="1">TW</Run>
<Run Typography.StylisticAlternates="1">TY</Run>
<Run Typography.StylisticAlternates="1">VT</Run>
<Run Typography.StylisticAlternates="1">WT</Run>
<Run Typography.StylisticAlternates="1">YT</Run>
</Paragraph>
En el texto siguiente se muestran glifos de ligadura discrecional para la fuente Pericles.
Texto que utiliza ligaduras discrecionales OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen glifos de ligadura discrecional para la fuente
Pericles, mediante propiedades del objeto Typography.
<Paragraph FontFamily="Pericles" Typography.DiscretionaryLigatures="True">
<Run Typography.StylisticAlternates="1">CO</Run>
<Run Typography.StylisticAlternates="1">LA</Run>
<Run Typography.StylisticAlternates="1">LE</Run>
<Run Typography.StylisticAlternates="1">LI</Run>
<Run Typography.StylisticAlternates="1">LL</Run>
<Run Typography.StylisticAlternates="1">LO</Run>
<Run Typography.StylisticAlternates="1">LU</Run>
</Paragraph>
De forma predeterminada, las fuentes OpenType de Windows Presentation Foundation (WPF) habilitan las
ligaduras estndar. Por ejemplo, si utiliza la fuente Palatino Linotype, las ligaduras estndar "fi", "ff" y "fl"
aparecern como un glifo de caracteres combinados. Observe que el par de caracteres de cada ligadura
estndar se toca entre s.
Texto que utiliza ligaduras estndar OpenType

No obstante, puede deshabilitar las caractersticas de ligadura estndar para que una ligadura estndar, tal
como "ff", se muestre como dos glifos independientes, en lugar de mostrarse como un glifo de caracteres
combinados.
Texto que utiliza ligaduras estndar OpenType deshabilitadas

En el ejemplo de marcado siguiente se muestra cmo deshabilitar glifos de ligadura estndar para la fuente
Palatino Linotype, mediante propiedades del objeto Typography.
<!-- Set standard ligatures to false in order to disable feature. -->
<Paragraph Typography.StandardLigatures="False" FontFamily="Palatino Linotype"
FontSize="72">
fi ff fl
</Paragraph>
Letras floreadas
Las letras floreadas son glifos decorativos en los que se utilizan adornos detallados que suelen asociarse a la
caligrafa. El texto siguiente muestra glifos normales y floreados de la fuente Pescadero.
Texto que utiliza glifos OpenType normales y floreados

Se utilizan a menudo como elementos decorativos en frases cortas, como los anuncios de eventos. En el texto
siguiente se utilizan glifos floreados para dar nfasis a las letras maysculas del nombre del evento.
Texto que utiliza letras floreadas OpenType

MCT: Luis Dueas

Pag 240 de 473

Manual de Windows Presentation Foundation

En el ejemplo de marcado siguiente se muestra cmo se definen letras floreadas para una fuente, mediante
propiedades del objeto Typography.
<Paragraph FontFamily="Pescadero" TextBlock.TextAlignment="Center">
Wishing you a<LineBreak/>
<Run Typography.StandardSwashes="1" FontSize="36">Happy New Year!</Run>
</Paragraph>
Letras floreadas contextuales
Algunas combinaciones de glifos floreados pueden producir un aspecto poco atractivo, como un solapamiento
de los trazos descendentes en letras adyacentes. El uso de floreo contextual permite utilizar un glifo floreado
sustituto que ofrezca un mejor aspecto. En el texto siguiente se muestra la misma palabra antes y despus de
aplicar un floreo contextual.
Texto que utiliza floreos contextuales OpenType

En el ejemplo de marcado siguiente se muestra cmo se define un floreo contextual para la fuente Pescadero,
mediante propiedades del objeto Typography.
<Paragraph FontFamily="Pescadero" Typography.StandardSwashes="1">
Lyon <Run Typography.ContextualSwashes="1">L</Run>yon
</Paragraph>
Alternativas
Las alternativas son glifos que pueden sustituirse por un glifo estndar. Las fuentes OpenType, tales como la
fuente Pericles que se utiliza en los ejemplos siguientes, puede contener glifos alternativos que puede usar para
crear diferentes apariencias para el texto. En el texto siguiente se muestran glifos estndar para la fuente
Pericles.
Texto que utiliza glifos estndar OpenType

La fuente Pericles de OpenType contiene glifos adicionales que proporcionan alternativas estilsticas al conjunto
estndar de glifos. En el texto siguiente se muestran glifos de alternativas estilsticas.
Texto que utiliza alternativas estilsticas de glifos OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen glifos de alternativa estilstica para la fuente
Pericles, mediante propiedades del objeto Typography.
<Paragraph FontFamily="Pericles">
<Run Typography.StylisticAlternates="1">A</Run>NCIENT
GR<Run Typography.StylisticAlternates="1">EE</Run>K
MYTH<Run Typography.StylisticAlternates="1">O</Run>LOGY
</Paragraph>
En el siguiente texto se muestran varios otros glifos de alternativas estilsticas para la fuente Pericles.
Texto que utiliza alternativas estilsticas de glifos OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen estos otros glifos de alternativas estilsticas.
<Paragraph FontFamily="Pericles">
<Run Typography.StylisticAlternates="1">A</Run>
<Run Typography.StylisticAlternates="2">A</Run>
<Run Typography.StylisticAlternates="3">A</Run>
<Run Typography.StylisticAlternates="1">C</Run>
<Run Typography.StylisticAlternates="1">E</Run>
<Run Typography.StylisticAlternates="1">G</Run>
<Run Typography.StylisticAlternates="1">O</Run>
<Run Typography.StylisticAlternates="1">Q</Run>
<Run Typography.StylisticAlternates="1">R</Run>

MCT: Luis Dueas

Pag 241 de 473

Manual de Windows Presentation Foundation


<Run Typography.StylisticAlternates="2">R</Run>
<Run Typography.StylisticAlternates="1">S</Run>
<Run Typography.StylisticAlternates="1">Y</Run>
</Paragraph>
Alternativas contextuales aleatorias
Las alternativas contextuales aleatorias proporcionan varios glifos sustitutos para un carcter individual.
Cuando se implementa con fuentes de tipo script, esta caracterstica puede simular la escritura a mano
mediante un conjunto de glifos elegidos aleatoriamente con ligeras diferencias de aspecto. En el texto siguiente
se utilizan alternativas contextuales aleatorias para la fuente Lindsey. Observe que la letra "a" vara
ligeramente de aspecto.
Texto que utiliza alternativas contextuales aleatorias de OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen alternativas contextuales aleatorias para la
fuente Lindsey, mediante propiedades del objeto Typography.
<TextBlock FontFamily="Lindsey">
<Run Typography.ContextualAlternates="True">
a banana in a cabana
</Run>
</TextBlock>
Formularios histricos
Las formas histricas son convenciones tipogrficas utilizadas comnmente en el pasado. En el texto siguiente
se muestra la frase "Boston, Massachusetts" utilizando un formato histrico de glifos para la fuente Palatino
Linotype.
Texto que utiliza formas histricas de OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen formas histricas para la fuente Palatino
Linotype, mediante propiedades del objeto Typography.
<Paragraph FontFamily="Palatino Linotype">
<Run Typography.HistoricalForms="True">Boston, Massachusetts</Run>
</Paragraph>
Estilos numricos
Las fuentes OpenType admiten un gran nmero de caractersticas que se pueden utilizar con valores numricos
en texto.
Fracciones
Las fuentes OpenType admiten estilos para fracciones, tales como los de fraccin apilada y con barra.
En el texto siguiente se muestran estilos de fraccin para la fuente Palatino Linotype.
Texto que utiliza fracciones con barra y apiladas de OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen estilos de fraccin para la fuente Palatino
Linotype, mediante propiedades del objeto Typography.
<Paragraph FontFamily="Palatino Linotype" Typography.Fraction="Slashed">
1/8 1/4 3/8 1/2 5/8 3/4 7/8
</Paragraph>
<Paragraph FontFamily="Palatino Linotype" Typography.Fraction="Stacked">
1/8 1/4 3/8 1/2 5/8 3/4 7/8
</Paragraph>
Numerales de estilo antiguo
Las fuentes OpenType admiten un formato numeral de estilo antiguo. Este formato es til para mostrar
nmeros en estilos que ya no son estndar. El texto siguiente muestra una fecha del siglo XVIII en formato
numrico estndar en formato y antiguo con la fuente Palatino Linotype.

MCT: Luis Dueas

Pag 242 de 473

Manual de Windows Presentation Foundation


Texto que utiliza numerales de estilo antiguo de OpenType

En el texto siguiente se muestran nmeros estndar para la fuente Palatino Linotype, seguidos por nmeros en
estilo antiguo.
Texto que utiliza conjuntos de nmeros de estilo antiguo de OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen nmeros de estilo antiguo para la fuente
Palatino Linotype, mediante las propiedades del objeto Typography.
<Paragraph FontFamily="Palatino Linotype">
<Run Typography.NumeralStyle="Normal">1234567890</Run>
<Run Typography.NumeralStyle="OldStyle">1234567890</Run>
</Paragraph>
Cifras proporcionales y tabulares
Las fuentes OpenType admiten una caracterstica de cifras proporcionales y tabulares para controlar la
alineacin de anchos al utilizar nmeros. Las cifras proporcionales tratan los nmeros como si tuvieran un
ancho diferente - "1" es ms estrecho que "5." Las cifras tabulares se tratan como nmeros de igual ancho para
que se alineen verticalmente, lo que aumenta la legibilidad de la informacin de tipo financiero.
En el texto siguiente se muestran dos cifras proporcionales en la primera columna con la fuente Miramonte.
Observe la diferencia de ancho entre los nmeros "5" y "1". La segunda columna muestra los mismos dos
valores numricos con los anchos ajustados utilizando la caracterstica de cifra tabular.
Texto que utiliza cifras proporcionales y tabulares de OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen las cifras proporcionales y tabulares para la
fuente Miramonte, mediante las propiedades del objeto Typography.
<TextBlock FontFamily="Miramonte">
<Run Typography.NumeralAlignment="Proportional">114,131</Run>
</TextBlock>
<TextBlock FontFamily="Miramonte">
<Run Typography.NumeralAlignment="Tabular">114,131</Run>
</TextBlock>
Cero cruzado
Las fuentes OpenType admiten un formato del numeral cero cruzado para dar nfasis a la diferencia entre la
letra "O" y el numeral "0". El numeral cero cruzado se utiliza a menudo para los identificadores en la
informacin financiera y comercial.
El texto siguiente muestra un identificador de orden con la fuente Miramonte. La primera lnea utiliza numerales
estndares. La segunda lnea utiliza numerales de cero cruzado para conseguir un mejor contraste con la letra
"O" mayscula.
Texto que utiliza nmeros con cero cruzado de OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen nmeros con cero cruzado para la fuente
Miramonte, mediante propiedades del objeto Typography.
<Paragraph FontFamily="Miramonte">
<Run>Order #0048-OTC-390</Run>
<LineBreak/>
<Run Typography.SlashedZero="True">Order #0048-OTC-390</Run>
</Paragraph>

MCT: Luis Dueas

Pag 243 de 473

Manual de Windows Presentation Foundation


Usar XamlPad para ver caractersticas de OpenType
Una tcnica til para aprender sobre las caractersticas de OpenType es usar XamlPad para crear marcado que
experimente con el uso de diferentes propiedades tipogrficas.
Probar fuentes OpenType con XamlPad

Clase Typography
El objeto Typography expone el conjunto de caractersticas compatibles con una fuente OpenType. Si establece
las propiedades de Typography en marcado, puede crear fcilmente documentos que saquen partido de las
caractersticas de OpenType.
En el texto siguiente se muestran las letras maysculas estndar de la fuente Pescadero, seguidas de letras con
estilo "SmallCaps" y "AllSmallCaps". En este caso, se utiliza el mismo tamao de fuente para las tres palabras.
Texto que utiliza maysculas OpenType

En el ejemplo de marcado siguiente se muestra cmo se definen las maysculas para la fuente Pescadero,
mediante las propiedades del objeto Typography. Cuando se utiliza el formato "SmallCaps", se omite cualquier
letra mayscula inicial.
<Paragraph FontFamily="Pescadero" FontSize="48">
<Run>CAPITALS</Run>
<Run Typography.Capitals="SmallCaps">Capitals</Run>
<Run Typography.Capitals="AllSmallCaps">Capitals</Run>
</Paragraph>
En el ejemplo de cdigo siguiente se realiza la misma tarea que en el ejemplo de marcado anterior.
MyParagraph.FontFamily = new FontFamily("Pescadero");
MyParagraph.FontSize = 48;
Run run_1 = new Run("CAPITALS ");
MyParagraph.Inlines.Add(run_1);
Run run_2 = new Run("Capitals ");
run_2.Typography.Capitals = FontCapitals.SmallCaps;
MyParagraph.Inlines.Add(run_2);
Run run_3 = new Run("Capitals");
run_3.Typography.Capitals = FontCapitals.AllSmallCaps;
MyParagraph.Inlines.Add(run_3);
MyParagraph.Inlines.Add(new LineBreak());
Propiedades de la clase Typography
En la tabla siguiente se presenta una lista de las propiedades, valores y configuraciones predeterminadas del
objeto Typography.
Property

Valores

Valor predeterminado

AnnotationAlternates

Valor numrico - byte

MCT: Luis Dueas

Pag 244 de 473

Manual de Windows Presentation Foundation

Capitals

AllPetiteCaps | AllSmallCaps | Normal |


PetiteCaps | SmallCaps | Titling | Unicase

FontCapitals.Normal

CapitalSpacing

Boolean

False

CaseSensitiveForms

Boolean

False

ContextualAlternates

Boolean

Trae

ContextualLigatures

Boolean

Trae

ContextualSwashes

Valor numrico - byte

DiscretionaryLigatures

Boolean

False

EastAsianExpertForms

Boolean

False

EastAsianLanguage

HojoKanji | Jis04 | Jis78 | Jis83 | Jis90 |


NlcKanji | Normal | Simplified | Traditional |
TraditionalNames

FontEastAsianLanguage.Normal

EastAsianWidths

Full | Half | Normal | Proportional | Quarter |


Third

FontEastAsianWidths.Normal

Fraction

Normal | Slashed | Stacked

FontFraction.Normal

HistoricalForms

Boolean

False

HistoricalLigatures

Boolean

False

Kerning

Boolean

Trae

MathematicalGreek

Boolean

False

NumeralAlignment

Normal | Proportional | Tabular

FontNumeralAlignment.Normal

NumeralStyle

Boolean

FontNumeralStyle.Normal

SlashedZero

Bolean

false

StandardLigatures

Bolean

true

StandardSwashes

valor numrico byte

StylisticAlternates

valor numrico byte

Variants

Inferior | Normal | Ordinal | Ruby | Subscript


| Superscript

FontVariants.Normal

6.5.6.2. Empaquetar Fuentes con Aplicaciones


En este tema se proporciona informacin general sobre cmo empaquetar fuentes con la aplicacin de Windows
Presentation Foundation (WPF).
Nota:
Como con la mayora de los tipos de software, los archivos de fuentes se otorgan bajo licencias, no se
venden. Las licencias que rigen el uso de las fuentes varan segn el proveedor de que se trate pero, por lo
general, la mayora de las licencias, incluidas las proporcionadas por Microsoft con aplicaciones y Windows,
no permiten incrustar las fuentes dentro de aplicaciones ni redistribuirlas de ningn otro modo. As pues,

MCT: Luis Dueas

Pag 245 de 473

Manual de Windows Presentation Foundation

como programador, es su responsabilidad asegurarse de disponer de los derechos de licencia necesarios


para cualquier fuente que incruste en una aplicacin o redistribuya de cualquier otro modo.
Introduccin al empaquetado de fuentes
Resulta fcil empaquetar fuentes como recursos dentro de las aplicaciones de WPF a fin de mostrar el texto de
la interfaz de usuario y otros tipos de contenido basado en texto. Las fuentes pueden ser independientes de los
archivos de ensamblado de la aplicacin o estar incrustadas en ellos. Tambin se puede crear una biblioteca de
fuentes slo de recursos, a la que haga referencia la aplicacin.
Las fuentes OpenType y TrueType contienen un marcador de tipo, fsType, que indica los derechos de licencia
de incrustacin de esa fuente. Sin embargo, este marcador de tipo slo se refiere a las fuentes incrustadas
almacenadas en un documento, no a las que se incrustan en una aplicacin. Puede recuperar los derechos de
incrustacin de una fuente creando un objeto GlyphTypeface y haciendo referencia a su propiedad
EmbeddingRights.
Agregar fuentes como elementos de contenido
Puede agregar fuentes a la aplicacin como elementos de contenido del proyecto que son independientes de los
archivos de ensamblado de la aplicacin. Esto significa que los elementos de contenido no se incrustan como
recursos dentro de un ensamblado. En el ejemplo de archivo de proyecto siguiente se muestra cmo definir
elementos de contenido.
<Project DefaultTargets="Build" mlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Other project build settings ... -->
<ItemGroup>
<Content Include="Peric.ttf" />
<Content Include="Pericl.ttf" />
</ItemGroup>
</Project>
Para asegurarse de que la aplicacin pueda utilizar las fuentes en tiempo de ejecucin, stas deben estar
accesibles en el directorio de implementacin de la aplicacin. El elemento <CopyToOutputDirectory> del
archivo de proyecto de la aplicacin permite copiar automticamente las fuentes en el directorio de
implementacin de la aplicacin durante el proceso de compilacin. En el ejemplo de archivo de proyecto
siguiente se muestra cmo copiar las fuentes en el directorio de implementacin.
<ItemGroup>
<Content Include="Peric.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Pericl.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
En el ejemplo de cdigo siguiente se muestra cmo hacer referencia a la fuente de una aplicacin como un
elemento de contenido; el elemento de contenido al que se hace referencia debe estar en el mismo directorio
que los archivos de ensamblado de la aplicacin.
<TextBlock FontFamily="./#Pericles Light">
Aegean Sea
</TextBlock>
Agregar fuentes como elementos de recursos
Puede agregar las fuentes a la aplicacin como elementos de recursos del proyecto incrustados dentro de los
archivos de ensamblado de la aplicacin. Utilizar un subdirectorio independiente para los recursos ayuda a
organizar los archivos de proyecto de la aplicacin. En el ejemplo del archivo de proyecto siguiente se muestra
cmo definir las fuentes como elementos de recursos en un subdirectorio independiente.
<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Other project build settings ... -->
<ItemGroup>
<Resource Include="resources\Peric.ttf" />

MCT: Luis Dueas

Pag 246 de 473

Manual de Windows Presentation Foundation


<Resource Include="resources\Pericl.ttf" />
</ItemGroup>
</Project>
Nota:
Cuando agregue fuentes como recursos a la aplicacin, asegrese de establecer el elemento <Resource>, y
no el elemento <EmbeddedResource>, en el archivo de proyecto de la aplicacin. El elemento
<EmbeddedResource> no se admite para la accin de compilacin.
En el ejemplo de marcado siguiente se muestra cmo hacer referencia a los recursos de fuentes de la
aplicacin.
<TextBlock FontFamily="./resources/#Pericles Light">
Aegean Sea
</TextBlock>
Hacer referencia a elementos de recursos de fuentes desde el cdigo
Para hacer referencia a los elementos de recursos de fuentes desde el cdigo, debe proporcionar una referencia
de recurso de fuente con dos componentes: el identificador de recursos uniforme (URI) base y la referencia de
ubicacin de fuente. Estos valores se utilizan como parmetros para el mtodo FontFamily. En el ejemplo de
cdigo siguiente se muestra cmo hacer referencia a los recursos de fuentes de la aplicacin en el subdirectorio
del proyecto denominado resources.
// The font resource reference includes the base URI reference (application directory
// level), and a relative URI reference.
myTextBlock.FontFamily = new FontFamily(new Uri("pack://application:,,,/"),
"./resources/#Pericles Light");
El identificador de recursos uniforme (URI) base puede incluir el subdirectorio de la aplicacin donde reside el
recurso de fuente. En este caso, no es necesario especificar un directorio en la referencia de ubicacin de
fuente, pero se tendra que incluir los caracteres iniciales "./" para indicar que el recurso de fuente se encuentra
en el mismo directorio especificado por el identificador de recursos uniforme (URI) base. En el ejemplo de
cdigo siguiente se muestra una manera alternativa de hacer referencia al elemento de recurso de fuente,
equivalente al ejemplo de cdigo anterior.
// The base URI reference can include an application subdirectory.
myTextBlock.FontFamily = new FontFamily(new Uri("pack://application:,,,/resources/"),
"./#Pericles Light");
Hacer referencia a fuentes desde el mismo subdirectorio de aplicacin
Puede colocar contenido de la aplicacin y archivos de recursos dentro del mismo subdirectorio definido por el
usuario del proyecto de aplicacin. En el ejemplo de archivo de proyecto siguiente se muestran una pgina de
contenido y recursos de fuente definidos en el mismo subdirectorio.
<ItemGroup>
<Page Include="pages\HomePage.xaml" />
</ItemGroup>
<ItemGroup>
<Resource Include="pages\Peric.ttf" />
<Resource Include="pages\Pericl.ttf" />
</ItemGroup>
Puesto que el contenido de la aplicacin y la fuente se encuentran en el mismo subdirectorio, la referencia a la
fuente es relativa al contenido de la aplicacin. En los ejemplos siguientes se muestra cmo hacer referencia al
recurso de fuente de la aplicacin cuando la fuente est en el mismo directorio que la aplicacin.
<TextBlock FontFamily="./#Pericles Light">Aegean Sea</TextBlock>
// The font resource reference includes the base Uri (application directory level),
// and the file resource location, which is relative to the base Uri.
myTextBlock.FontFamily = new FontFamily(new Uri("pack://application:,,,/"),
"/pages/#Pericles Light");
Enumerar fuentes en una aplicacin
Para enumerar las fuentes como elementos de recursos de su aplicacin, utilice el mtodo GetFontFamilies o
GetTypefaces. En el ejemplo siguiente se muestra cmo utilizar el mtodo GetFontFamilies para devolver la

MCT: Luis Dueas

Pag 247 de 473

Manual de Windows Presentation Foundation


coleccin de objetos FontFamily de la ubicacin de fuentes de la aplicacin. En este caso, la aplicacin contiene
un subdirectorio denominado "resources".
foreach (FontFamily fontFamily in Fonts.GetFontFamilies(new Uri("pack://application:,,,/"),
"./resources/"))
{
// Perform action.
}
En el ejemplo siguiente se muestra cmo utilizar el mtodo GetTypefaces para devolver la coleccin de objetos
Typeface de la ubicacin de fuentes de la aplicacin. En este caso, la aplicacin contiene un subdirectorio
denominado "resources".
foreach (Typeface typeface in Fonts.GetTypefaces(new Uri("pack://application:,,,/"),
"./resources/"))
{
// Perform action.
}
Crear una biblioteca de recursos de fuentes
Puede crear una biblioteca slo de recursos que nicamente contenga fuentes, un proyecto de librera de este
tipo no contiene cdigo. Crear una biblioteca slo de recursos es una tcnica comn para desacoplar los
recursos del cdigo de la aplicacin que los utiliza. Adems, permite incluir el ensamblado de biblioteca en
varios proyectos de aplicacin. En el ejemplo del archivo de proyecto siguiente se muestran los fragmentos
esenciales de un proyecto de biblioteca slo de recursos.
<PropertyGroup>
<AssemblyName>FontLibrary</AssemblyName>
<OutputType>library</OutputType>
...
</PropertyGroup>
...
<ItemGroup>
<Resource Include="Kooten.ttf" />
<Resource Include="Pesca.ttf" />
</ItemGroup
Hacer referencia a una fuente de una biblioteca de recursos
Para hacer referencia a una fuente de una biblioteca de recursos desde la aplicacin, debe agregar a la
referencia de la fuente un prefijo consistente en el nombre del ensamblado de biblioteca. En este caso, el
ensamblado de recursos de fuentes es "FontLibrary". Para separar el nombre del ensamblado de la referencia
dentro del ensamblado, se utiliza el carcter ';'. Con la adicin de la palabra clave "Component" seguida por la
referencia al nombre de fuente, se completa la referencia al recurso de la biblioteca de fuentes. En el ejemplo
de cdigo siguiente se muestra cmo hacer referencia a una fuente de un ensamblado de biblioteca de
recursos.
<Run FontFamily="/FontLibrary;Component/#Kootenay" FontSize="36">
ABCDEFGHIJKLMNOPQRSTUVWXYZ
</Run>
Limitaciones del uso de fuentes
En la lista siguiente se describen varias limitaciones para el empaquetado y el uso de fuentes en aplicaciones de
WPF:

Bits de permiso de incrustacin de fuentes: las aplicaciones de WPF no comprueban ni aplican ningn
bit de permiso de incrustacin de fuentes.

Fuentes del sitio de origen: las aplicaciones de WPF no permiten las referencias de fuente a
identificador de recursos uniforme (URI) http o ftp.

URI absoluto con la notacin "pack:": las aplicaciones de WPF no permiten crear un objeto FontFamily
mediante programacin que utilice "pack:" como parte de la referencia absoluta de identificador de
recursos uniforme (URI) a una fuente. Por ejemplo, "pack://application:,,,/resources/#Pericles Light"
es una referencia no vlida a una fuente.

MCT: Luis Dueas

Pag 248 de 473

Manual de Windows Presentation Foundation

Incrustacin automtica de fuentes: durante el tiempo de diseo, no se proporciona ninguna


compatibilidad con la bsqueda de las fuentes utilizadas en una aplicacin ni con la incrustacin
automtica de stas en los recursos de la misma.

Subconjuntos de fuentes: las aplicaciones de WPF no admiten la creacin de subconjuntos de fuentes


para documentos no fijos.

En los casos en que exista una referencia incorrecta, la aplicacin recurre a una fuente disponible.

6.5.6.3. Paquete de Fuentes OpenType de Ejemplo


En este tema se proporciona informacin general sobre el paquete de fuentes de ejemplo que contiene fuentes
OpenType distribuidas con Windows SDK. Las fuentes del ejemplo admiten caractersticas OpenType extendidas
que se pueden utilizar en las aplicaciones de Windows Presentation Foundation (WPF).
Fuentes del paquete de fuentes OpenType
Windows SDK proporciona un conjunto de fuentes OpenType de ejemplo que puede utilizar para crear
aplicaciones Windows Presentation Foundation (WPF). Estas fuentes de ejemplo se proporcionan bajo licencia
de Ascender Corporation. Estas fuentes implementan nicamente un subconjunto de las caractersticas totales
definidas por el formato OpenType. La lista siguiente contiene los nombres de las fuentes OpenType de
ejemplo, representados en la fuente correspondiente.
Fuentes del paquete de fuentes OpenType

Nota:
Ascender es un proveedor de productos avanzados de fuentes especializado en el diseo, la programacin y
las licencias de fuentes.
Tener acceso a las fuentes OpenType de ejemplo
Las fuentes OpenType de ejemplo se utilizan como recursos en el Ejemplo Using OpenType Fonts. En este
ejemplo se muestra el contenido de texto hospedado en FlowDocumentReader, que admite bsquedas,
navegacin, paginacin y ajuste de contenidos.
Ejemplo de fuentes OpenType

MCT: Luis Dueas

Pag 249 de 473

Manual de Windows Presentation Foundation


Al descargar este ejemplo, el directorio del proyecto contiene un subdirectorio fonts que contiene las fuentes
siguientes:
Nombre

Archivo

Kootenay

Kooten.ttf

Lindsey

Linds.ttf

Miramonte

Miramo.ttf

Miramonte Bold

Miramob.ttf

Pericles

Peric.ttf

Pericles Light

Pericl.ttf

Pescadero

Pesca.ttf

Pescadero Bold

Pescab.ttf

Nota:
Como programador, es su responsabilidad asegurarse de disponer de los derechos de licencia necesarios
para cualquier fuente que incruste en una aplicacin o redistribuya de cualquier otro modo.
Una vez las fuentes se encuentren en su equipo, puede mostrar un conjunto representativo de caracteres en
varios tamaos de fuente haciendo doble clic en el nombre del archivo de fuente en el subdirectorio. En la
captura de pantalla siguiente se muestra el resultado que se presenta para el archivo de la fuente Lindsey,
Linds.ttf.
Mostrar la fuente Lindsey

Utilizar las fuentes


Puede agregar fuentes a la aplicacin como elementos de contenido del proyecto que son independientes de los
archivos de ensamblado de la aplicacin. Esto significa que los elementos de contenido no se incrustan como
recursos dentro de un ensamblado. Como alternativa, puede agregar las fuentes a la aplicacin como
elementos de recursos del proyecto incrustados dentro de los archivos de ensamblado de la aplicacin.
Instalar las fuentes
Si lo desea, puede instalar las fuentes OpenType de ejemplo en el directorio de fuentes predeterminado de
Windows, ~\WINDOWS\Fonts. Utilice el applet Fonts del panel de control de Windows para instalar las
fuentes en el directorio de fuentes predeterminado de Windows. Una vez instaladas, las fuentes estn
accesibles por todas las aplicaciones, incluso XamlPad, que hacen referencia a las fuentes predeterminadas de
Windows.

MCT: Luis Dueas

Pag 250 de 473

Manual de Windows Presentation Foundation


Una tcnica til para ver las caractersticas de OpenType es utilizar XamlPad para crear un marcado que
experimente con el uso de diversos estilos tipogrficos.
Probar fuentes con XamlPad

6.5.6.4. Temas Cmo sobre Fuentes


En los temas de esta seccin se muestra cmo utilizar las caractersticas de fuente incluidas con Windows
Presentation Foundation (WPF).

6.5.6.4.1. Cmo: Enumerar Fuentes del Sistema


Ejemplo
En el ejemplo siguiente se muestra cmo enumerar las fuentes en la coleccin de fuentes del sistema. El
nombre de la familia de fuentes de cada FontFamily de SystemFontFamilies se agrega como un elemento a un
cuadro combinado.
public void FillFontComboBox(ComboBox comboBoxFonts)
{
// Enumerate the current set of system fonts,
// and fill the combo box with the names of the fonts.
foreach (FontFamily fontFamily in Fonts.SystemFontFamilies)
{
// FontFamily.Source contains the font family name.
comboBoxFonts.Items.Add(fontFamily.Source);
}
comboBoxFonts.SelectedIndex = 0;
}
Si hay varias versiones de la misma familia de fuentes que residen en el mismo directorio, la enumeracin de
fuentes de Windows Presentation Foundation (WPF) devuelve la versin ms reciente de la fuente. Si la
informacin de versin no proporciona la resolucin, se devuelve la fuente con la marca de tiempo ms
reciente. Si la informacin de marca de tiempo es equivalente, se devuelve el archivo de fuente que aparece
primero en orden alfabtico.

6.5.6.4.2. Cmo: Utilizar la Clase FontSizeConverter


Ejemplo
En este ejemplo se muestra cmo crear una instancia de FontSizeConverter y utilizarla para cambiar un tamao
de fuente.
En el ejemplo se define un mtodo personalizado denominado changeSize que convierte el contenido de
ListBoxItem, definido en un archivo Lenguaje de marcado de aplicaciones extensible (XAML) independiente, en
una instancia de Double y, a continuacin, en un valor de tipo String. Este mtodo pasa ListBoxItem a un
objeto FontSizeConverter, que convierte la propiedad Content de un ListBoxItem en una instancia de Double. A
continuacin, este valor se devuelve como valor de la propiedad FontSize del elemento TextBlock.

MCT: Luis Dueas

Pag 251 de 473

Manual de Windows Presentation Foundation


En este ejemplo tambin se define un segundo mtodo personalizado, denominado changeFamily. Este mtodo
convierte la propiedad Content de ListBoxItem en un valor String y, a continuacin, pasa el valor a la propiedad
FontFamily del elemento TextBlock. Este ejemplo no se ejecuta.
Private Sub changeSize(ByVal sender As Object, ByVal args As SelectionChangedEventArgs)
Dim li As ListBoxItem = CType(CType(sender, ListBox).SelectedItem, ListBoxItem)
Dim myFontSizeConverter As System.Windows.FontSizeConverter = _
New System.Windows.FontSizeConverter()
text1.FontSize = CType(myFontSizeConverter.ConvertFromString(li.Content.ToString()),
Double)
End Sub
Private Sub changeFamily(ByVal sender As Object, ByVal args As SelectionChangedEventArgs)
Dim li2 As ListBoxItem = CType(CType(sender, ListBox).SelectedItem, ListBoxItem)
text1.FontFamily = New System.Windows.Media.FontFamily(li2.Content.ToString())
End Sub

6.5.7. Glifos
Los glifos son una representacin de bajo nivel de un carcter que se va a dibujar en pantalla. Windows
Presentation Foundation (WPF) proporciona acceso directo a los glifos para los clientes que desean interceptar y
conservar el texto despus de darle formato.

6.5.7.1. Introduccin al Objeto GlyphRun y al Elemento Glyphs


En este tema se describen el objeto GlyphRun y el elemento Glyphs.
Introduccin a GlyphRun
Windows Presentation Foundation (WPF) proporciona compatibilidad con texto avanzado que incluye marcado
de nivel de glifos con acceso directo a Glyphs para los clientes que desean interceptar y conservar el texto
despus de darle formato. Estas caractersticas proporcionan compatibilidad vital para los distintos requisitos de
representacin de texto de cada uno de los escenarios siguientes.
1.

Presentacin en pantalla de documentos de formato fijo.

2.

Escenarios de impresin.

Lenguaje de marcado de aplicaciones extensible (XAML) como lenguaje de dispositivos de


impresin.

3.

Escritor de documentos XPS de Microsoft.


Controladores de impresora anteriores, salida de aplicaciones de Win32 en formato fijo.
Formato de colas de impresin.

Representacin de documentos de formato fijo, incluidos clientes de versiones anteriores de Windows


y otros dispositivos informticos.

Nota:
Glyphs y GlyphRun se han diseado para la presentacin de documentos de formato fijo y escenarios de
impresin. Windows Presentation Foundation (WPF) proporciona varios elementos para escenarios
generales de diseo e interfaz de usuario (UI), como Label y TextBlock.
Objeto GlyphRun
El objeto GlyphRun representa una secuencia de glifos de un solo tipo de letra de una nica fuente en un solo
tamao y con un nico estilo de representacin.
El objeto GlyphRun incluye detalles de la fuente, como la propiedad Indices del glifo y las posiciones de glifo
individuales. Adems, incluye los puntos de cdigo originales de Unicode a partir de los que se gener la
ejecucin, informacin de asignacin de desplazamiento de bfer de carcter a glifo, y marcadores por glifo y
por carcter.

MCT: Luis Dueas

Pag 252 de 473

Manual de Windows Presentation Foundation


GlyphRun tiene un objeto FrameworkElement correspondiente de alto nivel, Glyphs. Glyphs se puede utilizar en
el rbol de elementos y en marcado XAML para representar la salida de GlyphRun.
Elemento Glyphs
El elemento Glyphs representa la salida de GlyphRun en XAML. La sintaxis de marcado siguiente se utiliza para
describir el elemento Glyphs.
<!-- The example shows how to use a Glyphs object. -->
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel Background="PowderBlue">
<Glyphs
FontUri
= "C:\WINDOWS\Fonts\TIMES.TTF"
FontRenderingEmSize = "100"
StyleSimulations
= "BoldSimulation"
UnicodeString
= "Hello World!"
Fill
= "Black"
OriginX
= "100"
OriginY
= "200" />
</StackPanel>
</Page>
Las definiciones de propiedad siguientes corresponden a los primeros cuatro atributos del marcado del ejemplo.
Propiedad

Descripcin

FontUri

Especifica un identificador de recursos: el nombre de archivo, identificador de


recursos uniforme (URI) de web o referencia de recurso en el archivo .exe o
contenedor de la aplicacin.

FontRenderingEmSize

Especifica el tamao de fuente en unidades de la superficie de dibujo (el valor


predeterminado es 0,96 pulgadas).

StyleSimulations

Especifica los marcadores para los estilos de negrita y cursiva.

BidiLevel

Especifica el nivel del diseo bidireccional. Los nmeros pares y el cero significan
que el diseo es de izquierda a derecha; los valores impares representan un
diseo de derecha a izquierda.

Propiedad Indices
La propiedad Indices es una cadena de especificaciones del glifo. En aquellos casos en que una secuencia de
glifos forma un clster nico, la especificacin del primer glifo del clster va precedida por un a especificacin
del nmero de glifos y puntos de cdigo se combinan para constituir el clster. La propiedad Indices recopila en
una cadena las propiedades siguientes.

ndices de glifo
Anchos de avance del glifo
Combinacin de vectores de asociacin de glifos
Asignacin del clster de los puntos de cdigo a los glifos
Marcadores de glifo

Cada especificacin del glifo tiene la forma siguiente.


[GlyphIndex][,[Advance][,[uOffset][,[vOffset][,[Flags]]]]]
Mtricas de glifos
Cada glifo define mtricas que especifican cmo se alinea con otros Glyphs. En el grfico siguiente se definen
las distintas calidades tipogrficas de dos caracteres de glifo diferentes.

MCT: Luis Dueas

Pag 253 de 473

Manual de Windows Presentation Foundation

Marcado de glifos
En el ejemplo de cdigo siguiente se muestra cmo se utilizan diversas propiedades del elemento Glyphs en
XAML.
<!-- The example shows how to use different property settings of Glyphs objects. -->
<Canvas
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="PowderBlue" >
<Glyphs
FontUri
= "C:\WINDOWS\Fonts\ARIAL.TTF"
FontRenderingEmSize = "36"
StyleSimulations
= "ItalicSimulation"
UnicodeString
= "Hello World!"
Fill
= "SteelBlue"
OriginX
= "50"
OriginY
= "75" />
<!-- "Hello World!" with default kerning -->
<Glyphs
FontUri
= "C:\WINDOWS\Fonts\ARIAL.TTF"
FontRenderingEmSize = "36"
UnicodeString
= "Hello World!"
Fill
= "Maroon"
OriginX
= "50"
OriginY
= "150" />
<!-- "Hello World!" with explicit character widths for proportional font -->
<Glyphs
FontUri
= "C:\WINDOWS\Fonts\ARIAL.TTF"
FontRenderingEmSize = "36"
UnicodeString
= "Hello World!"
Indices
= ",80;,80;,80;,80;,80;,80;,80;,80;,80;,80;,80"
Fill
= "Maroon"
OriginX
= "50"
OriginY
= "225" />
<!-- "Hello World!" with fixed-width font -->
<Glyphs
FontUri
= "C:\WINDOWS\Fonts\COUR.TTF"
FontRenderingEmSize = "36"
StyleSimulations
= "BoldSimulation"
UnicodeString
= "Hello World!"
Fill
= "Maroon"
OriginX
= "50"
OriginY
= "300" />
<!-- "Open file" without "fi" ligature -->
<Glyphs
FontUri
= "C:\WINDOWS\Fonts\TIMES.TTF"
FontRenderingEmSize = "36"
StyleSimulations
= "BoldSimulation"
UnicodeString
= "Open file"

MCT: Luis Dueas

Pag 254 de 473

Manual de Windows Presentation Foundation


Fill
= "SlateGray"
OriginX
= "400"
OriginY
= "75" />
<!-- "Open file" with "fi" ligature -->
<Glyphs
FontUri
= "C:\WINDOWS\Fonts\TIMES.TTF"
FontRenderingEmSize = "36"
StyleSimulations
= "BoldSimulation"
UnicodeString
= "Open file"
Indices
= ";;;;;(2:1)191"
Fill
= "SlateGray"
OriginX
= "400"
OriginY
= "150" />
</Canvas>

6.5.7.2. Dibujar Texto mediante Glifos


En este tema se explica cmo utilizar el objeto Glyphs de bajo nivel para mostrar texto en Lenguaje de
marcado de aplicaciones extensible (XAML).
Ejemplo
En los ejemplos siguientes se muestra cmo definir las propiedades de un objeto Glyphs en Lenguaje de
marcado de aplicaciones extensible (XAML). El objeto Glyphs representa la salida de GlyphRun en XAML. En los
ejemplos se supone que las fuentes Arial, Courier New y Times New Roman estn instaladas en la carpeta
C:\WINDOWS\Fonts del equipo local.
<!-- The example shows how to use a Glyphs object. -->
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel Background="PowderBlue">
<Glyphs
FontUri
= "C:\WINDOWS\Fonts\TIMES.TTF"
FontRenderingEmSize = "100"
StyleSimulations
= "BoldSimulation"
UnicodeString
= "Hello World!"
Fill
= "Black"
OriginX
= "100"
OriginY
= "200" />
</StackPanel>
</Page>
En este ejemplo se muestra cmo definir otras propiedades de los objetos Glyphs en XAML.
<!-- The example shows how to use different property settings of Glyphs objects. -->
<Canvas
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="PowderBlue" >
<Glyphs
FontUri
= "C:\WINDOWS\Fonts\ARIAL.TTF"
FontRenderingEmSize = "36"
StyleSimulations
= "ItalicSimulation"
UnicodeString
= "Hello World!"
Fill
= "SteelBlue"
OriginX
= "50"
OriginY
= "75" />
<!-- "Hello World!" with default kerning -->
<Glyphs
FontUri
= "C:\WINDOWS\Fonts\ARIAL.TTF"
FontRenderingEmSize = "36"
UnicodeString
= "Hello World!"
Fill
= "Maroon"
OriginX
= "50"

MCT: Luis Dueas

Pag 255 de 473

Manual de Windows Presentation Foundation


OriginY
= "150" />
<!-- "Hello World!" with explicit character widths for proportional font -->
<Glyphs
FontUri
= "C:\WINDOWS\Fonts\ARIAL.TTF"
FontRenderingEmSize = "36"
UnicodeString
= "Hello World!"
Indices
= ",80;,80;,80;,80;,80;,80;,80;,80;,80;,80;,80"
Fill
= "Maroon"
OriginX
= "50"
OriginY
= "225" />
<!-- "Hello World!" with fixed-width font -->
<Glyphs
FontUri
= "C:\WINDOWS\Fonts\COUR.TTF"
FontRenderingEmSize = "36"
StyleSimulations
= "BoldSimulation"
UnicodeString
= "Hello World!"
Fill
= "Maroon"
OriginX
= "50"
OriginY
= "300" />
<!-- "Open file" without "fi" ligature -->
<Glyphs
FontUri
= "C:\WINDOWS\Fonts\TIMES.TTF"
FontRenderingEmSize = "36"
StyleSimulations
= "BoldSimulation"
UnicodeString
= "Open file"
Fill
= "SlateGray"
OriginX
= "400"
OriginY
= "75" />
<!-- "Open file" with "fi" ligature -->
<Glyphs
FontUri
= "C:\WINDOWS\Fonts\TIMES.TTF"
FontRenderingEmSize = "36"
StyleSimulations
= "BoldSimulation"
UnicodeString
= "Open file"
Indices
= ";;;;;(2:1)191"
Fill
= "SlateGray"
OriginX
= "400"
OriginY
= "150" />
</Canvas>

6.5.8. Temas Cmo sobre Tipografa


En los temas de esta seccin se describe cmo utilizar la compatibilidad de Windows Presentation Foundation
(WPF) para la presentacin enriquecida de texto en las aplicaciones.

6.5.8.1. Cmo: Crear una Decoracin de Texto


Un objeto TextDecoration es una ornamentacin visual que se puede agregar al texto. Hay cuatro tipos de
decoraciones de texto: subrayado, lnea base, tachado y lnea alta. En el ejemplo siguiente, se muestran las
ubicaciones de las decoraciones de texto con respecto al texto.
Ejemplo de tipos de decoracin de texto

Para agregar una decoracin al texto, cree un objeto TextDecoration y modifique sus propiedades. Utilice la
propiedad Location para especificar dnde aparecer la decoracin de texto, como un subrayado. Utilice la
propiedad Pen para especificar el aspecto de la decoracin de texto, como un relleno slido o un color de
degradado. Si no especifica un valor para la propiedad Pen, las decoraciones tienen como valor predeterminado

MCT: Luis Dueas

Pag 256 de 473

Manual de Windows Presentation Foundation


el mismo color que el texto. Una vez definido un objeto TextDecoration, agrguelo a la coleccin
TextDecorations del objeto de texto deseado.
En el ejemplo siguiente, se muestra una decoracin de texto a la que se la ha aplicado el estilo con un pincel de
degradado lineal y un lpiz rayado.
Ejemplo de subrayado con un pincel de degradado lineal y un lpiz rayado

El objeto Hyperlink es un elemento de contenido dinmico insertado que permite hospedar hipervnculos dentro
del contenido dinmico. De manera predeterminada, Hyperlink utiliza un objeto TextDecoration para mostrar un
subrayado. Crear instancias de los objetos TextDecoration puede afectar intensamente al rendimiento, en
especial si hay muchos objetos Hyperlink. Si realiza un uso excesivo de elementos Hyperlink, puede ser
conveniente mostrar la lnea de subrayado nicamente al desencadenar un evento, como el evento MouseEnter.
En el ejemplo siguiente, el subrayado para el vnculo "My MSN" es dinmico: nicamente aparece cuando se
activa el evento MouseEnter.
Hipervnculos definidos con TextDecorations

Ejemplo
En el ejemplo de cdigo siguiente, una decoracin de texto de subrayado utiliza el valor de fuente
predeterminado.
// Use the default font values for the strikethrough text decoration.
private void SetDefaultStrikethrough()
{
// Set the underline decoration directly to the text block.
TextBlock1.TextDecorations = TextDecorations.Strikethrough;
}
<!-- Use the default font values for the strikethrough text decoration. -->
<TextBlock TextDecorations="Strikethrough" FontSize="36" >
The quick red fox
</TextBlock>
En el ejemplo de cdigo siguiente, se crea una decoracin de texto de subrayado con un pincel de color slido
para el lpiz.
// Use a Red pen for the underline text decoration.
private void SetRedUnderline()
{
// Create an underline text decoration. Default is underline.
TextDecoration myUnderline = new TextDecoration();
// Create a solid color brush pen for the text decoration.
myUnderline.Pen = new Pen(Brushes.Red, 1);
myUnderline.PenThicknessUnit = TextDecorationUnit.FontRecommended;
// Set the underline decoration to a TextDecorationCollection and add it to the text
block.
TextDecorationCollection myCollection = new TextDecorationCollection();
myCollection.Add(myUnderline);
TextBlock2.TextDecorations = myCollection;
}
<!-- Use a Red pen for the underline text decoration -->
<TextBlock FontSize="36" >
jumped over
<TextBlock.TextDecorations>
<TextDecorationCollection>
<TextDecoration

MCT: Luis Dueas

Pag 257 de 473

Manual de Windows Presentation Foundation


PenThicknessUnit="FontRecommended">
<TextDecoration.Pen>
<Pen Brush="Red" Thickness="1" />
</TextDecoration.Pen>
</TextDecoration>
</TextDecorationCollection>
</TextBlock.TextDecorations>
</TextBlock>
En el ejemplo de cdigo siguiente, se crea una decoracin de texto subrayado con un pincel de degradado lineal
para el lpiz rayado.
// Use a linear gradient pen for the underline text decoration.
private void SetLinearGradientUnderline()
{
// Create an underline text decoration. Default is underline.
TextDecoration myUnderline = new TextDecoration();
// Create a linear gradient pen for the text decoration.
Pen myPen = new Pen();
myPen.Brush = new LinearGradientBrush(Colors.Yellow, Colors.Red, new Point(0, 0.5), new
Point(1, 0.5));
myPen.Brush.Opacity = 0.5;
myPen.Thickness = 1.5;
myPen.DashStyle = DashStyles.Dash;
myUnderline.Pen = myPen;
myUnderline.PenThicknessUnit = TextDecorationUnit.FontRecommended;
// Set the underline decoration to a TextDecorationCollection and add it to the text block.
TextDecorationCollection myCollection = new TextDecorationCollection();
myCollection.Add(myUnderline);
TextBlock3.TextDecorations = myCollection;
}
<!-- Use a linear gradient pen for the underline text decoration. -->
<TextBlock FontSize="36">the lazy brown dog.
<TextBlock.TextDecorations>
<TextDecorationCollection>
<TextDecoration
PenThicknessUnit="FontRecommended">
<TextDecoration.Pen>
<Pen Thickness="1.5">
<Pen.Brush>
<LinearGradientBrush Opacity="0.5"
StartPoint="0,0.5" EndPoint="1,0.5">
<LinearGradientBrush.GradientStops>
<GradientStop Color="Yellow" Offset="0" />
<GradientStop Color="Red" Offset="1" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Pen.Brush>
<Pen.DashStyle>
<DashStyle Dashes="2"/>
</Pen.DashStyle>
</Pen>
</TextDecoration.Pen>
</TextDecoration>
</TextDecorationCollection>
</TextBlock.TextDecorations>
</TextBlock>

6.5.8.2. Cmo: Usar una Decoracin de Texto con un Hipervnculo


El objeto Hyperlink es un elemento de contenido dinmico insertado que permite hospedar hipervnculos dentro
del contenido dinmico. De manera predeterminada, Hyperlink utiliza un objeto TextDecoration para mostrar un
subrayado. Crear instancias de los objetos TextDecoration puede afectar intensamente al rendimiento, en

MCT: Luis Dueas

Pag 258 de 473

Manual de Windows Presentation Foundation


especial si hay muchos objetos Hyperlink. Si realiza un uso excesivo de elementos Hyperlink, puede ser
conveniente mostrar la lnea de subrayado nicamente al desencadenar un evento, como el evento MouseEnter.
En el ejemplo siguiente, el subrayado para el vnculo "My MSN" es dinmico: nicamente aparece cuando se
activa el evento MouseEnter.
Hipervnculos definidos con TextDecorations

Ejemplo
En el ejemplo de marcado siguiente se muestra Hyperlink con y sin subrayado:
<!-- Hyperlink with default underline. -->
<Hyperlink NavigateUri="http://www.msn.com">
MSN Home
</Hyperlink>
<Run Text=" | " />
<!-- Hyperlink with no underline. -->
<Hyperlink Name="myHyperlink" TextDecorations="None"
MouseEnter="OnMouseEnter"
MouseLeave="OnMouseLeave"
NavigateUri="http://www.msn.com">
My MSN
</Hyperlink>
En el ejemplo de cdigo siguiente se muestra cmo crear un subrayado para Hyperlink cuando se produce el
evento MouseEnter y quitarlo cuando se produce el evento MouseLeave.
// Display the underline on only the MouseEnter event.
private void OnMouseEnter(object sender, EventArgs e)
{
myHyperlink.TextDecorations = TextDecorations.Underline;
}
// Remove the underline on the MouseLeave event.
private void OnMouseLeave(object sender, EventArgs e)
{
myHyperlink.TextDecorations = null;
}

6.5.8.3. Cmo: Aplicar Transformaciones a Texto


Las transformaciones pueden modificar la presentacin del texto en la aplicacin. Los ejemplos siguientes
utilizan diferentes tipos de representacin de las transformaciones que afectan a la presentacin del texto en un
control TextBlock.
Ejemplo
En el ejemplo siguiente se muestra texto girado alrededor de un punto especfico del plano bidimensional x-y.
Ejemplo de texto girado 90 grados

MCT: Luis Dueas

Pag 259 de 473

Manual de Windows Presentation Foundation


En el ejemplo de cdigo siguiente se utiliza un objeto RotateTransform para girar texto. Un valor Angle de 90
gira el elemento 90 grados en el sentido de las agujas del reloj.
<!-- Rotate the text 90 degrees using a RotateTransform. -->
<TextBlock FontFamily="Arial Black" FontSize="64" Foreground="Moccasin"
Margin ="80, 10, 0, 0"> Text Transforms
<TextBlock.RenderTransform>
<RotateTransform Angle="90" />
</TextBlock.RenderTransform>
</TextBlock>
En el ejemplo siguiente se muestra la segunda lnea de texto ajustada a una escala del 150% a lo largo del eje
X y la tercera lnea de texto ajustada a una escala del 150% a lo largo del eje Y.
Ejemplo de texto escalado

En el ejemplo de cdigo siguiente se utiliza ScaleTransform para escalar el texto desde su tamao original.
<!-- Scale the text using a ScaleTransform. -->
<TextBlock Name="textblockScaleMaster" FontSize="32" Foreground="SteelBlue"
Text="Scaled Text" Margin="100, 0, 0, 0" Grid.Column="0" Grid.Row="0">
</TextBlock>
<TextBlock FontSize="32" FontWeight="Bold" Foreground="SteelBlue"
Text="{Binding Path=Text, ElementName=textblockScaleMaster}"
Margin="100, 0, 0, 0" Grid.Column="0" Grid.Row="1">
<TextBlock.RenderTransform>
<ScaleTransform ScaleX="1.5" ScaleY="1.0" />
</TextBlock.RenderTransform>
</TextBlock>
<TextBlock FontSize="32" FontWeight="Bold" Foreground="SteelBlue"
Text="{Binding Path=Text, ElementName=textblockScaleMaster}"
Margin="100, 0, 0, 0" Grid.Column="0" Grid.Row="2">
<TextBlock.RenderTransform>
<ScaleTransform ScaleX="1.0" ScaleY="1.5" />
</TextBlock.RenderTransform>
</TextBlock>
Nota:
Escalar texto no es lo mismo que aumentar el tamao de la fuente de texto. Los tamaos de fuente se
calculan de forma independiente para proporciona la mejor resolucin en diferentes tamaos. El texto
escalado, por otro lado, conserva las proporciones del texto de tamao original.
En el ejemplo siguiente se muestra el texto sesgado a lo largo del eje X.
Ejemplo de texto sesgado

En el ejemplo de cdigo siguiente se utiliza un objeto SkewTransform para sesgar el texto. Un sesgo, tambin
conocido como distorsin, es una transformacin que expande el espacio de coordenadas de una manera no
uniforme. En este ejemplo, las dos cadenas de texto estn sesgadas -30 y 30 a lo largo de la coordenada x.
<!-- Skew the text using a SkewTransform. -->
<TextBlock Name="textblockSkewMaster" FontSize="32" FontWeight="Bold" Foreground="Maroon"
Text="Skewed Text" Margin="125, 0, 0, 0" Grid.Column="0" Grid.Row="0">
<TextBlock.RenderTransform>
<SkewTransform AngleX="-30" AngleY="0" />
</TextBlock.RenderTransform>
</TextBlock>
<TextBlock FontSize="32" FontWeight="Bold" Foreground="Maroon"
Text="{Binding Path=Text, ElementName=textblockSkewMaster}"
Margin="100, 0, 0, 0" Grid.Column="0" Grid.Row="1">

MCT: Luis Dueas

Pag 260 de 473

Manual de Windows Presentation Foundation


<TextBlock.RenderTransform>
<SkewTransform AngleX="30" AngleY="0" />
</TextBlock.RenderTransform>
</TextBlock>
En el ejemplo siguiente se muestra texto trasladado o movido, a lo largo del eje x y del eje y.
Ejemplo de texto trasladado

En el ejemplo de cdigo siguiente se utiliza un objeto TranslateTransform para desplazar texto. En este
ejemplo, una copia del texto primario ligeramente desplazada crea un efecto de sombra.
<!-- Skew the text using a TranslateTransform. -->
<TextBlock FontSize="32" FontWeight="Bold" Foreground="Black"
Text="{Binding Path=Text, ElementName=textblockTranslateMaster}"
Margin="100, 0, 0, 0" Grid.Column="0" Grid.Row="0">
<TextBlock.RenderTransform>
<TranslateTransform X="2" Y="2" />
</TextBlock.RenderTransform>
</TextBlock>
<TextBlock Name="textblockTranslateMaster" FontSize="32" FontWeight="Bold"
Foreground="Coral" Text="Translated Text" Margin="100, 0, 0, 0"
Grid.Column="0" Grid.Row="0"/>
Nota:
El objeto DropShadowBitmapEffect proporciona un rico conjunto de caractersticas para ofrecer efectos de
sombra.

6.5.8.4. Cmo: Aplicar Animaciones a Texto


Las animaciones pueden modificar la presentacin y el aspecto del texto de la aplicacin. En los ejemplos
siguientes se utilizan tipos diferentes de animaciones para influir en la presentacin del texto en un control
TextBlock.
Ejemplo
En el ejemplo siguiente se utiliza un objeto DoubleAnimation para animar el ancho del bloque de texto. El valor
del ancho cambia del ancho del bloque de texto a 0 a lo largo de 10 segundos y, a continuacin, se invierten los
valores de ancho y se contina. Este tipo de animacin crea un efecto de barrido.
<TextBlock Name="MyWipedText" Margin="20" Width="480" Height="100" FontSize="48"
FontWeight="Bold" Foreground="Maroon"> This is wiped text
<!-- Animates the text block's width. -->
<TextBlock.Triggers>
<EventTrigger RoutedEvent="TextBlock.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="MyWipedText"
Storyboard.TargetProperty="(TextBlock.Width)" To="0.0" Duration="0:0:10"
AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
En el ejemplo siguiente se utiliza un objeto DoubleAnimation para animar la opacidad del bloque de texto. El
valor de opacidad cambia de 1,0 a 0 a lo largo de 5 segundos y, a continuacin, se invierten los valores de
opacidad y se contina.
<TextBlock Name="MyFadingText" Margin="20" Width="640" Height="100" FontSize="48"
FontWeight="Bold" Foreground="Maroon"> This is fading text
<!-- Animates the text block's opacity. -->
<TextBlock.Triggers>

MCT: Luis Dueas

Pag 261 de 473

Manual de Windows Presentation Foundation


<EventTrigger RoutedEvent="TextBlock.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="MyFadingText"
Storyboard.TargetProperty="(TextBlock.Opacity)"
From="1.0" To="0.0" Duration="0:0:5"
AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
En el diagrama siguiente se muestra el efecto del control TextBlock al cambiar su opacidad de 1.00 a 0.00
durante el intervalo de 5 segundos definido por la propiedad Duration.
Cambio de la opacidad del texto de 1,00 a 0,00

En el ejemplo siguiente se utiliza ColorAnimation para animar el color de primer plano del bloque de texto. El
valor del color de primer plano cambia de un color a un segundo color a lo largo de 5 segundos y, a
continuacin, se invierten los valores de color y se contina.
<TextBlock Name="MyChangingColorText" Margin="20" Width="640" Height="100" FontSize="48"
FontWeight="Bold"> This is changing color text
<TextBlock.Foreground>
<SolidColorBrush x:Name="MySolidColorBrush" Color="Maroon" />
</TextBlock.Foreground>
<!-- Animates the text block's color. -->
<TextBlock.Triggers>
<EventTrigger RoutedEvent="TextBlock.Loaded">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="MySolidColorBrush"
Storyboard.TargetProperty="Color" From="DarkOrange" To="SteelBlue"
Duration="0:0:5" AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
En el ejemplo siguiente se utiliza un objeto DoubleAnimation para girar el bloque de texto. El bloque de texto
realiza un giro completo a lo largo de 20 segundos y, a continuacin, contina repitiendo el giro.
<TextBlock Name="MyRotatingText" Margin="20" Width="640" Height="100" FontSize="48"
FontWeight="Bold" Foreground="Teal"> This is rotating text
<TextBlock.RenderTransform>
<RotateTransform x:Name="MyRotateTransform" Angle="0" CenterX="230" CenterY="25"/>
</TextBlock.RenderTransform>
<!-- Animates the text block's rotation. -->
<TextBlock.Triggers>
<EventTrigger RoutedEvent="TextBlock.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="MyRotateTransform"
Storyboard.TargetProperty="(RotateTransform.Angle)"
From="0.0" To="360" Duration="0:0:10" RepeatBehavior="Forever" />
</Storyboard>

MCT: Luis Dueas

Pag 262 de 473

Manual de Windows Presentation Foundation


</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>

6.5.8.5. Cmo: Crear un Efecto de Texto


TextEffect es un objeto auxiliar que permite tratar el texto como uno o ms grupos de caracteres en una
cadena de texto. En el ejemplo siguiente, tomado de Ejemplo TextEffect, se muestra un carcter individual al
que se aplica una rotacin. Cada carcter gira de manera independiente a intervalos de 1 segundo.
Ejemplo de animacin de efecto de texto giratorio

Ejemplo
En el ejemplo de cdigo siguiente, se define un efecto TextEffect para un objeto TextBlock. En este caso, el
efecto de animacin deseado es una transformacin RotateTransform, que se aplicar a cada carcter en la
propiedad Text.
<TextBlock.TextEffects>
<!-- The TextEffect to animate. -->
<TextEffect PositionCount="1" x:Name="MyTextEffect">
<TextEffect.Transform>
<RotateTransform x:Name="TextEffectRotateTransform"
Angle="0" CenterX="10" CenterY="10" />
</TextEffect.Transform>
</TextEffect>
</TextBlock.TextEffects>
Nota:
Si desea girar la cadena de texto completa como una sola unidad, aplique la transformacin
RotateTransform a la propiedad RenderTransform de TextBlock.
En el ejemplo de cdigo siguiente se muestran las animaciones que se definen para las propiedades Angle y
CenterX. La secuencia de animacin se controla definiendo un objeto Int32AnimationUsingKeyFrames y
haciendo referencia a TextEffect al establecer TargetName y TargetProperty. La propiedad PositionStart cambia
de 0 a 12 durante la secuencia de animacin, lo que corresponde a la cadena de texto de 13 caracteres.
<TextBlock.Triggers>
<EventTrigger RoutedEvent="TextBlock.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ParallelTimeline RepeatBehavior="Forever">
<!-- Animates the angle of the RotateTransform applied to the TextEffect. -->
<DoubleAnimation Storyboard.TargetName="TextEffectRotateTransform"
Storyboard.TargetProperty="Angle" From="0" To="360"
Duration="00:00:0.75" BeginTime="0:0:0.25" />
</ParallelTimeline>
<!-- Animates the horizontal center of the RotateTransform applied to the
TextEffect. -->
<DoubleAnimation From="30" To="370" Duration="00:00:13"
RepeatBehavior="Forever" AutoReverse="True"
Storyboard.TargetName="TextEffectRotateTransform"
Storyboard.TargetProperty="CenterX" />
<!-- Animates the position of the TextEffect. -->
<Int32AnimationUsingKeyFrames Storyboard.TargetName="MyTextEffect"
Storyboard.TargetProperty="PositionStart" Duration="0:0:13"
AutoReverse="True" RepeatBehavior="Forever">
<Int32AnimationUsingKeyFrames.KeyFrames>
<DiscreteInt32KeyFrame Value="0" KeyTime="0:0:0" />

MCT: Luis Dueas

Pag 263 de 473

Manual de Windows Presentation Foundation


<DiscreteInt32KeyFrame Value="1" KeyTime="0:0:1" />
<DiscreteInt32KeyFrame Value="2" KeyTime="0:0:2" />
<DiscreteInt32KeyFrame Value="3" KeyTime="0:0:3" />
<DiscreteInt32KeyFrame Value="4" KeyTime="0:0:4" />
<DiscreteInt32KeyFrame Value="5" KeyTime="0:0:5" />
<DiscreteInt32KeyFrame Value="6" KeyTime="0:0:6" />
<DiscreteInt32KeyFrame Value="7" KeyTime="0:0:7" />
<DiscreteInt32KeyFrame Value="8" KeyTime="0:0:8" />
<DiscreteInt32KeyFrame Value="9" KeyTime="0:0:9" />
<DiscreteInt32KeyFrame Value="10" KeyTime="0:0:10" />
<DiscreteInt32KeyFrame Value="11" KeyTime="0:0:11" />
<DiscreteInt32KeyFrame Value="12" KeyTime="0:0:12" />
</Int32AnimationUsingKeyFrames.KeyFrames>
</Int32AnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>

6.5.8.6. Cmo: Crear Texto con Sombreado


En los ejemplos de esta seccin se muestra cmo crear gran variedad de efectos de sombra para el texto
mostrado.
Ejemplo
El objeto DropShadowBitmapEffect permite crear diversos de efectos de sombra para los objetos de Windows
Presentation Foundation (WPF). En el ejemplo siguiente se muestra un tipo tpico de efecto de sombra paralela
aplicado al texto. En este caso, la sombra es una sombra suave, lo que significa que su color est desenfocado.
Ejemplo de texto con una sombra suave

Puede controlar el ancho de una sombra estableciendo la propiedad ShadowDepth. Un valor de 4.0 indica un
ancho de la sombra de 4 pxeles. Puede controlar la suavidad, o desenfoque, de una sombra modificando la
propiedad Softness. Un valor de 0.0 indica que no est desenfocada en absoluto; un valor de 1.0 indica el
desenfoque total. En el ejemplo de cdigo siguiente se muestra cmo se crea una sombra suave.
<!-- Soft single shadow. -->
<TextBlock Text="Shadow Text" Foreground="Teal">
<TextBlock.BitmapEffect>
<DropShadowBitmapEffect ShadowDepth="4" Direction="330" Color="Black"
Opacity="0.5" Softness="0.25" />
</TextBlock.BitmapEffect>
</TextBlock>
Nota:
Estos efectos de sombra no atraviesan la canalizacin de representacin de texto de Windows Presentation
Foundation (WPF). Como resultado, ClearType se deshabilita al utilizar estos efectos.
En el ejemplo siguiente se muestra un efecto de sombra paralela ntida aplicada al texto. En este caso, la
sombra no est desenfocada.
Ejemplo de texto con una sombra ntida

Puede crear una sombra ntida estableciendo la propiedad Softness en 0.0, que indica que no se utiliza ningn
desenfoque. Puede controlar la direccin de la sombra modificando la propiedad Direction. Establezca el valor

MCT: Luis Dueas

Pag 264 de 473

Manual de Windows Presentation Foundation


direccional de esta propiedad en un valor de grados comprendido entre 0 y 360. En el diagrama siguiente se
muestran los valores direccionales del valor de la propiedad Direction.
Diagrama de direccin de DropShadow

En el ejemplo de cdigo siguiente se muestra cmo se crea una sombra ntida.


<!-- Hard single shadow. -->
<TextBlock Text="Shadow Text" Foreground="Maroon">
<TextBlock.BitmapEffect>
<DropShadowBitmapEffect ShadowDepth="6" Direction="135" Color="Maroon"
Opacity="0.35" Softness="0.0" />
</TextBlock.BitmapEffect>
</TextBlock>
En el ejemplo siguiente se muestra un efecto de sombra que combina la aplicacin de una sombra ntida y
suave al texto.
El ejemplo de texto con una sombra ntida y suave

En el ejemplo de cdigo siguiente se muestra cmo crear y combinar una sombra ntida y suave.
<!-- Hard shadow on top of soft shadow. -->
<TextBlock Text="Shadow Text" Foreground="CornflowerBlue">
<TextBlock.BitmapEffect>
<BitmapEffectGroup>
<BitmapEffectGroup.Children>
<DropShadowBitmapEffect ShadowDepth="5" Direction="330" Color="DarkSlateBlue"
Opacity="0.75" Softness="0.50" />
<DropShadowBitmapEffect ShadowDepth="2" Direction="330" Color="Maroon"
Opacity="0.5" Softness="0.0" />
</BitmapEffectGroup.Children>
</BitmapEffectGroup>
</TextBlock.BitmapEffect>
</TextBlock>
En el ejemplo siguiente se muestra una variacin del ejemplo anterior. En este ejemplo, la sombra suave
muestra una intensidad de color aleatoria. Puede controlar la intensidad de color aleatoria modificando la
propiedad Noise. Un valor de 0.0 indica ningn ruido, un valor de 1.0 indica el ruido mximo.
Ejemplo de texto con una sombra ntida y suave con ruido

En el ejemplo de cdigo siguiente se muestra cmo se crea una sombra con ruido.
<!-- Hard shadow on top of noisy shadow. -->
<TextBlock Text="Shadow Text" Foreground="Silver">
<TextBlock.BitmapEffect>
<BitmapEffectGroup>
<BitmapEffectGroup.Children>
<DropShadowBitmapEffect ShadowDepth="3" Direction="330" Color="Black"
Opacity="0.75" Softness="0.0" />
<DropShadowBitmapEffect Noise="0.5" ShadowDepth="6" Direction="330"
Color="Black" Opacity="0.35" Softness="0.25" />
</BitmapEffectGroup.Children>

MCT: Luis Dueas

Pag 265 de 473

Manual de Windows Presentation Foundation


</BitmapEffectGroup>
</TextBlock.BitmapEffect>
</TextBlock>
Utilizar un efecto de resplandor exterior de mapa de bits
OuterGlowBitmapEffect se puede utilizar para crear un efecto similar a la sombra. Sin embargo, un resplandor
exterior se irradia uniformemente por detrs del texto, a diferencia de DropShadowBitmapEffect, que se
representa segn una direccin especificada.
En el ejemplo siguiente se muestra un efecto de resplandor exterior aplicado al texto.
Ejemplo de texto con un efecto del resplandor exterior

Puede controlar el ancho de un resplandor exterior estableciendo la propiedad GlowSize. Un valor de 4.0 indica
un ancho del resplandor exterior de 4 pxeles. En el siguiente ejemplo de cdigo se muestra cmo crear un
efecto de resplandor exterior.
<!-- Shadow effect by creating an outer glow. -->
<TextBlock Text="Shadow Text" Foreground="SteelBlue">
<TextBlock.BitmapEffect>
<OuterGlowBitmapEffect GlowSize="4.0" GlowColor="Orange" Opacity="1.0"/>
</TextBlock.BitmapEffect>
</TextBlock>
Utilizar un efecto de mapa de bits de desenfoque
Un BlurBitmapEffect se puede utilizar para crear un efecto similar a la sombra que se puede colocar detrs de
un objeto de texto. Un efecto de mapa de bits de desenfoque aplicado al texto lo desenfoca de manera
uniforme en todas direcciones.
En el ejemplo siguiente se muestra un efecto de desenfoque aplicado al texto.
Ejemplo de texto con un efecto de desenfoque

En el ejemplo de cdigo siguiente se muestra cmo se crea un efecto de desenfoque.


<!-- Shadow effect by creating a blur. -->
<TextBlock Text="Shadow Text" Foreground="Green" Grid.Column="0" Grid.Row="0" >
<TextBlock.BitmapEffect>
<BlurBitmapEffect Radius="8.0" KernelType="Box"/>
</TextBlock.BitmapEffect>
</TextBlock>
<TextBlock Text="Shadow Text" Foreground="Maroon" Grid.Column="0" Grid.Row="0" />
Utilizar una transformacin de traslacin
Un TranslateTransform se puede utilizar para crear un efecto similar a la sombra que se puede colocar detrs
de un objeto de texto.
En el ejemplo de cdigo siguiente se utiliza un objeto TranslateTransform para desplazar texto. En este
ejemplo, una copia del texto primario ligeramente desplazada crea un efecto de sombra.
Ejemplo de texto que utiliza una transformacin para un efecto de sombra

En el ejemplo de cdigo siguiente se muestra cmo crear una transformacin para un efecto de sombra.
<!-- Shadow effect by creating a transform. -->
<TextBlock Foreground="Black" Text="Shadow Text" Grid.Column="0" Grid.Row="0">
<TextBlock.RenderTransform>
<TranslateTransform X="3" Y="3" />
</TextBlock.RenderTransform>
</TextBlock>
<TextBlock Foreground="Coral" Text="Shadow Text" Grid.Column="0" Grid.Row="0">
</TextBlock>

MCT: Luis Dueas

Pag 266 de 473

Manual de Windows Presentation Foundation

6.5.8.7. Cmo: Crear Texto con Contorno


En la mayora de los casos, al agregar ornamentacin a las cadenas de texto en una aplicacin de Windows
Presentation Foundation (WPF), se utiliza el texto considerndolo una coleccin de caracteres discretos, o glifos.
Por ejemplo, podra crear un pincel de degradado lineal y aplicarlo a la propiedad Foreground de un objeto
TextBox. Al mostrar o modificar el cuadro de texto, el pincel de degradado lineal se aplica automticamente al
conjunto actual de caracteres de la cadena de texto.
Ejemplo de un pincel de degradado lineal aplicado a un cuadro de texto

Sin embargo, tambin puede convertir el texto en objetos Geometry, lo que permite crear otros tipos de texto
visualmente enriquecido. Por ejemplo, podra crear un objeto Geometry basado en el contorno de una cadena
de texto.
Ejemplo de un pincel de degradado lineal aplicado a la geometra del contorno de texto

Cuando se convierte el texto en un objeto Geometry, deja de ser una coleccin de caracteres; no se pueden
modificar los caracteres de la cadena de texto. Sin embargo, se puede cambiar la apariencia del texto
convertido modificando sus propiedades de trazo y relleno. El trazo se refiere al contorno del texto convertido;
el relleno se refiere al rea situada dentro del contorno del texto convertido.
En los ejemplos siguientes se muestran varias maneras de crear efectos visuales modificando el trazo y el
relleno del texto convertido.
Ejemplo de cmo establecer el trazo y el relleno en diferentes colores

Ejemplo de un pincel de imagen aplicado al trazo

Tambin es posible modificar el rectngulo de seleccin o resaltado del texto convertido. En el ejemplo
siguiente se muestra una manera de crear efectos visuales modificando el trazo y el resaltado del texto
convertido.
Ejemplo de un pincel de imagen aplicado al trazo y al resaltado

Ejemplo
La clave para convertir texto en un objeto Geometry es utilizar el objeto FormattedText. Una vez creado este
objeto, puede utilizar los mtodos BuildGeometry y BuildHighlightGeometry para convertir el texto en objetos
Geometry. El primer mtodo devuelve la geometra del texto con formato; el segundo mtodo devuelve la
geometra del rectngulo de seleccin del texto con formato. En el ejemplo de cdigo siguiente se muestra
cmo crear un objeto FormattedText y recuperar las geometras del texto con formato y su rectngulo de
seleccin.
/// <summary>
/// Create the outline geometry based on the formatted text.
/// </summary>
public void CreateText()
{
System.Windows.FontStyle fontStyle = FontStyles.Normal;
FontWeight fontWeight = FontWeights.Medium;
if (Bold == true) fontWeight = FontWeights.Bold;
if (Italic == true) fontStyle = FontStyles.Italic;
// Create the formatted text based on the properties set.

MCT: Luis Dueas

Pag 267 de 473

Manual de Windows Presentation Foundation


FormattedText formattedText = new FormattedText(Text,
CultureInfo.GetCultureInfo("en-us"), FlowDirection.LeftToRight,
new Typeface(Font,fontStyle,fontWeight,FontStretches.Normal), FontSize,
System.Windows.Media.Brushes.Black // This brush does not matter since we use the
geometry of the text.);
// Build the geometry object that represents the text.
_textGeometry = formattedText.BuildGeometry(new System.Windows.Point(0, 0));
// Build the geometry object that represents the text hightlight.
if (Highlight == true)
{
_textHighLightGeometry = formattedText.BuildHighlightGeometry(
new System.Windows.Point(0, 0));
}
}
Para mostrar los objetos Geometry recuperados, deber tener acceso al DrawingContext del objeto que
muestra el texto convertido. En estos ejemplos de cdigo, esto se hace creando un objeto de control
personalizado derivado de una clase que admite la representacin definida por el usuario.
Para mostrar objetos Geometry en el control personalizado, invalide el mtodo OnRender. El mtodo invalidado
deber utilizar el mtodo DrawGeometry para dibujar los objetos Geometry.
/// <summary>
/// OnRender override draws the geometry of the text and optional highlight.
/// </summary>
/// <param name="drawingContext">Drawing context of the OutlineText control.</param>
protected override void OnRender(DrawingContext drawingContext)
{
// Draw the outline based on the properties that are set.
drawingContext.DrawGeometry(Fill, new System.Windows.Media.Pen(Stroke,
StrokeThickness), _textGeometry);
// Draw the text highlight based on the properties that are set.
if (Highlight == true)
{
drawingContext.DrawGeometry(null, new System.Windows.Media.Pen(Stroke,
StrokeThickness), _textHighLightGeometry);
}
}

6.5.8.8. Cmo: Crear una Animacin de PathGeometry para Texto


Puede convertir el texto con formato en un objeto PathGeometry y utiliza el objeto para resaltar el texto. Por
ejemplo, puede aplicar una animacin al objeto PathGeometry para que la animacin siga el contorno del texto
con formato.
En el ejemplo siguiente se muestra texto con formato convertido en un objeto PathGeometry. Una elipse
animada sigue el contorno, o los trazos, del texto representado.
Ejemplo de texto con formato representado como una geometra con un resaltado animado

Ejemplo de cdigo heredado


En el ejemplo de cdigo siguiente se utiliza un objeto Path para mostrar la geometra del texto con formato. El
objeto Path puede dibujar formas cerradas o abiertas, varias formas y formas curvas. Se crea un objeto Ellipse
animado se crea que seguir el contorno, o los trazos, del texto con formato.
<!-- Top-left starting point should be half the width of the ellipse so the text strokes
align to the center of circle. -->
<Path Canvas.Top="15" Canvas.Left="15" Stroke="SteelBlue" StrokeThickness="3"
Fill="LightSteelBlue" Name="path" />
<Ellipse Canvas.Top="0" Canvas.Left="0" Width="30" Height="30">
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0.5,0.5" Center="0.5,0.5" RadiusX="0.5" RadiusY="0.5">

MCT: Luis Dueas

Pag 268 de 473

Manual de Windows Presentation Foundation


<RadialGradientBrush.GradientStops>
<GradientStop Color="Yellow" Offset="0.25" />
<GradientStop Color="Transparent" Offset="1" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<MatrixTransform />
</Ellipse.RenderTransform>
<Ellipse.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard x:Name="storyboard">
<MatrixAnimationUsingPath x:Name="matrixAnimation" Duration="0:00:40"
RepeatBehavior="Forever" Storyboard.TargetProperty="RenderTransform.Matrix" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Ellipse.Triggers>
</Ellipse>
En el ejemplo de cdigo siguiente se muestra cmo se crea un objeto PathGeometry. El se asigna a la
propiedad Data de un objeto Path, que representa el texto con formato convertido en una geometra. A
continuacin, el objeto PathGeometry se asigna a la propiedad PathGeometry de un objeto MatrixAnimation
UsingPath, que proporciona el trazado que debe seguir la elipse animada.
// Display the text string and animate the ellipse to trace the text character outlines.
public void DisplayText(string textToDisplay)
{
// Create a formatted text string.
FormattedText formattedText = new FormattedText(textToDisplay,
CultureInfo.GetCultureInfo("en-us"), FlowDirection.LeftToRight,
new Typeface("Verdana"), 96, System.Windows.Media.Brushes.Black);
// Set the font weight to Bold for the formatted text.
formattedText.SetFontWeight(FontWeights.Bold);
// Build a geometry out of the formatted text.
Geometry geometry = formattedText.BuildGeometry(new System.Windows.Point(0, 0));
// Create a set of polygons by flattening the Geometry object.
PathGeometry pathGeometry = geometry.GetFlattenedPathGeometry();
// Supply the empty Path element in XAML with the PathGeometry in order to render the
// polygons.
path.Data = pathGeometry;
// Use the PathGeometry for the matrix animation,
// allowing the ellipse to follow the path of the polygons.
matrixAnimation.PathGeometry = pathGeometry;
}

6.5.8.9. Cmo: Dibujar Texto en el Fondo de un Control


Puede dibujar directamente el texto en el fondo de un control; para ello, convierta una cadena de texto en un
objeto FormattedText y, a continuacin, dibuje el objeto en el objeto DrawingContext del control. Tambin
puede utilizar esta tcnica para dibujar en el fondo de objetos derivados de Panel, tales como Canvas y
StackPanel.
Ejemplo de controles con fondos de texto personalizados

Ejemplo
Para dibujar en el fondo de un control, cree un nuevo objeto DrawingBrush y dibuje el texto convertido en el
DrawingContext del objeto. A continuacin, asigne el nuevo DrawingBrush a la propiedad de fondo del control.

MCT: Luis Dueas

Pag 269 de 473

Manual de Windows Presentation Foundation


En el ejemplo de cdigo siguiente se muestra cmo crear un objeto FormattedText y dibujar en el fondo de un
objeto Label y un objeto Button.
// Handle the WindowLoaded event for the window.
private void WindowLoaded(object sender, EventArgs e)
{
// Update the background property of the label and button.
myLabel.Background = new DrawingBrush(DrawMyText("My Custom Label"));
myButton.Background = new DrawingBrush(DrawMyText("Display Text"));
}
// Convert the text string to a geometry and draw it to the control's DrawingContext.
private Drawing DrawMyText(string textString)
{
// Create a new DrawingGroup of the control.
DrawingGroup drawingGroup = new DrawingGroup();
// Open the DrawingGroup in order to access the DrawingContext.
using (DrawingContext drawingContext = drawingGroup.Open())
{
// Create the formatted text based on the properties set.
FormattedText formattedText = new FormattedText(textString,
CultureInfo.GetCultureInfo("en-us"), FlowDirection.LeftToRight,
new Typeface("Comic Sans MS Bold"), 48,
System.Windows.Media.Brushes.Black );
// Build the geometry object that represents the text.
Geometry textGeometry = formattedText.BuildGeometry(new System.Windows.Point(20, 0));
// Draw a rounded rectangle under the text that is slightly larger than the text.
drawingContext.DrawRoundedRectangle(System.Windows.Media.Brushes.PapayaWhip, null,
new Rect(new System.Windows.Size(formattedText.Width + 50, formattedText.Height + 5)),
5.0, 5.0);
// Draw the outline based on the properties that are set.
drawingContext.DrawGeometry(System.Windows.Media.Brushes.Gold, new
System.Windows.Media.Pen(System.Windows.Media.Brushes.Maroon, 1.5), textGeometry);
// Return the updated DrawingGroup content to be used by the control.
return drawingGroup;
}
}

6.5.8.10. Cmo: Dibujar Texto en un Elemento Visual


En el ejemplo siguiente se muestra cmo dibujar texto en un objeto DrawingVisual mediante un objeto
DrawingContext. Para devolver un contexto de dibujo, se llama al mtodo RenderOpen de un objeto
DrawingVisual. Puede dibujar grficos y texto en un contexto de dibujo.
Para dibujar informacin de texto en el contexto del dibujo, se utiliza el mtodo DrawText de un objeto
DrawingContext. Cuando haya terminado de dibujar contenido en el contexto de dibujo, llame al mtodo Close
para cerrar el contexto de dibujo y conservar el contenido.
Ejemplo
// Create a DrawingVisual that contains text.
private DrawingVisual CreateDrawingVisualText()
{
// Create an instance of a DrawingVisual.
DrawingVisual drawingVisual = new DrawingVisual();
// Retrieve the DrawingContext from the DrawingVisual.
DrawingContext drawingContext = drawingVisual.RenderOpen();
// Draw a formatted text string into the DrawingContext.
drawingContext.DrawText(new FormattedText("Click Me!",
CultureInfo.GetCultureInfo("en-us"), FlowDirection.LeftToRight,
new Typeface("Verdana"), 36, System.Windows.Media.Brushes.Black),
new System.Windows.Point(200, 116));
// Close the DrawingContext to persist changes to the DrawingVisual.
drawingContext.Close();

MCT: Luis Dueas

Pag 270 de 473

Manual de Windows Presentation Foundation


return drawingVisual;
}

6.5.8.11. Cmo: Establecer Propiedades Tipogrficas


Windows Presentation Foundation (WPF) incluye compatibilidad con un conjunto complejo de propiedades
tipogrficas. Estas propiedades se pueden utilizar en el cdigo en el nivel de TextRun o en Lenguaje de
marcado de aplicaciones extensible (XAML).
Ejemplo
En el ejemplo de cdigo siguiente se muestra el uso de subndices, superndices y otras propiedades
tipogrficas (Variants) en XAML. Las variantes son aquellos tipos de elementos tipogrficos en que se utiliza
una forma de glifo alternativa para expresar una forma. Observe que la variante se limita exclusivamente al
texto contenido en el elemento Run.
<FlowDocument FontFamily="Palatino Linotype" FontSize="24">
<Paragraph>
This is an <Run Typography.Variants="Ordinal">ordinal</Run><LineBreak/>
This is a <Run Typography.Variants="Superscript">superscript</Run><LineBreak/>
This is a <Run Typography.Variants="Subscript">subscript</Run><LineBreak/>
This is an <Run Typography.Variants="Inferior">inferior</Run><LineBreak/>
</Paragraph>
</FlowDocument>
En el ejemplo de cdigo siguiente se muestra el uso de la propiedad tipogrfica Capitals en XAML. Observe que
la variante se aplica a todo el texto contenido en el elemento Paragraph.
<FlowDocument FontFamily="Palatino Linotype" FontSize="24">
<Paragraph Typography.Capitals="SmallCaps">
This example shows the use of the Capitals property
of the Typography object. The entirety of this paragraph
is displayed in small capitals letters,
except for the first letter of a sentence,
and where an uppercase letter is used. In these cases,
a large capital letter is used.
</Paragraph>
</FlowDocument>
En el ejemplo de cdigo siguiente se muestra el uso de las propiedades tipogrficas Capitals y NumeralStyle en
XAML. Observe que ambas propiedades tipogrficas se aplican al prrafo completo.
<FlowDocument FontFamily="Palatino Linotype" FontSize="24">
<Paragraph Typography.Capitals="SmallCaps" Typography.NumeralStyle="OldStyle">
Welcome to C#! . . . 14<LineBreak/>
Working with Variables, Operators, and Expressions . . . 29<LineBreak/>
Writing Methods and Applying Scope . . . 45
</Paragraph>
</FlowDocument>

6.5.8.12. Cmo: Usar Caracteres Especiales en XAML


Los archivos de marcado que se crean en Microsoft Visual Studio se guardan automticamente en el formato de
archivo Unicode UTF-8, lo que significa que la mayora de los caracteres especiales, como las tildes, se codifican
correctamente. Sin embargo, existe un conjunto de caracteres especiales utilizados con frecuencia que se
administran de manera diferente. Estos caracteres especiales siguen la norma de codificacin World Wide Web
Consortium (W3C) de XML.
En la tabla siguiente se muestra la sintaxis para codificar este conjunto de caracteres especiales:
Carcter

Sintaxis

Descripcin

<

&lt;

Smbolo de menor que.

MCT: Luis Dueas

Pag 271 de 473

Manual de Windows Presentation Foundation

>

&gt;

Smbolo de mayor que.

&

&amp;

Smbolo de Y comercial.

"

&quot;

Smbolo de comillas dobles.

Nota:
Si crea un archivo de marcado mediante un editor de texto, como el Bloc de notas de Windows, debe
guardar el archivo en el formato de archivo Unicode UTF-8 para conservar los caracteres especiales
codificados.
En el ejemplo siguiente se muestra cmo se pueden utilizar los caracteres especiales en el texto al crear el
marcado.
Ejemplo
<!-- Display special characters that require special encoding: < > & " -->
<TextBlock>
&lt;
<!-- Less than symbol -->
&gt;
<!-- Greater than symbol -->
&amp;
<!-- Ampersand symbol -->
&quot; <!-- Double quote symbol -->
</TextBlock>
<!-- Display miscellaneous special characters -->
<TextBlock>
Csar
<!-- AE dipthong symbol -->
2006 <!-- Copyright symbol -->
Espaol <!-- Tilde symbol -->

<!-- Yen symbol -->


</TextBlock>

6.6. Imprimir y Administracin de Sistemas de Impresin


Windows Vista y Microsoft .NET Framework introducen una nueva ruta de acceso de impresin, una alternativa
a la impresin Interfaz de dispositivo grfico de Microsoft Windows (GDI), y un conjunto inmensamente
ampliado de API de administracin de sistemas de impresin.

6.6.1. Informacin General sobre Impresin


Con Microsoft .NET Framework, los programadores de aplicaciones que utilicen Windows Presentation
Foundation (WPF) disponen de un nuevo conjunto enriquecido de API de impresin y administracin del sistema
de impresin. Con Windows Vista, algunas de estas mejoras del sistema de impresin estn tambin
disponibles para los programadores que creen aplicaciones formularios Windows Forms y los programadores
que utilicen cdigo no administrado. En el ncleo de esta nueva funcionalidad est el nuevo formato de archivo
XML Paper Specification (XPS) y la ruta de impresin XPS.
Acerca de XPS
XPS es un formato de documento electrnico, un formato de archivo de cola y un lenguaje de descripcin de
pginas. Es un formato de documento abierto que utiliza XML, Convenciones de empaquetado abierto (OPC) y
otros estndares del sector para crear documentos multiplataforma. XPS simplifica el proceso mediante el cual
los documentos digitales se crean, se comparten. se ven y se almacenan.
En el ejemplo de Printing an XPS Document y en Cmo: Imprimir mediante programacin archivos XPS se
muestran varias tcnicas de impresin de contenido basadas en XPS que utilizan WPF. Quiz le resulte til
consultar como referencia estos ejemplos mientras revisa el contenido de este tema. (Es recomendable que los
desarrolladores de cdigo no administrado consulten la ayuda de escape de impresora de convertidor de
documentos XPS de Microsoft. Los desarrolladores de formularios Windows Forms deben usar la API del espacio

MCT: Luis Dueas

Pag 272 de 473

Manual de Windows Presentation Foundation


de nombres System.Drawing.Printing, que no admite la ruta de impresin XPS completa, pero s una ruta de
impresin hbrida GDI-a-XPS. Vea Arquitectura de la ruta de impresin, a continuacin.)
Ruta de impresin XPS
La ruta de impresin XML Paper Specification (XPS) es una nueva caracterstica de Windows que redefine el
modo como se administra la impresin en las aplicaciones Windows. Dado que XPS puede reemplazar un
lenguaje de presentacin de documentos (como RTF), un formato de cola de impresin (como WMF) y un
lenguaje de descripcin de pginas (como PCL o Postscript), la nueva ruta de impresin mantiene el formato
XPS desde la publicacin de la aplicacin hasta el procesamiento final en el controlador o dispositivo de
impresin.
La ruta de impresin XPS se basa en el modelo de controlador de impresora XPS (XPSDrv), que ofrece varias
ventajas a los programadores, tales como la impresin "lo que ve es lo que imprime" (WYSIWYG), mejor
compatibilidad de color y un rendimiento de impresin significativamente mejorado. (Para ver ms informacin
sobre XPSDrv, vea Windows Driver Development)
El funcionamiento de la cola de impresin para documentos XPS es esencialmente igual que en versiones
anteriores de Windows. Sin embargo, se ha mejorado para que admita la ruta de impresin XPS adems de la
ruta de impresin GDI existente. La nueva ruta de impresin consume de forma nativa un archivo de cola XPS.
Aunque los controladores de impresora de modo usuario escritos para versiones anteriores de Windows
continuarn funcionando, se requiere un controlador de impresora XPS (XPSDrv) para utilizar la ruta de
impresin XPS.
Las ventajas de la ruta de impresin XPS son significativas, e incluyen:

Compatibilidad con impresin WYSIWYG

Compatibilidad nativa con perfiles de color avanzados, que incluyen 32 bits por canal (bpc), CMYK,
colores con nombre, tintas n y compatibilidad nativa de transparencia y degradados.

Rendimiento de impresin mejorado tanto para aplicaciones basadas en .NET Framework como en
Win32.

Formato XPS estndar del sector.

Para los escenarios de impresin bsicos, hay una API simple e intuitiva disponible con un punto nico de
entrada para la interfaz de usuario, la configuracin y el envo de trabajos. Para escenarios avanzados, se
agrega una compatibilidad adicional para la personalizacin de la interfaz de usuario (UI) (o ninguna interfaz de
usuario en absoluto), para la impresin sincrnica o asincrnica y capacidades de impresin por lotes. Ambas
opciones ofrecen compatibilidad de impresin o en modo de confianza total o parcial.
XPS se ha diseado pensando en la extensibilidad. Utilizando el marco de la extensibilidad, es posible agregar
caractersticas y funciones a XPS de una manera modular. Entre las caractersticas de extensibilidad se
incluyen:

Esquema de impresin El esquema pblico se actualiza peridicamente y permite la extensin rpida


de la funcionalidad del dispositivo. (Vea PrintTicket y PrintCapabilities, a continuacin.)

Canalizacin de filtros extensible. La canalizacin de filtros del controlador de impresora XPS (XPSDrv)
se ha diseado para permitir la impresin tanto directa como escalable de documentos XPS.

Arquitectura de la ruta de impresin


Aunque tanto las aplicaciones Win32 como .NET Framework son compatibles con XPS, las aplicaciones Win32 y
formularios Windows Forms utilizan una GDI para la conversin de XPS, con el propsito de crear contenido
XPS con formato para el controlador de impresora XPS (XPSDrv). Estas aplicaciones no necesitan usar la ruta
de impresin XPS y pueden continuar usando la impresin basada en Metarchivo mejorado (EMF). No obstante,

MCT: Luis Dueas

Pag 273 de 473

Manual de Windows Presentation Foundation


la mayora de las caractersticas y mejoras de XPS solamente estn disponibles para las aplicaciones orientadas
a la ruta de impresin XPS.
Para habilitar el uso de impresoras basadas en XPSDrv para aplicaciones Win32 y formularios Windows Forms,
el controlador de impresora XPS (XPSDrv) admite la conversin del formato GDI al formato XPS. El modelo
XPSDrv tambin permite convertir el formato XPS a formato GDI para que las aplicaciones Win32 puedan
imprimir documentos XPS. Para las aplicaciones WPF, la conversin del formato XPS al formato GDI la realizan
automticamente los mtodos Write y WriteAsync de la clase XpsDocumentWriter siempre que la cola de
impresin de destino de la operacin de escritura no tenga un controlador XPSDrv. (Las aplicaciones
formularios Windows Forms no pueden imprimir documentos XPS.)
La ilustracin siguiente describe el subsistema de impresin y define las partes proporcionadas por Microsofty
las partes definidas por proveedores de software y hardware.

Impresin XPS bsica


WPF define tanto una APIbsica como una avanzada. Para las aplicaciones que no requieran una
personalizacin de impresin extensa o que tengan acceso al conjunto de caractersticas de XPS, se dispone de
soporte de impresin bsico. La compatibilidad de impresin bsica se expone a travs de un control de dilogo
de impresin que requiere una configuracin mnima y muestra una interfaz de usuario conocida. Muchas
caractersticas de XPS estn disponibles utilizando este modelo de impresin simplificado.
PrintDialog
El control System.Windows.Controls.PrintDialog proporciona un punto de entrada nico para la configuracin de
interfaz de usuario y el envo de trabajos de XPS.
Impresin XPS avanzada
Para tener acceso al conjunto completo de caractersticas de XPS debe utilizarse la API de impresin avanzada.
Varias API relevantes se describen a continuacin con mayor detalle. Para ver una lista completa de las API de
ruta de impresin XPS, vea las referencias a los espacios de nombres System.Windows.Xps y System.Printing.
PrintTicket y PrintCapabilities
Las clases PrintTicket y PrintCapabilities son la base de las caractersticas avanzadas de XPS. Ambos tipos de
objetos son estructuras con formato XML de caractersticas orientadas a la impresin tales como la
intercalacin, la impresin a doble cara, el grapado, etc. Estas estructuras las define el Esquema de impresin.
Un objeto PrintTicket indica una impresora cmo procesar un trabajo de impresin. La clase PrintCapabilities
define la funcionalidad de una impresora. Consultando la funcionalidad de una impresora, se puede crear un
objeto PrintTicket que aprovecha al mximo las ventajas de las caractersticas compatibles de una impresora.
De igual forma, se puede evitar las caractersticas no compatibles.

MCT: Luis Dueas

Pag 274 de 473

Manual de Windows Presentation Foundation


En el siguiente ejemplo se muestra cmo consultar el objeto PrintCapabilities de una impresora y crear un
objeto PrintTicket usando cdigo.
// ---------------------- GetPrintTicketFromPrinter ----------------------/// <summary>
///
Returns a PrintTicket based on the current default printer.</summary>
/// <returns>
///
A PrintTicket for the current local default printer.</returns>
private PrintTicket GetPrintTicketFromPrinter()
{
PrintQueue printQueue = null;
LocalPrintServer localPrintServer = new LocalPrintServer();
// Retrieving collection of local printer on user machine
PrintQueueCollection localPrinterCollection = localPrintServer.GetPrintQueues();
System.Collections.IEnumerator localPrinterEnumerator =
localPrinterCollection.GetEnumerator();
if (localPrinterEnumerator.MoveNext())
{
// Get PrintQueue from first available printer
printQueue = (PrintQueue)localPrinterEnumerator.Current;
}
else
{
// No printer exist, return null PrintTicket
return null;
}
// Get default PrintTicket from printer
PrintTicket printTicket = printQueue.DefaultPrintTicket;
PrintCapabilities printCapabilites = printQueue.GetPrintCapabilities();
// Modify PrintTicket
if (printCapabilites.CollationCapability.Contains(Collation.Collated))
{
printTicket.Collation = Collation.Collated;
}
if ( printCapabilites.DuplexingCapability.Contains(Duplexing.TwoSidedLongEdge))
{
printTicket.Duplexing = Duplexing.TwoSidedLongEdge;
}
if (printCapabilites.StaplingCapability.Contains(Stapling.StapleDualLeft))
{
printTicket.Stapling = Stapling.StapleDualLeft;
}
return printTicket;
}// end:GetPrintTicketFromPrinter()
PrintServer y PrintQueue
La clase PrintServer representa un servidor de impresin de red, y la clase PrintQueue representa una
impresora y la lista de espera de los trabajos de salida asociados. Juntas, estas API permiten la administracin
avanzada de los trabajos de impresin de un servidor. Para administrar un objeto PrintQueue se utiliza un
objeto PrintServer o una de sus clases derivadas. El mtodo AddJob se utiliza para insertar un nuevo trabajo de
impresin en la cola.
En el ejemplo siguiente se muestra cmo crear un objeto LocalPrintServer y cmo obtener acceso a su objeto
PrintQueue predeterminado mediante cdigo.
// -------------------- GetPrintXpsDocumentWriter() ------------------/// <summary>
///
Returns an XpsDocumentWriter for the default print queue.</summary>
/// <returns>
///
An XpsDocumentWriter for the default print queue.</returns>
private XpsDocumentWriter GetPrintXpsDocumentWriter()
{
// Create a local print server

MCT: Luis Dueas

Pag 275 de 473

Manual de Windows Presentation Foundation


LocalPrintServer ps = new LocalPrintServer();
// Get the default print queue
PrintQueue pq = ps.DefaultPrintQueue;
// Get an XpsDocumentWriter for the default print queue
XpsDocumentWriter xpsdw = PrintQueue.CreateXpsDocumentWriter(pq);
return xpsdw;
}// end:GetPrintXpsDocumentWriter()
XpsDocumentWriter
Un objeto XpsDocumentWriter, con sus diversos mtodos Write y WriteAsync, se utiliza para escribir
documentos XPS en un objeto PrintQueue. Por ejemplo, el mtodo Write(FixedPage, PrintTicket) se utiliza para
generar sincrnicamente un documento XPS y PrintTicket. El mtodo WriteAsync(FixedDocument, PrintTicket)
se utiliza para generar de forma asincrnica un documento XPS y PrintTicket.
En el ejemplo siguiente se muestra cmo crear un objeto XpsDocumentWriter mediante cdigo.
// -------------------- GetPrintXpsDocumentWriter() ------------------/// <summary>
///
Returns an XpsDocumentWriter for the default print queue.</summary>
/// <returns>
///
An XpsDocumentWriter for the default print queue.</returns>
private XpsDocumentWriter GetPrintXpsDocumentWriter()
{
// Create a local print server
LocalPrintServer ps = new LocalPrintServer();
// Get the default print queue
PrintQueue pq = ps.DefaultPrintQueue;
// Get an XpsDocumentWriter for the default print queue
XpsDocumentWriter xpsdw = PrintQueue.CreateXpsDocumentWriter(pq);
return xpsdw;
}// end:GetPrintXpsDocumentWriter()
Los mtodos AddJob tambin proporcionan maneras de imprimir.
Ruta de impresin GDI
Aunque las aplicaciones WPF son compatibles de forma nativa con la ruta de acceso XPS, las aplicaciones Win32
y formularios Windows Forms tambin pueden aprovechar algunas de las caractersticas de XPS. El controlador
de impresora XPS (XPSDrv) puede convertir resultados basados en GDI al formato XPS. Para escenarios
avanzados, se admite la conversin personalizada de contenido mediante el Escape de impresora de
convertidor de documentos XPS de Microsoft. De igual forma, las aplicaciones WPF tambin pueden enviar
resultados a la ruta de impresin GDI si llaman a uno de los mtodos Write o WriteAsync de la clase
XpsDocumentWriter y designa una impresora que no es XpsDrv como cola de impresin de destino.
Para las aplicaciones que no requieran o no admitan la funcionalidad XPS, no se modifica la ruta de impresin
GDI actual.
Modelo de controlador de XPSDrv
La ruta de impresin XPS mejora la eficacia del administrador de trabajos de impresin utilizando XPS como
formato nativo de cola de impresin al imprimir en una impresora o un controlador compatible con XPS. El
proceso simplificado de administracin trabajos de impresin elimina la necesidad de generar un archivo de cola
intermedio, tal como un archivo de datos EMF, antes de poner el documento en la cola. Utilizando menores
tamaos de archivo de cola, la ruta de impresin XPS puede reducir el trfico de red y mejorar el rendimiento
de impresin.
EMF es un formato cerrado que representa el resultado de la aplicacin como una serie de llamadas a GDI para
servicios de representacin. A diferencia de EMF, el formato de cola de XPS representa el documento real, sin
que sea necesaria ninguna otra interpretacin cuando se enva a un controlador de impresora basado en XPS
(XPSDrv). Los controladores pueden trabajar directamente con los datos en este formato. Esta funcin elimina

MCT: Luis Dueas

Pag 276 de 473

Manual de Windows Presentation Foundation


las conversiones de datos y espacio de color necesarias al utilizar archivos EMF y controladores de impresin
basados en GDI.
El tamao de los archivo de cola suelen reducirse al utilizar documentos XPS destinados a un controlador de
impresora XPS (XPSDrv), en comparacin con sus equivalentes EMF; no obstante, hay excepciones:

Un grfico vectorial muy complejo, con varias capas o escrito de forma ineficaz puede ser mayor que
una versin de mapa de bits del mismo grfico.

Para la presentacin en pantalla, los archivos XPS incrustan las fuentes de dispositivo, as como las
fuentes con base en el equipo; por el contrario, los archivos de cola de GDI no incrustan las fuentes de
dispositivo. No obstante, ambos tipos de fuentes se agrupan en subconjuntos (vea a continuacin) y
los controladores de impresora pueden quitar las fuentes de dispositivo antes de transmitir el archivo a
la impresora.

La reduccin de tamao de la cola tiene lugar mediante diversos mecanismos:

Agrupacin de fuentes en subconjuntos. Solamente se almacenan en el archivo XPS los caracteres


utilizados realmente en el documento.

Compatibilidad con grficos avanzados. La compatibilidad nativa con transparencia y primitivas de


degradado evita la rasterizacin del contenido en el documento XPS.

Identificacin de recursos comunes. Los recursos que se utilizan varias veces (como una imagen
que representa un logotipo corporativo) se tratan como recursos compartidos y se cargan solamente
una vez.

Compresin ZIP. Todos los documentos XPS utilizan la compresin ZIP.

6.6.2. Temas Cmo de Impresin


En los temas en esta seccin se muestra cmo utilizar las caractersticas de impresin y de administracin del
sistema de impresin incluidas con Windows Presentation Foundation (WPF), as como la nueva ruta de acceso
de impresin de XML Paper Specification (XPS).

6.6.2.1. Cmo: Invocar un Cuadro de Dilogo de Impresin


Para proporcionar la capacidad de imprimir desde la aplicacin, basta con crear y abrir un objeto PrintDialog.
Ejemplo
El control PrintDialog proporciona un punto de entrada nico para la configuracin de interfaz de usuario y el
envo de trabajos de XPS. El control es fcil utilizar y pueden crearse instancias de l mediante marcado
Lenguaje de marcado de aplicaciones extensible (XAML) o cdigo. En el ejemplo siguiente se muestra cmo
crear instancias del control, abrirlo mediante cdigo e imprimir desde l. Tambin se muestra cmo asegurarse
de que el cuadro de dilogo permita al usuario establecer un intervalo de pginas concreto. En el ejemplo de
cdigo se da por hecho que existe un archivo FixedDocumentSequence.xps en la raz de la unidad de disco C:.
Encontrar este archivo en la subcarpeta \Content despus de descargar el ejemplo completo de Ejemplo
PrintDialog.
private void InvokePrint(object sender, RoutedEventArgs e)
{
// Create the print dialog object and set options
PrintDialog pDialog = new PrintDialog();
pDialog.PageRangeSelection = PageRangeSelection.AllPages;
pDialog.UserPageRangeEnabled = true;
// Display the dialog. This returns true if the user presses the Print button.
Nullable<Boolean> print = pDialog.ShowDialog();
if (print == true)
{

MCT: Luis Dueas

Pag 277 de 473

Manual de Windows Presentation Foundation


XpsDocument xpsDocument = new XpsDocument("C:\\FixedDocumentSequence.xps",
FileAccess.ReadWrite);
FixedDocumentSequence fixedDocSeq = xpsDocument.GetFixedDocumentSequence();
pDialog.PrintDocument(fixedDocSeq.DocumentPaginator, "Test print job");
}
}
Una vez abierto el cuadro de dilogo, el usuario podr seleccionar una de las impresoras instaladas en el
equipo. Tambin dispondr de la opcin de seleccionar el Escritor de documentos XPS de Microsoft para crear
un archivo XML Paper Specification (XPS) en lugar de imprimir.
Nota:
El control System.Windows.Controls.PrintDialog de WPF, que se explica en este tema, no se debe confundir
con el componente System.Windows.Forms.PrintDialog de formularios Windows Forms.
En sentido estricto, puede utilizar el mtodo PrintDocument sin llegar a abrir nunca el cuadro de dilogo. En
este sentido, el control se puede utilizar como componente de impresin oculto. No obstante, por motivos de
rendimiento, es preferible utilizar el mtodo AddJob o uno de los numerosos mtodos Write y WriteAsync de
XpsDocumentWriter.

6.6.2.2. Cmo: Clonar una Impresora


En la mayora de las empresas, en algn punto se adquieren varias impresoras del mismo modelo.
Normalmente, todas ellas se instalan configuraciones casi idnticas. Instalar cada impresora puede exigir
mucho tiempo y dar lugar a errores. El espacio de nombres System.Printing.IndexedProperties y la clase
InstallPrintQueue que se exponen con Microsoft .NET Framework permiten instalar al instante cualquier nmero
de colas de impresin adicionales que se clonan a partir de una existente.
Ejemplo
En el ejemplo siguiente, se clona una segunda cola de impresin a partir de otra existente. La segunda se
diferencia de la primera nicamente en su nombre, ubicacin, puerto y estado compartido. Los pasos
principales para hacerlo son los siguientes.
1.

Cree un objeto PrintQueue para la impresora existente que va a clonar.

2.

Cree un objeto PrintPropertyDictionary a partir de la propiedad PropertiesCollection del objeto


PrintQueue. La propiedad Value de cada entrada de este diccionario es un objeto de uno de los tipos
derivados de PrintProperty. Hay dos maneras de establecer el valor de una entrada en este diccionario.

Utilizar los mtodos Remove y Add del diccionario para quitar la entrada y, a continuacin,
volver a agregarla con el valor deseado.

Utilizar el mtodo SetProperty del diccionario.

En el ejemplo siguiente se muestran ambas maneras.


3.

Cree un objeto PrintBooleanProperty y establezca su propiedad Name en "IsShared" y su propiedad


Value en true.

4.

Utilice el objeto PrintBooleanProperty como valor de la entrada "IsShared" del objeto PrintProperty
Dictionary.

5.

Cree un objeto PrintStringProperty y establezca su propiedad Name en "ShareName" y su propiedad


Value en un valor de tipo String apropiado.

6.

Utilice el objeto PrintStringProperty como valor de la entrada "ShareName" del objeto PrintProperty
Dictionary.

7.

Cree otro objeto PrintStringProperty y establezca su propiedad Name en "Location" y su propiedad


Value en un valor de tipo String apropiado.

MCT: Luis Dueas

Pag 278 de 473

Manual de Windows Presentation Foundation


8.

Utilice el segundo objeto PrintStringProperty como valor de la entrada "Location" del objeto
PrintPropertyDictionary.

9.

Cree una matriz de valores de tipo String. Cada elemento es el nombre de un puerto del servidor.

10. Utilice InstallPrintQueue para instalar la nueva impresora con los nuevos valores.
A continuacin se muestra un ejemplo.
LocalPrintServer myLocalPrintServer = new
LocalPrintServer(PrintSystemDesiredAccess.AdministrateServer);
PrintQueue sourcePrintQueue = myLocalPrintServer.DefaultPrintQueue;
PrintPropertyDictionary myPrintProperties = sourcePrintQueue.PropertiesCollection;
// Share the new printer using Remove/Add methods
PrintBooleanProperty shared = new PrintBooleanProperty("IsShared", true);
myPrintProperties.Remove("IsShared");
myPrintProperties.Add("IsShared", shared);
// Give the new printer its share name using SetProperty method
PrintStringProperty theShareName = new PrintStringProperty("ShareName", "\"Son of " +
sourcePrintQueue.Name +"\"");
myPrintProperties.SetProperty("ShareName", theShareName);
// Specify the physical location of the new printer using Remove/Add methods
PrintStringProperty theLocation = new PrintStringProperty("Location", "the supply room");
myPrintProperties.Remove("Location");
myPrintProperties.Add("Location", theLocation);
// Specify the port for the new printer
String[] port = new String[] { "COM1:" };
// Install the new printer on the local print server
PrintQueue clonedPrinter = myLocalPrintServer.InstallPrintQueue("My clone of " +
sourcePrintQueue.Name, "Xerox WCP 35 PS", port, "WinPrint", myPrintProperties);
myLocalPrintServer.Commit();
// Report outcome
Console.WriteLine("{0} in {1} has been installed and shared as {2}", clonedPrinter.Name,
clonedPrinter.Location, clonedPrinter.ShareName);
Console.WriteLine("Press Return to continue ...");
Console.ReadLine();

6.6.2.3. Cmo: Diagnosticar Trabajos de Impresin Problemticos


Los administradores de red con frecuencia tienen que ocuparse de las quejas de los usuarios sobre trabajos de
impresin que no se imprimen o lo hacen con lentitud. El conjunto enriquecido de propiedades de trabajos de
impresin expuesto en las API de Microsoft .NET Framework proporciona un medio para realizar un diagnstico
remoto rpido de los trabajos de impresin.
Ejemplo
Los pasos principales para crear este tipo de utilidad son los siguientes.
1.

Identifique el trabajo de impresin que provoca la queja del usuario. Con frecuencia, los usuarios no lo
pueden hacer con precisin. Es posible que no sepan el nombre de los servidores de impresin o de las
impresoras. Puede que describan la ubicacin de la impresora con una terminologa distinta a la
utilizada al establecer su propiedad Location. En consecuencia, es conveniente generar una lista de
trabajos del usuario actualmente enviados. Si hay ms de uno, entonces puede utilizarse la
comunicacin entre el usuario y el administrador del sistema de impresin para determinar con
precisin qu trabajo es el que presenta problemas. Para ello, proceda como sigue.
a.

Obtenga una lista de todos los servidores de impresin.

b.

Recorra en bucle los servidores para consultar sus colas de impresin.

c.

En cada pasada del bucle de servidor, recorra en bucle todas las colas del servidor para
consultar sus trabajos.

MCT: Luis Dueas

Pag 279 de 473

Manual de Windows Presentation Foundation


d.

Dentro de cada pasada del bucle de cola, recorra en bucle sus trabajos y recolecte la
informacin identificativa sobre aqullos que ha enviado el usuario que se queja.

2.

Una vez identificado el trabajo de impresin problemtico, examine las propiedades pertinentes para
ver cul puede ser el problema. Por ejemplo, el trabajo se encuentra en un estado de error? o la
impresora correspondiente se ha desconectado antes de imprimir el trabajo?

El cdigo siguiente consiste en una serie de ejemplos de cdigo.


El primer ejemplo de cdigo contiene el bucle que recorre las colas de impresin. (Paso 1c anterior.) La variable
myPrintQueues es el objeto PrintQueueCollection correspondiente al servidor de impresin actual.
El ejemplo de cdigo comienza actualizando el objeto de cola de impresin actual mediante PrintQueue.Refresh.
De este modo, se asegura de que las propiedades del objeto se corresponden con precisin al estado de la
impresora fsica que representan. A continuacin, la aplicacin obtiene la coleccin de trabajos de impresin
que se encuentran en este momento en la cola de impresin, mediante GetPrintJobInfoCollection.
Luego, la aplicacin recorre en bucle la coleccin PrintSystemJobInfo y compara cada propiedad Submitter con
el alias del usuario que ha presentado la queja. Si coinciden, la aplicacin agrega informacin identificativa
sobre el trabajo a la cadena que se presentar. (Las variables jobList y userName se inicializan anteriormente
en la aplicacin.)
foreach (PrintQueue pq in myPrintQueues)
{
pq.Refresh();
PrintJobInfoCollection jobs = pq.GetPrintJobInfoCollection();
foreach (PrintSystemJobInfo job in jobs)
{
// Since the user may not be able to articulate which job is problematic,
// present information about each job the user has submitted.
if (job.Submitter == userName)
{
atLeastOne = true;
jobList = jobList + "\nServer:" + line;
jobList = jobList + "\n\tQueue:" + pq.Name;
jobList = jobList + "\n\tLocation:" + pq.Location;
jobList = jobList + "\n\t\tJob: " + job.JobName + " ID: " + job.JobIdentifier;
}
}// end for each print job
}// end for each print queue
En el ejemplo de cdigo siguiente se contina con la aplicacin en el paso 2. (Consulte lo anterior.) Se ha
identificado el trabajo problemtico y la aplicacin solicita la informacin que lo identificar. A partir de esta
informacin, crea los objetos PrintServer, PrintQueue y PrintSystemJobInfo.
En este punto, la aplicacin contiene una estructura de bifurcacin que corresponde a las dos maneras de
comprobar el estado de un trabajo de impresin:

Puede leer los marcadores de la propiedad JobStatus, que es del tipo PrintJobStatus.
Puede leer cada propiedad pertinente, como IsBlocked y IsInError.

En este ejemplo se muestran ambos mtodos, de modo que previamente se ha preguntado al usuario qu
mtodo desea utilizar y este ha respondido con "Y" (S) si desea utilizar los marcadores de la propiedad
JobStatus. Consulte ms adelante los detalles de los dos mtodos. Por ltimo, la aplicacin utiliza un mtodo
denominado ReportQueueAndJobAvailability para informar de si el trabajo se puede imprimir en este
momento de da.
// When the problematic print job has been identified, enter information about it.
Console.Write("\nEnter the print server hosting the job (including leading slashes \\\\): "
+ "\n(press Return for the current computer \\\\{0}): ", Environment.MachineName);
String pServer = Console.ReadLine();
if (pServer == "")

MCT: Luis Dueas

Pag 280 de 473

Manual de Windows Presentation Foundation


{
pServer = "\\\\" +Environment.MachineName;
}
Console.Write("\nEnter the print queue hosting the job: ");
String pQueue = Console.ReadLine();
Console.Write("\nEnter the job ID: ");
Int16 jobID = Convert.ToInt16(Console.ReadLine());
// Create objects to represent the server, queue, and print job.
PrintServer hostingServer = new PrintServer(pServer,
PrintSystemDesiredAccess.AdministrateServer);
PrintQueue hostingQueue = new PrintQueue(hostingServer, pQueue,
PrintSystemDesiredAccess.AdministratePrinter);
PrintSystemJobInfo theJob = hostingQueue.GetJob(jobID);
if (useAttributesResponse == "Y")
{
TroubleSpotter.SpotTroubleUsingJobAttributes(theJob);
// TroubleSpotter class is defined in the complete example.
}
else
{
TroubleSpotter.SpotTroubleUsingProperties(theJob);
}
TroubleSpotter.ReportQueueAndJobAvailability(theJob);
Para comprobar estado del trabajo de impresin mediante los marcadores de la propiedad JobStatus, se
comprueba cada marcador pertinente para ver si est establecido. La manera estndar de comprobar si un bit
est establecido en un conjunto de indicadores de bits, es realizar la operacin de AND lgico con el conjunto
de marcadores como uno de los operandos y con el propio marcador como el otro operando. Puesto que el
marcador nicamente tiene un bit establecido, el resultado de la operacin de AND lgico es que, a lo sumo,
ese mismo bit est establecido. Para averiguar si lo est o no, basta con comparar el resultado de la operacin
de AND lgico con el propio marcador.
Para cada atributo cuyo bit est establecido, el cdigo informa de ello en la pantalla de la consola y, en
ocasiones, sugiere una manera de responder. (El mtodo HandlePausedJob al que se llama si el trabajo o la
cola est en pausa se describe ms adelante.)
// Check for possible trouble states of a print job using the flags of the JobStatus
property
internal static void SpotTroubleUsingJobAttributes(PrintSystemJobInfo theJob)
{
if ((theJob.JobStatus & PrintJobStatus.Blocked) == PrintJobStatus.Blocked)
{
Console.WriteLine("The job is blocked.");
}
if (((theJob.JobStatus & PrintJobStatus.Completed) == PrintJobStatus.Completed)
|| ((theJob.JobStatus & PrintJobStatus.Printed) == PrintJobStatus.Printed))
{
Console.WriteLine("The job has finished. Have user recheck all output bins and be
sure the correct printer is being checked.");
}
if (((theJob.JobStatus & PrintJobStatus.Deleted) == PrintJobStatus.Deleted)
|| ((theJob.JobStatus & PrintJobStatus.Deleting) == PrintJobStatus.Deleting))
{
Console.WriteLine("The user or someone with administration rights to the queue has
deleted the job. It must be resubmitted.");
}
if ((theJob.JobStatus & PrintJobStatus.Error) == PrintJobStatus.Error)
{
Console.WriteLine("The job has errored.");
}
if ((theJob.JobStatus & PrintJobStatus.Offline) == PrintJobStatus.Offline)
{

MCT: Luis Dueas

Pag 281 de 473

Manual de Windows Presentation Foundation


Console.WriteLine("The printer is offline. Have user put it online with printer
front panel.");
}
if ((theJob.JobStatus & PrintJobStatus.PaperOut) == PrintJobStatus.PaperOut)
{
Console.WriteLine("The printer is out of paper of the size required by the job.
Have user add paper.");
}
if (((theJob.JobStatus & PrintJobStatus.Paused) == PrintJobStatus.Paused)
|| ((theJob.HostingPrintQueue.QueueStatus & PrintQueueStatus.Paused) ==
PrintQueueStatus.Paused))
{
HandlePausedJob(theJob);
//HandlePausedJob is defined in the complete example.
}
if ((theJob.JobStatus & PrintJobStatus.Printing) == PrintJobStatus.Printing)
{
Console.WriteLine("The job is printing now.");
}
if ((theJob.JobStatus & PrintJobStatus.Spooling) == PrintJobStatus.Spooling)
{
Console.WriteLine("The job is spooling now.");
}
if ((theJob.JobStatus & PrintJobStatus.UserIntervention)==PrintJobStatus.UserIntervention)
{
Console.WriteLine("The printer needs human intervention.");
}
}//end SpotTroubleUsingJobAttributes
Para comprobar el estado del trabajo de impresin mediante propiedades independientes, basta con leer cada
propiedad y, si la propiedad es true, informar de ello en la pantalla de la consola y, si se desea, sugerir una
manera de responder. (El mtodo HandlePausedJob al que se llama si el trabajo o la cola est en pausa se
describe ms adelante.)
// Check for possible trouble states of a print job using its properties
internal static void SpotTroubleUsingProperties(PrintSystemJobInfo theJob)
{
if (theJob.IsBlocked)
{
Console.WriteLine("The job is blocked.");
}
if (theJob.IsCompleted || theJob.IsPrinted)
{
Console.WriteLine("The job has finished. Have user recheck all output bins and be
sure the correct printer is being checked.");
}
if (theJob.IsDeleted || theJob.IsDeleting)
{
Console.WriteLine("The user or someone with administration rights to the queue has
deleted the job. It must be resubmitted.");
}
if (theJob.IsInError)
{
Console.WriteLine("The job has errored.");
}
if (theJob.IsOffline)
{
Console.WriteLine("The printer is offline. Have user put it online with printer
front panel.");
}
if (theJob.IsPaperOut)
{
Console.WriteLine("The printer is out of paper of the size required by the job.

MCT: Luis Dueas

Pag 282 de 473

Manual de Windows Presentation Foundation


Have user add paper.");
}
if (theJob.IsPaused || theJob.HostingPrintQueue.IsPaused)
{
HandlePausedJob(theJob);
//HandlePausedJob is defined in the complete example.
}
if (theJob.IsPrinting)
{
Console.WriteLine("The job is printing now.");
}
if (theJob.IsSpooling)
{
Console.WriteLine("The job is spooling now.");
}
if (theJob.IsUserInterventionRequired)
{
Console.WriteLine("The printer needs human intervention.");
}
}//end SpotTroubleUsingProperties
El mtodo HandlePausedJob permite al usuario de la aplicacin reanudar remotamente los trabajos en pausa.
Dado que puede existir una razn de peso por la que se ha puesto en pausa la cola de impresin, el mtodo
comienza solicitando una decisin del usuario sobre si reanudarla o no. Si la respuesta es "Y" (S), entonces se
llama al mtodo PrintQueue.Resume.
Luego, se pide al usuario que decida si se debe reanudar el propio trabajo, por si se ha puesto en pausa con
independencia de la cola de impresin. (Observe las diferencias entre PrintQueue.IsPaused y PrintSystem
JobInfo.IsPaused.) Si la respuesta es "Y" (S), entonces se llama al mtodo PrintSystemJobInfo.Resume, de lo
contrario, se llama al mtodo Cancel.
internal static void HandlePausedJob(PrintSystemJobInfo theJob)
{
// If there's no good reason for the queue to be paused, resume it and
// give user choice to resume or cancel the job.
Console.WriteLine("The user or someone with administrative rights to the queue" +
"\nhas paused the job or queue." +
"\nResume the queue? (Has no effect if queue is not paused.)" +
"\nEnter \"Y\" to resume, otherwise press return: ");
String resume = Console.ReadLine();
if (resume == "Y")
{
theJob.HostingPrintQueue.Resume();
// It is possible the job is also paused. Find out how the user wants to handle that.
Console.WriteLine("Does user want to resume print job or cancel it?" +
"\nEnter \"Y\" to resume (any other key cancels the print job): ");
String userDecision = Console.ReadLine();
if (userDecision == "Y")
{
theJob.Resume();
}
else
{
theJob.Cancel();
}
}//end if the queue should be resumed
}//end HandlePausedJob

6.6.2.4. Cmo: Detectar si un Trabajo de Impresin se puede Imprimir en esta


Hora del Da

MCT: Luis Dueas

Pag 283 de 473

Manual de Windows Presentation Foundation


Las colas de impresin no siempre estn disponibles durante las 24 horas del da. Tienen propiedades de hora
inicial y final que se pueden establecer para que no estn disponibles en determinados momentos del da. Por
ejemplo, esta caracterstica se puede utilizar para reservar una impresora para el uso exclusivo de un
departamento determinado a partir de las 5.00 p.m. Ese departamento tendra una cola diferente para dar
servicio a la impresora que los dems departamentos. La cola de los dems departamentos se establecera
como no disponible despus de las 5.00 p.m., mientras que la cola del departamento favorecido podra
establecerse de modo que estuviera disponible en todo momento.
Adems, se pueden establecer los propios trabajos de impresin para que nicamente puedan imprimirse en un
intervalo de tiempo especificado.
Las clases PrintQueue y PrintSystemJobInfo expuestas en las API de Microsoft .NET Framework ofrecen un
medio de comprobar de manera remota si un trabajo de impresin dado se puede imprimir en una cola
determinada en cada momento.
Ejemplo
El ejemplo siguiente se ha tomado de una aplicacin de ejemplo capaz de diagnosticar los problemas con un
trabajo de impresin.
Existen dos pasos principales para este tipo de funcin, que son los siguientes.
1.

Lea las propiedades StartTimeOfDay y UntilTimeOfDay del objeto PrintQueue a fin de determinar si la
hora actual se encuentra comprendida entre ellas.

2.

Lea las propiedades StartTimeOfDay y UntilTimeOfDay del objeto PrintSystemJobInfo a fin de


determinar si la hora actual se encuentra comprendida entre ellas.

No obstante, las complicaciones surgen del hecho de que estas propiedades no son objetos DateTime. En
cambio, se trata de objetos Int32 que expresan la hora del da como el nmero de minutos transcurridos desde
la medianoche. Es ms, no se trata de la medianoche en la zona horaria actual, sino de la medianoche segn la
hora universal coordinada (UTC).
En el primer ejemplo de cdigo se presenta el mtodo esttico ReportQueueAndJobAvailability, al que se
pasa un objeto PrintSystemJobInfo y que llama a mtodos auxiliares para determinar si el trabajo se puede
imprimir en este momento y, en caso contrario, cundo se puede hacer. Observe que PrintQueue no se pasa al
mtodo. Esto se debe a que PrintSystemJobInfo incluye una referencia a la cola en su propiedad
HostingPrintQueue.
Los mtodos subordinados incluyen el mtodo sobrecargado ReportAvailabilityAtThisTime, que puede
aceptar PrintQueue o PrintSystemJobInfo como parmetro. Tambin est el mtodo TimeConverter.
ConvertToLocalHumanReadableTime. Todos estos mtodos se describen ms adelante.
El mtodo ReportQueueAndJobAvailability comienza comprobando si la cola o el trabajo de impresin no
estn disponibles en este momento. Si cualquiera de ellos no est disponible, comprueba si la cola no est
disponible. Si no est disponible, el mtodo informa de ello y de la hora a la que la cola estar disponible de
nuevo. A continuacin, comprueba el trabajo y, si no est disponible, informa del prximo intervalo de tiempo
en que se podr imprimir. Por ltimo, el mtodo informa del momento ms prximo en que el trabajo se podr
imprimir. Ser el posterior de los dos siguientes.

La prxima hora a la que la cola de impresin est disponible.

La prxima hora a la que el trabajo de impresin est disponible.

Al informar sobre las horas del da, se llama al mtodo ToShortTimeString tambin, porque este mtodo
suprime los aos, meses y das del resultado. No se puede restringir la disponibilidad de una cola de impresin
o de un trabajo de impresin a determinados aos, meses o das concretos.

MCT: Luis Dueas

Pag 284 de 473

Manual de Windows Presentation Foundation


internal static void ReportQueueAndJobAvailability(PrintSystemJobInfo theJob)
{
if (!(ReportAvailabilityAtThisTime(theJob.HostingPrintQueue) &&
ReportAvailabilityAtThisTime(theJob)))
{
if (!ReportAvailabilityAtThisTime(theJob.HostingPrintQueue))
{
Console.WriteLine("\nThat queue is not available at this time of day." +
"\nJobs in the queue will start printing again at {0}",
TimeConverter.ConvertToLocalHumanReadableTime(
theJob.HostingPrintQueue.StartTimeOfDay).ToShortTimeString());
// TimeConverter class is defined in the complete sample
}
if (!ReportAvailabilityAtThisTime(theJob))
{
Console.WriteLine("\nThat job is set to print only between {0} and {1}",
TimeConverter.ConvertToLocalHumanReadableTime(theJob.StartTimeOfDay).ToShortTimeString(),
TimeConverter.ConvertToLocalHumanReadableTime(theJob.UntilTimeOfDay).ToShortTimeString());
}
Console.WriteLine("\nThe job will begin printing as soon as it reaches the top of
the queue after:");
if (theJob.StartTimeOfDay > theJob.HostingPrintQueue.StartTimeOfDay)
{
Console.WriteLine(TimeConverter.ConvertToLocalHumanReadableTime(theJob.
StartTimeOfDay).ToShortTimeString());
}
else
{
Console.WriteLine(TimeConverter.ConvertToLocalHumanReadableTime(theJob.
HostingPrintQueue.StartTimeOfDay).ToShortTimeString());
}
}//end if at least one is not available
}//end ReportQueueAndJobAvailability
Las dos sobrecargas del mtodo ReportAvailabilityAtThisTime son idnticas salvo por el tipo que se les
pasa, por lo que nicamente se presenta la versin PrintQueue a continuacin.
Nota:
El hecho de que los mtodos sean idnticos salvo por el tipo, suscita la pregunta de por qu en el ejemplo
no se crea un mtodo genrico ReportAvailabilityAtThisTime<T>. La razn es que este tipo de mtodo
tendra que estar restringido a una clase que tuviera las propiedades StartTimeOfDay y UntilTimeOfDay
a las que el mtodo llama, pero un mtodo genrico nicamente se puede restringir a una sola clase y la
nica clase comn a PrintQueue y PrintSystemJobInfo en el rbol de herencia es PrintSystemObject, que
carece de estas propiedades.
El mtodo ReportAvailabilityAtThisTime (que se presenta en el ejemplo de cdigo siguiente) comienza
inicializando una variable centinela Boolean en true. Se restablecer en false si la cola no est disponible.
Luego, el mtodo comprueba si los momentos de inicio y "hasta" son idnticos. Si lo son, la cola siempre est
disponible, por lo que el mtodo devuelve true.
Si la cola no est disponible en todo momento, el mtodo utiliza la propiedad UtcNow esttica para obtener la
hora actual como un objeto DateTime. (No se necesita la hora local porque las propiedades StartTimeOfDay y
UntilTimeOfDay se expresan en hora UTC.)
Sin embargo, estas dos propiedades no son objetos DateTime. Son valores Int32 que expresan la hora como el
nmero de minutos transcurridos desde la medianoche segn la hora UTC. As que es preciso convertir el objeto
DateTime en minutos desde la medianoche. Una vez convertidos, el mtodo se limita a comprobar si "ahora" se
encuentra entre las horas de inicio y "hasta" de la cola, establece el centinela en false si no es as, y devuelve el
centinela.
private static Boolean ReportAvailabilityAtThisTime(PrintQueue pq)

MCT: Luis Dueas

Pag 285 de 473

Manual de Windows Presentation Foundation


{
Boolean available = true;
if (pq.StartTimeOfDay != pq.UntilTimeOfDay)
// If the printer is not available 24 hours a day
{
DateTime utcNow = DateTime.UtcNow;
Int32 utcNowAsMinutesAfterMidnight = (utcNow.TimeOfDay.Hours * 60) +
utcNow.TimeOfDay.Minutes;
// If now is not within the range of available times . . .
if (!((pq.StartTimeOfDay < utcNowAsMinutesAfterMidnight)
&&
(utcNowAsMinutesAfterMidnight < pq.UntilTimeOfDay)))
{
available = false;
}
}
return available;
}//end ReportAvailabilityAtThisTime
El mtodo TimeConverter.ConvertToLocalHumanReadableTime (presentado en el ejemplo de cdigo
siguiente) no utiliza ningn mtodo introducido con Microsoft .NET Framework, de modo que su explicacin es
breve. El mtodo tiene una tarea de conversin doble: debe tomar un entero que expresa minutos
transcurridos desde la medianoche y convertirlo en una hora legible, y luego convertir este valor en la hora
local. Para ello, en primer lugar crea un objeto DateTime establecido en la medianoche segn la hora UTC y, a
continuacin, utiliza el mtodo AddMinutes para sumar los minutos que se pasaron al mtodo. Esto devuelve un
nuevo valor DateTime que expresa la hora original que se pas al mtodo. A continuacin, el mtodo
ToLocalTime convierte este valor en la hora local.
class TimeConverter
{
// Convert time as minutes past UTC midnight into human readable time in local time zone
internal static DateTime ConvertToLocalHumanReadableTime(Int32
timeInMinutesAfterUTCMidnight)
{
// Construct a UTC midnight object.
// Must start with current date so that the local Daylight Savings system, if any,
// will be taken into account.
DateTime utcNow = DateTime.UtcNow;
DateTime utcMidnight = new DateTime(utcNow.Year, utcNow.Month, utcNow.Day, 0, 0, 0,
DateTimeKind.Utc);
// Add the minutes passed into the method in order to get the intended UTC time.
Double minutesAfterUTCMidnight = (Double)timeInMinutesAfterUTCMidnight;
DateTime utcTime = utcMidnight.AddMinutes(minutesAfterUTCMidnight);
// Convert to local time.
DateTime localTime = utcTime.ToLocalTime();
return localTime;
}// end ConvertToLocalHumanReadableTime
}//end TimeConverter class

6.6.2.5. Cmo: Enumerar un Subconjunto de Colas de Impresin


Una situacin comn a la que se enfrentan los profesionales de la tecnologa de la informacin (TI) que
administran el conjunto de impresoras de toda la compaa es generar una lista de impresoras que tienen
determinadas caractersticas. El mtodo GetPrintQueues de un objeto PrintServer y la enumeracin
EnumeratedPrintQueueTypes proporcionan esta funcionalidad.
Ejemplo
En el ejemplo siguiente, el cdigo comienza creando una matriz de marcadores que especifican las
caractersticas de las colas de impresin que deseamos mostrar en la lista. En este ejemplo, se buscan las colas
de impresin que estn instaladas localmente en el servidor de impresin y son compartidas. La enumeracin
EnumeratedPrintQueueTypes proporciona muchas otras posibilidades.

MCT: Luis Dueas

Pag 286 de 473

Manual de Windows Presentation Foundation


A continuacin, el cdigo crea un objeto LocalPrintServer, una clase derivada de PrintServer. El servidor de
impresin local es el equipo en el que se ejecuta la aplicacin.
El ltimo paso significativo consiste en pasar la matriz al mtodo GetPrintQueues.
Por ltimo, se presentan los resultados al usuario.
// Specify that the list will contain only the print queues that are installed as local and
are shared
EnumeratedPrintQueueTypes[] enumerationFlags = {EnumeratedPrintQueueTypes.Local,
EnumeratedPrintQueueTypes.Shared};
LocalPrintServer printServer = new LocalPrintServer();
//Use the enumerationFlags to filter out unwanted print queues
PrintQueueCollection printQueuesOnLocalServer =
printServer.GetPrintQueues(enumerationFlags);
Console.WriteLine("These are your shared, local print queues:\n\n");
foreach (PrintQueue printer in printQueuesOnLocalServer)
{
Console.WriteLine("\tThe shared printer " + printer.Name + " is located at " +
printer.Location + "\n");
}
Console.WriteLine("Press enter to continue.");
Console.ReadLine();
Puede extender este ejemplo haciendo que el bucle foreach que recorre todas las colas de impresin realice un
filtrado mayor. Por ejemplo, podra dejar fuera las impresoras que no admitan la impresin a dos caras
haciendo que el bucle llame al mtodo GetPrintCapabilities de cada cola de impresin y pruebe el valor devuelto
correspondiente a la presencia de la impresin a dos caras.

6.6.2.6. Cmo: Ampliar el Esquema de Impresin y Crear Nuevas Clases del


Sistema de Impresin
Cuando la aplicacin debe funcionar con dispositivos de impresin especializados que tengan caractersticas no
reflejadas por las clases PrintSystemObject, PrintQueue, PrintCapabilities y PrintTicket existentes, quiz desee
derivar las nuevas clases mediante herencia, crear versiones extendidas de las clases PrintCapabilities y
PrintTicket y, probablemente, extender tambin el Print Schema. En este artculo se describen las principales
partes de este tipo de proyectos; pero se describe slo una de las muchas formas de extender las clases
administradas pertinentes.
Se recomienda que lea este artculo junto con la amplia documentacin del Print Schema. En este artculo se
presupone que, al menos, el lector tiene una idea bsica de qu es el esquema y de qu son los documentos de
PrintTicket y PrintCapabilities.
Ejemplo
El proyecto de ejemplo que se presenta en este artculo no proporciona todos los detalles necesarios para
compilar y ejecutar una aplicacin. Su finalidad es proporcionar una imagen slo de los pasos principales
necesarios

para

extender

la

funcionalidad

de

los

espacios

de

nombres

System.Printing

System.Printing.IndexedProperties a los dispositivos de impresin que tienen las caractersticas no admitidas


explcitamente en estos espacios de nombres. Los ejemplos de cdigo se proporcionan slo cuando se necesitan
detalles concretos.
Asimismo, estos ejemplos no incluyen necesariamente las tcnicas de las buenas prcticas de programacin ni
cdigo seguro. Se pasa por alto cualquier informacin que no sea importante para el tema del artculo.
Finalmente, este artculo se dirige principalmente a los programadores de aplicaciones en lugar de a los
programadores de controladores de dispositivos. Por esta razn, se hace hincapi en escribir los objetos
PrintTicket, en vez de los objetos PrintCapabilities.

MCT: Luis Dueas

Pag 287 de 473

Manual de Windows Presentation Foundation


Derivar un nueva clase mediante herencia
Empiece por derivar una clase que represente el dispositivo a partir de la clase PrintQueue. En el ejemplo de
cdigo siguiente, se deriva una clase BrailleEmbosser. Braille es un idioma que leen los invidentes ya que sus
smbolos estn formados por "puntos" que sobresalen de la superficie del papel para que puedan sentirlos con
las puntas de los dedos. Una impresora Braille es simplemente una impresora que imprime Braille. Algunos
controladores de las impresoras Braille pueden traducir el Braille normal al grado 3 de Braille, tambin
denominado Braille 3, versin que ahorra espacio de Braille y que usa muchas contracciones y abreviaturas. El
ejemplo proporciona una propiedad para representar esta caracterstica.
A menudo tambin deber sobrecargar algunas propiedades y mtodos heredados.
class BrailleEmbosser : PrintQueue
{
public BrailleEmbosser(PrintServer ps, String s, PrintSystemDesiredAccess access) :
base(ps, s, access)
{
// Optionally, include here code to set non-inherited fields.
}
// other constructor declarations omitted
private Boolean isBraille3Enabled;
public Boolean IsBraile3Enabled
{
get { return isBraille3Enabled; }
set { isBraille3Enabled = value; }
}
// remainder of class definition omitted
}
Determinar si las caractersticas del dispositivo ya estn definidas en el esquema de impresin
Las clases PrintTicket y PrintCapabilities tienen propiedades que representan las caractersticas de impresora
ms habituales, pero hay muchas ms caractersticas definidas en la especificacin Print Schema Public
Keywords que no se reflejan explcitamente en esas clases. (Para obtener una lista de las caractersticas
comunes, vea las propiedades de la clase PrintCapabilities.) Debe leer la especificacin para determinar si las
caractersticas especiales del dispositivo se definen en ella. Para cualquier caracterstica que est incluida en la
especificacin, existe una gran ventaja al usar el modelo de la especificacin: un modelo y terminologa
comunes permiten que los documentos XML de PrintTicket creados para un dispositivo puedan ser usados por
otro. (Quiz otro fabricante haya fabricado el segundo dispositivo y, por esta razn, puede que se haya
diseado despus de haberse creado originalmente el documento de PrintTicket.) Se pueden incrustar print
tickets (solicitudes de impresin) en los propios documentos; por tanto, cuando se distribuye el documento a
otras personas con impresoras distintas tambin se incluyen las opciones y el formato de impresin
establecidas por el autor.
En el resto de este artculo, las caractersticas explcitamente admitidas por las clases PrintTicket y
PrintCapabilities se denominar "caractersticas comunes". Las que se definen en la especificacin Print Schema
Public Keywords, pero que no admiten explcitamente las dos clases, "caractersticas definidas" y las que no se
definen en la especificacin de palabras clave pblicas, "nuevas caractersticas".
Asimismo, puede que el dispositivo tenga una caracterstica que tenga una coincidencia casi completa con una
caracterstica ya definida, pero con una o ms opciones adicionales no reconocidas en la especificacin Print
Schema Public Keywords. El Print Schema se puede extender para controlar dichas caractersticas con el fin de
aprovechar al mximo las definiciones existentes.
Crear los tipos que van a representar las caractersticas del dispositivo
Las propiedades de PrintTicket y PrintCapabilities, que corresponden a caractersticas de la impresora toman
tipos especiales, suelen ser enumeraciones que representan una caracterstica y sus valores posibles. La
enumeracin Collation es, por ejemplo, el tipo de la propiedad PrintTicket.Collation. Tambin es el tipo de los

MCT: Luis Dueas

Pag 288 de 473

Manual de Windows Presentation Foundation


miembros de la coleccin que constituye el tipo datos al que pertenece la propiedad PrintCapabilities.
CollationCapability.
Cree las clases para las caractersticas definidas por el dispositivo y para las caractersticas nuevas usando
estas clases existentes como modelos. En el ejemplo de cdigo siguiente, se declara una enumeracin
BrailleGrade3. Se construye basndose en el modelo de la enumeracin Collation ya que la traduccin de
Grade 3 es anloga a la intercalacin: cualquier impresora debe admitir por lo menos un tipo de salida
(intercalado o sin intercalar) porque son mutuamente excluyentes. Algunas impresoras admiten los dos. (Hay
pocas impresoras que slo admitan el resultado intercalado, pero se debe tener en cuenta esa posibilidad.) Por
tanto, puede haber impresoras que slo admitan salida de Braille sin traducir, slo salida traducida (no es
probable, pero s posible) o ambos. La enumeracin BrailleGrade3 incluye un valor Unknown por la misma
razn que lo hace Collation: para controlar situaciones en las que una aplicacin que genera un documento de
PrintTicket ha establecido la caracterstica de intercalacin en un valor que no reconoce la especificacin Print
Schema Public Keywords. Si su aplicacin crea un objeto PrintTicket mediante este tipo de documento, la
propiedad PrintTicket.Collation obtendr el valor Desconocido. (Los objetos PrintCapabilities nunca usan el
valor Unknown.)
public enum BrailleGrade3 { Translated, Untranslated, Unknown }
Las enumeraciones no son siempre la mejor forma de representar una caracterstica. A veces es mejor una
clase de tipo de referencia. Esto ocurre, sobre todo, cuando la caracterstica tiene una estructura anidada de
partes subordinadas que tienen su propio valor. Por ejemplo, la clase PageMediaSize tiene las propiedades
Height y Width. Adems, es el tipo de la propiedad PrintTicket.PageMediaSize. Tambin es el tipo de los
miembros

de

la

coleccin

que

define

el

tipo

de

datos

al

que

pertenece

la

propiedad

PrintCapabilities.PageMediaSizeCapability.
Extender las clases PrintCapabilities y PrintTicket
Aunque las clases PrintTicket y PrintCapabilities no se pueden heredar, puede extender el Print Schema para
reconocer las caractersticas definidas y las nuevas.
Extender la clase PrintTicket
Para utilizar la clase PrintTicket con el sistema extendido de caractersticas, siga los pasos siguientes. A
continuacin se describen algunos detalles.

Procedimiento de alto nivel para extender la clase PrintTicket


1.

Si el dispositivo tiene caractersticas nuevas, cree una nueva clase para encapsularlas. (Vea la seccin
Crear una clase NewFeaturesPrintTicket para obtener algunos detalles.)

2.

Si el dispositivo tiene definidas las caractersticas, cree una clase para encapsularlas. (Vea la seccin
Crear una clase DefinedFeaturesPrintTicket para obtener algunos detalles.)

3.

Cree una clase que represente una solicitud de impresin completa. Esta clase representar el papel
en la aplicacin que representara la clase PrintTicket si el dispositivo no tuviese caractersticas
definidas ni nuevas. (Vea la seccin Crear una clase WholePrintTicket para obtener algunos
detalles.)

Procedimiento para crear una clase NewFeaturesPrintTicket


1.

Si el dispositivo tiene caractersticas nuevas, declare una clase para encapsularlas. Se denominar
NewFeaturesPrintTicket.

2.

Proporcione a la nueva clase las propiedades necesarias para representar las nuevas caractersticas del
dispositivo. Cada propiedad suele pertenecer a uno de los tipos nuevos creados, normalmente una
enumeracin.

3.

Proporcione a la nueva clase una propiedad adicional, que se denominar PrivateNamespace, que
contendr una referencia al espacio de nombres XML privado que define las nuevas caractersticas del

MCT: Luis Dueas

Pag 289 de 473

Manual de Windows Presentation Foundation


dispositivo. Necesitar esta cadena al escribir el marcado XML en los documentos de PrintCapabilities y
PrintTicket.
4.

Proporcione a la clase dos constructores. Los constructores se deben basar en los dos constructores de
PrintTicket. Uno no toma parmetros, el otro, un objeto Stream con contenido XML. La secuencia XML
ser un documento de PrintTicket que defina caractersticas nuevas en lugar de comunes. Los
constructores con el parmetro deben producir excepciones basadas en el modelo del constructor de la
clase PrintTicket usada como parmetro en el constructor de la clase.

5.

Cree un mtodo GetXmlStream y un mtodo SaveTo para la clase. Utilice la palabra clave de acceso
"internal" para ambos. Su finalidad es que coincidan con la funcionalidad de los mtodos con igual
nombre definidos en la clase PrintTicket salvo que procesarn los documentos de PrintTicket que
definen las nuevas caractersticas en lugar de las comunes. Estos mtodos se deben asegurar de que
las secuencias generadas tengan una declaracin de espacio de nombres adicional (el valor de la
propiedad PrivateNamespace) en el elemento <PrintTicket > de apertura.

Procedimiento para crear una clase DefinedFeaturesPrintTicket

Si el dispositivo tiene definidas caractersticas nuevas, declare una clase para encapsularlas. Se
denominar

DefinedFeaturesPrintTicket.

Se

debe

construir

como

construy

NewFeaturesPrintTicket anteriormente, con las excepciones siguientes.

Las propiedades de la clase deben tener nombres que coincidan con el nombre de la
caracterstica correspondiente de la especificacin Print Schema Public Keywords.

No cree ninguna propiedad PrivateNamespace. El espacio de nombres de las caractersticas


definidas es el mismo para las caractersticas comunes.

Los mtodos GetXmlStream y SaveTo no generan secuencias con una declaracin de


espacio de nombres adicional.

Procedimiento para crear una clase WholePrintTicket


1.

Declare una clase que representar una solicitud de impresin completa y, de esta forma, representar
el papel en la aplicacin que habra representado la clase PrintTicket si el dispositivo no tuviese
caractersticas definidas ni nuevas. Se denominar WholePrintTicket.

2.

Proporcione una propiedad a WholePrintTicket, se denominar CommonFeatures, de tipo


PrintTicket.

3.

Proporcione a WholePrintTicket una o las dos propiedades adicionales siguientes, en funcin de si


tiene nuevas caractersticas, caractersticas definidas o ambos tipos.

4.

NewFeatures de tipo NewFeaturesPrintTicket.


DefinedFeatures de tipo DefinedFeaturesPrintTicket.

Proporcione a WholePrintTicket dos constructores. Uno que no tome ningn parmetro y otro que
tome un objeto Stream con contenido XML. El constructor con un parmetro har lo siguiente.

Pasar Stream al constructor del objeto PrintTicket al que hace referencia la propiedad
CommonFeatures. Ese constructor omitir cualquier marcado de Stream que no sea
pertinente para las caractersticas comunes.

Pasar Stream al constructor del objeto NewFeaturesPrintTicket al que hace referencia la


propiedad NewFeatures. Ese constructor omitir cualquier marcado de Stream que no sea
pertinente para las caractersticas nuevas. Si la nueva caracterstica contiene marcado,
Stream empezar por <psf:PrintTicket
xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework

MCT: Luis Dueas

Pag 290 de 473

Manual de Windows Presentation Foundation


" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="1" xmlns:espacio-de-nombresabreviatura=nombre-del-espacio-de-nombres-completo>, donde nombre-del-espacio-denombres-completo es la direccin URL usada para identificar las nuevas caractersticas y
espacio-de-nombres-abreviatura es su abreviatura.

Pasar Stream al constructor del objeto DefinedFeaturesPrintTicket al que hace referencia


la propiedad DefinedFeatures. Ese constructor omitir cualquier marcado de Stream que no
sea pertinente para las caractersticas definidas.

5.

Cree GetXmlStream y un mtodo SaveTo para la clase. Su finalidad es coincidir con la funcionalidad
de los mtodos de igual nombre definidos en la clase PrintTicket. Deben tener las caractersticas
siguientes.

Cada uno de estos mtodos debe llamar al mtodo de igual nombre sobre los objetos a los
que hacen referencia las propiedades CommonFeatures, DefinedFeatures (si la hay) y
NewFeatures (si la hay).

Cada mtodo debe concatenar las secuencias generadas por las llamadas descritas en el
punto anterior. Obviamente, todas las etiquetas finales </PrintTicket>, menos la ltima
deben eliminarse; del mismo

modo, deben borrarse todas

las

etiquetas

de

inicio

<PrintTicket > excepto la primera. A menos que no haya caractersticas nuevas, la etiqueta
de apertura debe tener la declaracin de espacio de nombres adicional. (Vea el paso 4b.) Por
este motivo, siempre debera hacer que la secuencia de caractersticas nuevas, si la hubiera,
fuese la primera en la secuencia concatenada ya que siempre tendr la declaracin del nuevo
espacio de nombres en su etiqueta inicial <PrintTicket >.

Para realizar la concatenacin, los mtodos GetXmlStream y SaveTo de WholePrintTicket


debern funcionar primero internamente en una secuencia temporal. Especficamente, se
lograr que las tres clases distintas, cada una serializa su contenido en la secuencia temporal,
realicen el trabajo de concatenacin (y de eliminacin: vea el siguiente punto) y, a
continuacin, generen el resultado de concatenacin en la secuencia de salida final.

Despus de la concatenacin, los mtodos GetXmlStream y SaveTo de WholePrintTicket


tambin debern quitar las copias duplicadas y triplicadas de los elementos de la secuencia
antes de devolver la secuencia final. Estas entradas duplicadas y triplicadas estarn en la
secuencia ya que cada uno de estos tres objetos, PrintTicket, NewFeaturesPrintTicket y
DefinedFeaturesPrintTicket, conservan todo el documento original de PrintTicket que se
us para crearlos, aunque cada uno de ellos exponga slo un subconjunto del documento en
sus propiedades.

Si el constructor WholePrintTicket(Stream), vea ms arriba, dividi la secuencia de


entrada en tres partes creando secuencias independientes para los elementos de marcado
definidos, nuevos y comunes, podra evitar el paso de eliminacin descrito en el punto
anterior. Despus, se pasa cada una de estas secuencias menores al constructor
correspondiente de las tres propiedades de WholePrintTicket. Esta tcnica exige que
agregue una etiqueta de cierre </PrintTicket> a cada una de las tres secuencias. A la
secuencia de las propiedades CommonFeatures y DefinedFeatures, agrega una etiqueta
de apertura como sta: <psf:PrintTicket
xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework
" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="1">. La etiqueta de apertura
del constructor de la propiedad NewFeatures agregara un espacio de nombres privado
adicional como ste: <psf:PrintTicket

MCT: Luis Dueas

Pag 291 de 473

Manual de Windows Presentation Foundation


xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework
" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="1" xmlns:espacio-de-nombresabreviatura=nombre-del-espacio-de-nombres-completo>, donde nombre-del-espacio-denombres-completo es la direccin URL usada para identificar el esquema que define las
nuevas caractersticas y espacio-de-nombres-abreviatura es su abreviatura.)
En el ejemplo de cdigo siguiente, se muestra parte del resultado de este procedimiento cuando se aplica al
ejemplo de impresora Braille. (Por razones de espacio y simplicidad, se ha omitido gran parte de l.) Observe
que la propiedad BrailleGrade3 es un tipo de Nullable<(Of <(T>)>). Este ejemplo sigue el modelo de las
propiedades de PrintTicket como Collation. En el ejemplo, se supone que hay caractersticas nuevas y definidas,
aunque no se especifique ninguna caracterstica definida concreta.
class NewFeaturesPrintTicket
{
public NewFeaturesPrintTicket()
{
// Optionally, initialize fields. For example:
privateNamespace = "http://www.AjaxEmbossers.com/schemas/deluxemodels/v.1.0";
}
public NewFeaturesPrintTicket(Stream printTicketDocument)
{
// Parse the stream and initialize fields representing features here.
// Optionally, initialize other fields. For example:
privateNamespace = "http://www.AjaxEmbossers.com/schemas/deluxemodels/v.1.0";
}
private Nullable<BrailleGrade3> brailleGrade3;
public Nullable<BrailleGrade3> BrailleGrade3
{
get { return brailleGrade3; }
set { brailleGrade3 = value;}
}
private String privateNamespace;
public String PrivateNamespace
{
get { return privateNamespace; }
set { privateNamespace = value; }
}
}
class DefinedFeaturesPrintTicket
{
// Details omitted. Use the NewFeaturesPrintTicket
// as a model, but leave out the privateNamespace field
// and property.
}
class WholePrintTicket
{
public WholePrintTicket()
{
commonFeatures = new PrintTicket();
newFeatures = new NewFeaturesPrintTicket();
definedFeatures = new DefinedFeaturesPrintTicket();
}
public WholePrintTicket(Stream wholePrintTicket)
{
// Method 1: Pass the stream to the three constructors.
// Be sure to reset the read-write position of the stream
// to the beginning of the stream after each call to a constructor.
// commonFeatures = new PrintTicket(wholePrintTicket);
// reset read-write head here
// newFeatures = new NewFeaturesPrintTicket(wholePrintTicket);

MCT: Luis Dueas

Pag 292 de 473

Manual de Windows Presentation Foundation


// reset read-write head here
// definedFeatures = new DefinedFeaturesPrintTicket(wholePrintTicket);
// reset read-write head here
// Method 2: Parse the stream and split it into three streams.
// Then pass them to the constructors of the three properties.
// commonFeatures = new PrintTicket(commonFeaturesPrintTicketDocument);
// newFeatures = new NewFeaturesPrintTicket(newFeaturesPrintTicketDocument);
// definedFeatures = new
DefinedFeaturesPrintTicket(definedFeaturesPrintTicketDocument);
}
private PrintTicket commonFeatures;
public PrintTicket CommonFeatures
{
get { return commonFeatures; }
set { commonFeatures = value;}
}
private PrintTicket newFeatures;
public PrintTicket NewFeatures
{
// Details omitted. See CommonFeatures above.}
private PrintTicket definedFeatures;
public PrintTicket DefinedFeatures
{
// Details omitted. See CommonFeatures above.}
}
Extender la clase PrintCapabilities
Para usar la clase PrintCapabilities con el dispositivo, debe extenderse de forma similar a como se extendi la
clase PrintTicket. Puesto que puede utilizar la extensin de PrintTicket como modelo, slo se incluye aqu una
breve informacin general.

Cree tres clases nuevas, una para encapsular las nuevas caractersticas (NewFeaturesPrint
Capabilities), otra para encapsular las definidas (DefinedFeaturesPrintCapabilities) y otra para
representar un documento de PrintCapabilities completo (WholePrintCapabilities).

Los tipos de las propiedades de las dos primeras clases nuevas suelen ser ReadOnlyCollection<(Of
<(T>)>). Utilice las propiedades existentes de PrintCapabilities, como CollationCapability, como
modelos.

Siguiendo tambin el modelo de la clase PrintCapabilities existente, los nombres de propiedad deben
tener "Capability" al fin; por ejemplo, BrailleGrade3Capability.

La clase NewFeaturesPrintCapabilities tendr una propiedad PrivateNamespace idntica a la que


cre para NewFeaturesPrintTicket.

Ninguna de las clases tendr ningn mtodo que no sean los que heredan de la clase Object.

Cada una de las clases tendr un constructor nico que toma un parmetro Stream. Stream ser un
documento de PrintCapabilities.

El constructor DefinedFeaturesPrintCapabilities debe validar que el objeto Stream que se le pas


se ajuste al Print Schema Framework antes de que use Stream para inicializar propiedades. (Una
forma fcil de hacerlo es pasar primero Stream al constructor PrintCapabilities. Si el ltimo no produce
ninguna excepcin, la secuencia es vlida.)

El constructor NewFeaturesPrintCapabilities debe validar que el objeto Stream que se le pas se


ajuste al espacio de nombres privado antes de que use Stream para inicializar propiedades.

Los

dos

constructores

DefinedFeaturesPrintCapabilities

NewFeaturesPrintCapabilities

producirn excepciones en el modelo del constructor PrintCapabilities existente.

MCT: Luis Dueas

Pag 293 de 473

Manual de Windows Presentation Foundation

La clase WholePrintCapabilities tendr las propiedades CommonFeatures, DefinedFeatures y


NewFeatures.

Los

tipos

de

estas

propiedades

sern

PrintCapabilities,

DefinedFeaturesPrintCapabilities y NewFeaturesPrintCapabilties, respectivamente. Cada uno se


crear basndose en el modelo de las propiedades de WholePrintTicket.

El constructor de la clase WholePrintCapabilities tomar un solo parmetro Stream que representa


un documento de PrintCapabilities completo. El constructor debe pasar la entrada Stream completa a
los tres constructores de sus propiedades miembro. Puesto que la clase WholePrintCapabilities no
tiene ningn mtodo que exporte su configuracin en formato XML (como los mtodos GetXmlStream
y SaveTo de WholePrintTicket), no tiene importancia el hecho de que haya marcado duplicado o
triplicado en las tres propiedades.

Leer y escribir secuencias XML de PrintCapabilities y PrintTicket


El trabajo principal de los constructores y los mtodos de las clases PrintTicket y PrintCapabilities existentes y
de las clases nuevas creadas anteriormente en la seccin Extender las clases PrintCapabilities y PrintTicket es
leer, analizar, escribir y, a veces, validar los objetos Stream cuyo contenido sea un documento de PrintTicket o
un documento de PrintCapabilities. Debe estar familiarizado con E/S de archivos bsica, as como con los
mtodos de esas clases. Puesto que el contenido de estas secuencias es XML, tambin se debe familiarizar con
la compatibilidad de lectura y escritura de XML que se describe en Opciones de procesamiento XML en .NET
Framework. Si la aplicacin funciona con documentos de XML Paper Specification (XPS), hay API en los espacios
de

nombres

System.Windows.Xps,

System.Windows.Xps.Packaging

System.Windows.Xps.Serialization

diseados para leer y escribir en documentos de la XPS, incluidos leer y escribir PrintTicket. Vea tambin
PackageRelationship para obtener informacin sobre cmo agregar una relacin a PrintTicket en un paquete.
Se pueden encontrar ejemplos completos de un documento de PrintTicket y un documento de PrintCapabilities
en PrintTicket Example y PrintCapabilities Document Example.
A continuacin, se muestra un ejemplo sencillo de un documento de PrintCapabilities en el que se han pasado
por alto todas las caractersticas del dispositivo menos una. La caracterstica mostrada es DocumentCollate que
es la caracterstica representada por las propiedades PrintCapabilities.CollationCapability y PrintTicket.Collation.
<psf:PrintCapabilities
xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="1">
<!-- other features omitted -->
<psf:Feature name="psk:DocumentCollate">
<psf:Property name="psf:SelectionType">
<psf:Value xsi:type="xsd:QName">psk:PickOne</psf:Value>
</psf:Property>
<psf:Property name="psk:DisplayName">
<psf:Value xsi:type="xsd:string">Collate Copies</psf:Value>
</psf:Property>
<psf:Option name="psk:Collated" constrained="psk:None">
<psf:Property name="psk:DisplayName">
<psf:Value xsi:type="xsd:string">Yes</psf:Value>
</psf:Property>
</psf:Option>
<psf:Option name="psk:Uncollated" constrained="psk:None">
<psf:Property name="psk:DisplayName">
<psf:Value xsi:type="xsd:string">No</psf:Value>
</psf:Property>
</psf:Option>
</psf:Feature>
<!-- other features omitted -->
</PrintCapabilities>
Observe que se muestra una lista de todas las opciones posibles de la caracterstica (tanto intercaladas como
sin intercalar) y se identifica el estado de cada una (es decir, si se restringe o no). En un documento de

MCT: Luis Dueas

Pag 294 de 473

Manual de Windows Presentation Foundation


PrintCapabilities completo, se identifica el estado de cada opcin de cada caracterstica; por tanto, el
documento es un tipo de captura de la configuracin del dispositivo.
A continuacin, se muestra un ejemplo sencillo de un documento de PrintTicket que contiene una nica
caracterstica.
<psf:PrintTicket
xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="1">
<!-- other features omitted -->
<psf:Feature name="psk:DocumentCollate">
<psf:Option name="psk:Collated" />
</psf:Feature>
<!-- other features omitted -->
</PrintTicket>
Observe que la estructura puede ser algo ms fcil porque no es necesario que un documento de PrintTicket
especifique explcitamente el estado de cada opcin. Slo necesita identificar la opcin solicitada.
La sintaxis y la estructura de la caracterstica se definen en DocumentCollate mediante el Print Schema
Framework y las Print Schema Public Keywords. Al trabajar con nuevas caractersticas, estar trabajando con
las caractersticas definidas en un espacio de nombres XML privado definido en un esquema suplementario. El
fabricante del dispositivo suele proporcionar esta informacin, mediante una asociacin comercial, o lo hace
una organizacin de normalizacin no lucrativa; pero podra ser el usuario quin lo cree. Use Print Schema
Framework y Print Schema Public Keywords existentes para obtener la sintaxis necesaria y buscar modelos en
los que pueda basar las definiciones. La propia Microsoft cre un nuevo espacio de nombres para extender el
sistema de PrintCapabilities y PrintTicket a su nuevo controlador del Escritor de documentos XPS de Microsoft
(MXDW).
Si suponemos que una compaa ficticia de Ajax Embosser defini la caracterstica de traduccin de
BrailleGrade3 de forma que sea similar a como Microsoft defini la caracterstica de intercalacin de
documentos, podemos ver que las entradas de los documentos de PrintCapabilities y PrintTicket para la
caracterstica tendran el siguiente aspecto. Observe que el espacio de nombres en el que se define la
caracterstica se debe declarar en las etiquetas de apertura <PrintCapabilities > y <PrintTicket >.
Marcado del documento de PrintCapabilities
<psf:PrintCapabilities
xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ajax="http://www.AjaxEmbossers.com/schemas/deluxemodels" version="1">
<!-- other features omitted -->
<psf:Feature name="ajax:BrailleGrade3Translation">
<psf:Property name="psf:SelectionType">
<psf:Value xsi:type="xsd:QName">psk:PickOne</psf:Value>
</psf:Property>
<psf:Property name="psk:DisplayName">
<psf:Value xsi:type="xsd:string">Braille3 translation</psf:Value>
</psf:Property>
<psf:Option name="ajax:Translated" constrained="psk:None">
<psf:Property name="psk:DisplayName">
<psf:Value xsi:type="xsd:string">Yes</psf:Value>
</psf:Property>
</psf:Option>
<psf:Option name="ajax:Untranslated" constrained="psk:None">
<psf:Property name="psk:DisplayName">
<psf:Value xsi:type="xsd:string">No</psf:Value>
</psf:Property>
</psf:Option>

MCT: Luis Dueas

Pag 295 de 473

Manual de Windows Presentation Foundation


</psf:Feature>
<!-- other features omitted -->
</PrintCapabilities>
Marcado del documento de PrintTicket
<psf:PrintTicket
xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ajax="http://www.AjaxEmbossers.com/schemas/deluxemodels" version="1">
<!-- other features omitted -->
<psf:Feature name="ajax:BrailleGrade3Translation">
<psf:Option name="ajax:Translated" />
</psf:Feature>
<!-- other features omitted -->
</PrintTicket>
Sobrecargar y extender las propiedades y los mtodos
Cualquier mtodo de cualquier clase que tenga como parmetro un objeto de la clase PrintTicket o devuelva un
objeto PrintTicket y que desee usar con el sistema de caractersticas extendidas, se deber reemplazar
mediante un mtodo que use WholePrintTicket en lugar de PrintTicket. De igual forma, cualquier propiedad
de tipo PrintTicket que desee utilizar se deber reemplazar por propiedades de tipo WholePrintTicket. Los
mismos puntos se aplican, mutatis mutandis, a PrintCapabilities y WholePrintCapabilities.
Cuando la clase que hospeda el mtodo o la propiedad se pueda heredar, puede derivar una clase y
sobrecargar el mtodo o la propiedad que va a usar WholePrintTicket en lugar de PrintTicket.
Si la clase que hospeda dicho mtodo no se puede heredar, deber extenderla de la forma en la que se
extendieron PrintTicket y PrintCapabilities anteriormente: cree una clase que tenga la clase inicial como
miembro. A continuacin, asigne a los mtodos y las propiedades de la clase exterior los mismos nombres que
los de los mtodos correspondientes que tienen (o devuelven) los parmetros PrintTicket o PrintCapabilities.
Normalmente, la clase exterior debe coincidir con la funcionalidad de la clase interior del mismo nombre, pero
utilizar los parmetros WholePrintTicket o WholePrintCapabilities en lugar de los parmetros PrintTicket o
PrintCapabilities. Adems, para las propiedades de la clase interior de tipo PrintTicket o PrintCapabilities, cree
una propiedad

coincidente

del mismo nombre en la clase

exterior

que usa

WholePrintTicket o

WholePrintCapabilities.
Probablemente,

los

mtodos

ms

importantes

que

deber

sobrecargar

son

las

dos

versiones

de

MergeAndValidatePrintTicket. La clase BrailleEmbosser (vea la seccin Derivar una nueva clase mediante
herencia) necesitar reemplazos que usen los parmetros WholePrintTicket y que agreguen la lgica para
validar las solicitudes con nuevas caractersticas en el esquema del espacio de nombres privado. Puede
sobrecargar los mtodos existentes o crear nuevos mtodos denominados MergeAndValidateWholePrint
Ticket.
Dos mtodos que devuelven PrintTicket son ConvertDevModeToPrintTicket y Clone. En .NET Framework hay
otros 15 mtodos, sin contar las sobrecargas, que toman un parmetro PrintTicket y 19 propiedades de tipo
PrintTicket. Probablemente, cualquier aplicacin especificada usar slo unos pocos; por tanto, la carga de
trabajo no ser tan grande como podran sugerir esos nmeros. Afortunadamente, el nico mtodo que
devuelve un objeto PrintCapabilities es GetPrintCapabilities y no existen propiedades con tipo PrintCapabilities.
Expandir las caractersticas definidas por el esquema
Las caractersticas totalmente nuevas no son la nica situacin que podra requerir una extensin de Print
Schema. Adems, es posible que un dispositivo proporcione opciones nuevas y sin definir a una caracterstica
conocida y definida. Puesto que se debe usar un espacio de nombres privado para las nuevas opciones sin
definir de la misma forma que se debe utilizar para una caracterstica totalmente nueva, es probable que sea
ms fcil tratar tales caractersticas extendidas como lo hara con caractersticas totalmente nuevas (pero use
los nombres definidos por el esquema para la caracterstica y los de sus opciones que ya estn definidos):

MCT: Luis Dueas

Pag 296 de 473

Manual de Windows Presentation Foundation


hospdelos en NewFeaturesPrintTicket y NewFeaturesPrintCapabilities. La informacin necesaria para
describir todos los detalles necesarios para implementar este tipo de extensin de esquema, sobrepasa los
lmites de este artculo, pero tenga en cuenta que el trabajo de concatenacin realizado por los mtodos
WholePrintTicket.GetXmlStream y WholePrintTicket.SaveAs tendra que convertirse en algo ms
complicado.
Extender el espacio de nombres System.Printing.IndexedProperties
Si desea aprovechar las API del espacio de nombres System.Printing.IndexedProperties, quiz deba derivar una
nueva clase de PrintProperty. Debera hacerlo si una propiedad que ha creado para la clase derivada de
PrintQueue tiene un tipo que no est representado por ninguna de las clases existentes de System.Printing.
IndexedProperties.
Esto no se aplica necesariamente a nuestro ejemplo anterior, ya que la nica propiedad que agregamos a
nuestra clase BrailleEmbosser es IsBraile3Enabled, que es Boolean. Ya hay una clase System.Printing.
IndexedProperties.PrintBooleanProperty.

6.6.2.7. Cmo: Obtener Propiedades de un Objeto de Sistema de Impresin sin


Reflexin
Utilizar Reflexin para detallar las propiedades (y los tipos de esas propiedades) de un objeto puede deteriorar
el rendimiento de la aplicacin. El espacio de nombres System.Printing.IndexedProperties proporciona un medio
para obtener esta informacin sin utilizar Reflexin.
Ejemplo
Los pasos para hacerlo son los siguientes.
1.

Cree una instancia del tipo. En el ejemplo siguiente, el tipo es el tipo PrintQueue que se distribuye con
Microsoft .NET Framework, pero un cdigo prcticamente idntico debera funcionar para los tipos que
derive de PrintSystemObject.

2.

Cree PrintPropertyDictionary a partir de la propiedad PropertiesCollection del tipo. La propiedad Value


de cada entrada de este diccionario es un objeto de uno de los tipos derivados de PrintProperty.

3.

Enumere los miembros del diccionario. Para cada uno de ellos, haga lo siguiente.

4.

Convierta el tipo del valor de cada entrada a PrintProperty y utilcelo para crear un objeto
PrintProperty.

5.

Obtenga el tipo de la propiedad Value de cada uno de los objetos PrintProperty.

// Enumerate the properties, and their types, of a queue without using Reflection
LocalPrintServer localPrintServer = new LocalPrintServer();
PrintQueue defaultPrintQueue = LocalPrintServer.GetDefaultPrintQueue();
PrintPropertyDictionary printQueueProperties = defaultPrintQueue.PropertiesCollection;
Console.WriteLine("These are the properties, and their types, of {0}, a {1}",
defaultPrintQueue.Name, defaultPrintQueue.GetType().ToString() +"\n");
foreach (DictionaryEntry entry in printQueueProperties)
{
PrintProperty property = (PrintProperty)entry.Value;
if (property.Value != null)
{
Console.WriteLine(property.Name + "\t(Type: {0})",
property.Value.GetType().ToString());
}
}
Console.WriteLine("\n\nPress Return to continue...");
Console.ReadLine();

MCT: Luis Dueas

Pag 297 de 473

Manual de Windows Presentation Foundation

6.6.2.8. Cmo: Imprimir mediante Programacin Archivos XPS


Puede utilizar una sobrecarga del mtodo AddJob para imprimir archivos XML Paper Specification (XPS) sin abrir
un control PrintDialog o, en principio, ninguna interfaz de usuario (UI) en absoluto.
Tambin puede imprimir archivos XML Paper Specification (XPS) usando los diversos mtodos Write y
WriteAsync de la clase XpsDocumentWriter.
Otra manera de imprimir XML Paper Specification (XPS) es utilizar los mtodos PrintDocument o PrintVisual del
control PrintDialog.
Ejemplo
Los pasos principales para utilizar el mtodo de tres parmetros AddJob(String, String, Boolean) son los
siguientes. El ejemplo siguiente proporciona informacin detallada.
1.

Determine si la impresora es una impresora XPSDrv.

2.

Si la impresora no es una impresora XPSDrv, establezca el estado de subprocesamiento en un


subproceso nico.

3.

Cree una instancia de un servidor de impresin e imprima el objeto de cola.

4.

Llame al mtodo especificando un nombre de trabajo, el archivo que se va a imprimir y un marcador


Boolean que indique si la impresora es una impresora XPSDrv o no.

El ejemplo siguiente muestra cmo imprimir por lotes todos los archivos XPS de un directorio. Aunque la
aplicacin pide al usuario que especifique el directorio, el mtodo de tres parmetros AddJob(String, String,
Boolean) no requiere una interfaz de usuario (UI). Se puede utilizar en cualquier ruta de acceso de cdigo
donde tenga un nombre de archivo XPS y una ruta de acceso que puede pasarle.
La sobrecarga AddJob(String, String, Boolean) de tres parmetros de AddJob se debe ejecutar en un
subprocesamiento controlado simple cada vez que el parmetro Boolean sea false, que debe ser cuando se
utilice una impresora que no sea XPSDrv. Sin embargo, el estado de subprocesamiento predeterminado para
Microsoft .NET es de varios subprocesos. Esta opcin predeterminada se debe invertir, dado que el ejemplo
asume que se trata de una impresora que no es XPSDrv.
Hay

dos

maneras

de

cambiar

el

valor

predeterminado.

Una

consiste

en

agregar

simplemente

STAThreadAttribute (es decir, "[System.STAThreadAttribute()]") inmediatamente encima de la primera lnea del


mtodo Main de la aplicacin (habitualmente, "static void Main(string[] args)"). Sin embargo, muchas
aplicaciones requieren que el mtodo Main tenga un estado subprocesamiento mltiple, as que hay un segundo
mtodo: coloque la llamada a AddJob(String, String, Boolean) en un subproceso independiente cuyo estado
subprocesamiento est establecido en STA con SetApartmentState. En el ejemplo siguiente se utiliza esta
segunda tcnica.
Consecuentemente, el ejemplo comienza creando una instancia de un objeto Thread y pasndole un mtodo
PrintXPS como parmetro ThreadStart. (El mtodo PrintXPS se define ms adelante en el ejemplo.) A
continuacin, el subproceso se establece en un subprocesamiento controlado simple. El nico cdigo restante
del mtodo Main inicia el nuevo subproceso.
La parte interesante del ejemplo est en el mtodo staticBatchXPSPrinter.PrintXPS. Despus de crear un
servidor y una cola de impresin, el mtodo pide al usuario un directorio que contenga los archivos XPS.
Despus de validar la existencia del directorio y la presencia en l de archivos *.xps, el mtodo agrega cada
uno de los archivos a la cola de impresin. En el ejemplo se supone que la impresora no es XPSDrv, por lo que
estamos pasando false al ltimo parmetro del mtodo AddJob(String, String, Boolean). Por esta razn, el
mtodo validar el marcado XPS del archivo antes de intentar convertirlo al lenguaje de descripcin de pginas

MCT: Luis Dueas

Pag 298 de 473

Manual de Windows Presentation Foundation


de la impresora. Si se produce un error de validacin, se genera una excepcin. El cdigo de ejemplo detectar
la excepcin, se la notificar al usuario y, a continuacin, continuar procesando el archivo XPS siguiente.
class Program
{
[System.MTAThreadAttribute()] // Added for clarity, but this line is redundant because
MTA is the default.
static void Main(string[] args)
{
// Create the secondary thread and pass the printing method for
// the constructor's ThreadStart delegate parameter. The BatchXPSPrinter
// class is defined below.
Thread printingThread = new Thread(BatchXPSPrinter.PrintXPS);
// Set the thread that will use PrintQueue.AddJob to single threading.
printingThread.SetApartmentState(ApartmentState.STA);
// Start the printing thread. The method passed to the Thread
// constructor will execute.
printingThread.Start();
}//end Main
}//end Program class
public class BatchXPSPrinter
{
public static void PrintXPS()
{
// Create print server and print queue.
LocalPrintServer localPrintServer = new LocalPrintServer();
PrintQueue defaultPrintQueue = LocalPrintServer.GetDefaultPrintQueue();
// Prompt user to identify the directory, and then create the directory object.
Console.Write("Enter the directory containing the XPS files: ");
String directoryPath = Console.ReadLine();
DirectoryInfo dir = new DirectoryInfo(directoryPath);
// If the user mistyped, end the thread and return to the Main thread.
if (!dir.Exists)
{
Console.WriteLine("There is no such directory.");
}
else
{
// If there are no XPS files in the directory, end the thread
// and return to the Main thread.
if (dir.GetFiles("*.xps").Length == 0)
{
Console.WriteLine("There are no XPS files in the directory.");
}
else
{
Console.WriteLine("\nJobs will now be added to the print queue.");
Console.WriteLine("If the queue is not paused and the printer is working,
jobs will begin printing.");
// Batch process all XPS files in the directory.
foreach (FileInfo f in dir.GetFiles("*.xps"))
{
String nextFile = directoryPath + "\\" + f.Name;
Console.WriteLine("Adding {0} to queue.", nextFile);
try
{
// Print the Xps file while providing XPS validation and progress notifications.
PrintSystemJobInfo xpsPrintJob = defaultPrintQueue.AddJob(f.Name, nextFile, false);
}
catch (PrintJobException e)
{
Console.WriteLine("\n\t{0} could not be added to the print queue.", f.Name);
if (e.InnerException.Message == "File contains corrupted data.")

MCT: Luis Dueas

Pag 299 de 473

Manual de Windows Presentation Foundation


{
Console.WriteLine("\tIt is not a valid XPS file. Use the isXPS Conformance
Tool to debug it.");
}
Console.WriteLine("\tContinuing with next XPS file.\n");
}
}// end for each XPS file
}//end if there are no XPS files in the directory
}//end if the directory does not exist
Console.WriteLine("Press Enter to end program.");
Console.ReadLine();
}// end PrintXPS method
}// end BatchXPSPrinter class
Si est utilizando una impresora XPSDrv, puede establecer el parmetro final en true. En ese caso, dado que
XPS es el lenguaje de descripcin de pginas de la impresora, el mtodo enviar el archivo a la impresora sin
validarlo ni convertirlo a otro lenguaje de descripcin de pginas. Si no sabe con seguridad en tiempo de diseo
si la aplicacin utilizar una impresora XPSDrv, puede modificar la aplicacin para que lea la propiedad
IsXpsDevice y se bifurque en funcin de lo que encuentre.
Dado que inicialmente habr pocas impresoras XPSDrv disponibles inmediatamente despus del lanzamiento de
Windows Vista y Microsoft .NET Framework, quiz deba hacer que las impresoras que no sean XPSDrv
aparezcan como XPSDrv. Para ello, agregue Pipelineconfig.xml a la lista de archivos de la siguiente clave del
Registro del equipo donde se ejecute la aplicacin:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Environments\Windows NT
x86\Drivers\Version-3\<PseudoXPSPrinter>\DependentFiles
donde <PseudoXPSPrinter> es cualquier cola de impresin. A continuacin se debe reiniciar el equipo.
Este camuflaje le permitir pasar true como ltimo parmetro final de AddJob(String, String, Boolean) sin
producir una excepcin, pero dado que <PseudoXPSPrinter> no es realmente una impresora XPSDrv, slo se
imprimir basura.
Nota

Para simplificar, el ejemplo anterior utiliza la presencia de una extensin *.xps como prueba de que un

archivo es XPS. Sin embargo, los archivos XPS no tienen necesariamente esta extensin. Herramienta isXPS
Conformance es un medio de probar la validez XPS de un archivo.

6.6.2.9. Cmo: Supervisar de forma Remota el Estado de las Impresoras


En cualquier momento determinado, en las compaas medianas y grandes puede haber varias impresoras que
no funcionen por un atasco de papel, por falta de papel o por cualquier otra situacin problemtica. El conjunto
enriquecido de propiedades de impresora expuesto en las API de Microsoft .NET Framework proporciona un
medio para realizar un repaso rpido de los estados de las impresoras.
Ejemplo
Los pasos principales para crear este tipo de utilidad son los siguientes.
1.

Obtenga una lista de todos los servidores de impresin.

2.

Recorra en bucle los servidores para consultar sus colas de impresin.

3.

Dentro de cada paso del bucle de servidor, recorra en bucle todas las colas del servidor y lea cada
propiedad que pueda indicar que la cola no est funcionando actualmente.

El cdigo siguiente consiste en una serie de fragmentos de cdigo.


Para mayor simplicidad, en este ejemplo se da por hecho que existe una lista de servidores de impresin
delimitada por CRLF. La variable fileOfPrintServers es un objeto StreamReader para este archivo. Puesto que

MCT: Luis Dueas

Pag 300 de 473

Manual de Windows Presentation Foundation


cada nombre de servidor figura en su propia lnea, cualquier llamada de ReadLine obtiene el nombre del
servidor siguiente y mueve el cursor de StreamReader al principio de la lnea siguiente.
Dentro del bucle exterior, el cdigo crea un objeto PrintServer para el ltimo servidor de impresin y especifica
que la aplicacin debe tener derechos administrativos en el servidor.
Nota:
Si hay muchos servidores, puede mejorar el rendimiento utilizando los constructores PrintServer(String,
array<String>[]()[], PrintSystemDesiredAccess), que inicializan nicamente las propiedades que se van a
necesitar.
A continuacin, en el ejemplo se utiliza el mtodo GetPrintQueues para crear una coleccin de todas las colas
del servidor y comienza a recorrerlas en bucle. Este bucle interno contiene una estructura de bifurcacin que
corresponde a las dos maneras de comprobar el estado de una impresora:

Puede leer los marcadores de la propiedad QueueStatus, que es del tipo PrintQueueStatus.
Puede leer cada propiedad pertinente, como IsOutOfPaper y IsPaperJammed.

En este ejemplo se muestran ambos mtodos, de modo que previamente se ha preguntado al usuario qu
mtodo desea utilizar y este ha respondido con "y" (S) si desea utilizar los marcadores de la propiedad
QueueStatus. Consulte ms adelante los detalles de los dos mtodos.
Por ltimo, se presentan los resultados al usuario.
// Survey queue status for every queue on every print server
String line;
String statusReport = "\n\nAny problem states are indicated below:\n\n";
while ((line = fileOfPrintServers.ReadLine()) != null)
{
PrintServer myPS = new PrintServer(line, PrintSystemDesiredAccess.AdministrateServer);
PrintQueueCollection myPrintQueues = myPS.GetPrintQueues();
statusReport = statusReport + "\n" + line;
foreach (PrintQueue pq in myPrintQueues)
{
pq.Refresh();
statusReport = statusReport + "\n\t" + pq.Name + ":";
if (useAttributesResponse == "y")
{
TroubleSpotter.SpotTroubleUsingQueueAttributes(ref statusReport, pq);
// TroubleSpotter class is defined in the complete example.
}
else
{
TroubleSpotter.SpotTroubleUsingProperties(ref statusReport, pq);
}
}// end for each print queue
}// end while list of print servers is not yet exhausted
fileOfPrintServers.Close();
Console.WriteLine(statusReport);
Console.WriteLine("\nPress Return to continue.");
Console.ReadLine();
Para comprobar estado de la impresora mediante los marcadores de la propiedad QueueStatus, se comprueba
cada marcador pertinente para ver si est establecido. La manera estndar de comprobar si un bit est
establecido en un conjunto de indicadores de bits, es realizar la operacin de AND lgico con el conjunto de
marcadores como uno de los operandos y con el propio marcador como el otro operando. Puesto que el
marcador nicamente tiene un bit establecido, el resultado de la operacin de AND lgico es que, a lo sumo,
ese mismo bit est establecido. Para averiguar si lo est o no, basta con comparar el resultado de la operacin
de AND lgico con el propio marcador.

MCT: Luis Dueas

Pag 301 de 473

Manual de Windows Presentation Foundation


Para cada atributo cuyo bit est establecido, el cdigo agrega un aviso al informe final que se presentar al
usuario. (El mtodo ReportAvailabilityAtThisTime al que se llama al final del cdigo se aborda ms
adelante.)
// Check for possible trouble states of a printer using the flags of the QueueStatus
property
internal static void SpotTroubleUsingQueueAttributes(ref String statusReport, PrintQueue
pq)
{
if ((pq.QueueStatus & PrintQueueStatus.PaperProblem) == PrintQueueStatus.PaperProblem)
{
statusReport = statusReport + "Has a paper problem. ";
}
if ((pq.QueueStatus & PrintQueueStatus.NoToner) == PrintQueueStatus.NoToner)
{
statusReport = statusReport + "Is out of toner. ";
}
if ((pq.QueueStatus & PrintQueueStatus.DoorOpen) == PrintQueueStatus.DoorOpen)
{
statusReport = statusReport + "Has an open door. ";
}
if ((pq.QueueStatus & PrintQueueStatus.Error) == PrintQueueStatus.Error)
{
statusReport = statusReport + "Is in an error state. ";
}
if ((pq.QueueStatus & PrintQueueStatus.NotAvailable) == PrintQueueStatus.NotAvailable)
{
statusReport = statusReport + "Is not available. ";
}
if ((pq.QueueStatus & PrintQueueStatus.Offline) == PrintQueueStatus.Offline)
{
statusReport = statusReport + "Is off line. ";
}
if ((pq.QueueStatus & PrintQueueStatus.OutOfMemory) == PrintQueueStatus.OutOfMemory)
{
statusReport = statusReport + "Is out of memory. ";
}
if ((pq.QueueStatus & PrintQueueStatus.PaperOut) == PrintQueueStatus.PaperOut)
{
statusReport = statusReport + "Is out of paper. ";
}
if ((pq.QueueStatus & PrintQueueStatus.OutputBinFull) == PrintQueueStatus.OutputBinFull)
{
statusReport = statusReport + "Has a full output bin. ";
}
if ((pq.QueueStatus & PrintQueueStatus.PaperJam) == PrintQueueStatus.PaperJam)
{
statusReport = statusReport + "Has a paper jam. ";
}
if ((pq.QueueStatus & PrintQueueStatus.Paused) == PrintQueueStatus.Paused)
{
statusReport = statusReport + "Is paused. ";
}
if ((pq.QueueStatus & PrintQueueStatus.TonerLow) == PrintQueueStatus.TonerLow)
{
statusReport = statusReport + "Is low on toner. ";
}
if ((pq.QueueStatus&PrintQueueStatus.UserIntervention)==PrintQueueStatus.UserIntervention)
{
statusReport = statusReport + "Needs user intervention. ";
}
// Check if queue is even available at this time of day
// The method below is defined in the complete example.

MCT: Luis Dueas

Pag 302 de 473

Manual de Windows Presentation Foundation


ReportAvailabilityAtThisTime(ref statusReport, pq);
}
Para comprobar estado de la impresora mediante cada propiedad, basta con leer cada una de ellas y agregar
una nota al informe final que se presentar al usuario en caso de que la propiedad sea true. (El mtodo
ReportAvailabilityAtThisTime al que se llama al final del cdigo se aborda ms adelante.)
// Check for possible trouble states of a printer using its properties
internal static void SpotTroubleUsingProperties(ref String statusReport, PrintQueue pq)
{
if (pq.HasPaperProblem)
{
statusReport = statusReport + "Has a paper problem. ";
}
if (!(pq.HasToner))
{
statusReport = statusReport + "Is out of toner. ";
}
if (pq.IsDoorOpened)
{
statusReport = statusReport + "Has an open door. ";
}
if (pq.IsInError)
{
statusReport = statusReport + "Is in an error state. ";
}
if (pq.IsNotAvailable)
{
statusReport = statusReport + "Is not available. ";
}
if (pq.IsOffline)
{
statusReport = statusReport + "Is off line. ";
}
if (pq.IsOutOfMemory)
{
statusReport = statusReport + "Is out of memory. ";
}
if (pq.IsOutOfPaper)
{
statusReport = statusReport + "Is out of paper. ";
}
if (pq.IsOutputBinFull)
{
statusReport = statusReport + "Has a full output bin. ";
}
if (pq.IsPaperJammed)
{
statusReport = statusReport + "Has a paper jam. ";
}
if (pq.IsPaused)
{
statusReport = statusReport + "Is paused. ";
}
if (pq.IsTonerLow)
{
statusReport = statusReport + "Is low on toner. ";
}
if (pq.NeedUserIntervention)
{
statusReport = statusReport + "Needs user intervention. ";
}
// Check if queue is even available at this time of day
// The following method is defined in the complete example.

MCT: Luis Dueas

Pag 303 de 473

Manual de Windows Presentation Foundation


ReportAvailabilityAtThisTime(ref statusReport, pq);
}//end SpotTroubleUsingProperties
Se cre el mtodo ReportAvailabilityAtThisTime por si se necesita determinar si la cola est disponible a
esta hora del da.
El mtodo no har nada si las propiedades StartTimeOfDay y UntilTimeOfDay son iguales, porque en ese caso
significa que la impresora est disponible en todo momento. Si son diferentes, el mtodo obtiene la hora actual
que, a continuacin, se tiene que convertir a minutos totales transcurridos desde la pasada medianoche; esto
es porque las propiedades StartTimeOfDay y UntilTimeOfDay son objetos Int32 que representan minutos
transcurridos desde la medianoche, no objetos DateTime. Por ltimo, el mtodo comprueba si la hora actual se
encuentra entre los momentos de inicio y "hasta".
private static void ReportAvailabilityAtThisTime(ref String statusReport, PrintQueue pq)
{
if (pq.StartTimeOfDay != pq.UntilTimeOfDay)
//If the printer is not available 24 hours a day
{
DateTime utcNow = DateTime.UtcNow;
Int32 utcNowAsMinutesAfterMidnight = (utcNow.TimeOfDay.Hours * 60) +
utcNow.TimeOfDay.Minutes;
// If now is not within the range of available times . . .
if (!((pq.StartTimeOfDay < utcNowAsMinutesAfterMidnight)
&&
(utcNowAsMinutesAfterMidnight < pq.UntilTimeOfDay)))
{
statusReport = statusReport + " Is not available at this time of day. ";
}
}
}

6.6.2.10. Cmo: Validar y Combinar Elementos PrintTicket


Una novedad de Windows Vista es el esquema de impresin, que incluye los elementos PrintCapabilities y
PrintTicket flexibles y extensibles. El primero de ellos detalla las funciones de un dispositivo de impresin y el
segundo especifica se deben usar funciones en el dispositivo con respecto a una secuencia determinada de
documentos, un documento individual o una pgina individual.
A continuacin se indica una secuencia tpica de tareas para una aplicacin que admite la impresin.
1.

Determinar las funciones de una impresora.

2.

Configurar una PrintTicket para utilizar esas funciones.

3.

Validar la solicitud de impresin (PrintTicket).

En este artculo se muestra cmo hacerlo.


Ejemplo
En el ejemplo simple siguiente, lo nico que nos interesa es saber si una impresora admite la impresin a dos
caras. A continuacin se enumeran los pasos principales.
1.
2.

Obtenga un objeto PrintCapabilities mediante el mtodo GetPrintCapabilities.


Compruebe la presencia de la funcin que desea. En el ejemplo siguiente, se prueba la propiedad
DuplexingCapability del objeto PrintCapabilities para comprobar si est presente la funcin de
impresin a dos caras girando la pgina sobre el lado largo de la hoja. Puesto que DuplexingCapability
es una coleccin, utilizamos el mtodo Contains de ReadOnlyCollection<(Of <(T>)>).
Nota:

MCT: Luis Dueas

Pag 304 de 473

Manual de Windows Presentation Foundation

Este paso no es estrictamente necesario. El mtodo MergeAndValidatePrintTicket que se utiliza a


continuacin comprueba cada solicitud de PrintTicket con respecto a las funciones de la impresora.
Si la impresora no admite la funcin solicitada, en su lugar el controlador de impresora utiliza una
solicitud alternativa en la PrintTicket devuelto por el mtodo.
3.

Si la impresora admite la impresin a dos caras, el ejemplo de cdigo crea una PrintTicket, que solicita
la impresin a dos caras. Pero la aplicacin no especifica cada posible configuracin de impresora
disponible en el elemento PrintTicket. Esto malgastara tiempo de programacin y tiempo de ejecucin
del programa. En lugar de ello, en el cdigo se establece nicamente la solicitud de impresin a dos
caras y, a continuacin, se combina PrintTicket con otra PrintTicket totalmente configurada y validada.
En este caso, se utiliza la PrintTicket predeterminada del usuario.

4.

En consecuencia, en el ejemplo se llama al mtodo MergeAndValidatePrintTicket para combinar la


nueva PrintTicket mnima con la PrintTicket predeterminada del usuario. Esto devuelve un Validation
Result que incluye la nueva PrintTicket como una de sus propiedades.

5.

A continuacin, en el ejemplo se comprueba que la nueva PrintTicket solicita la impresin a dos caras.
Si lo hace, entonces se convierte en la nueva solicitud de impresin predeterminada del usuario. Si se
omitiera el paso 2 anterior y la impresora no admite la impresin a dos caras con giro en el lado largo,
entonces el resultado de la prueba sera false. (Consulte la nota anterior.)

6.

El ltimo paso significativo es confirmar el cambio a la propiedad UserPrintTicket de PrintQueue con el

mtodo Commit.
/// <summary>
/// Changes the user-default PrintTicket setting of the specified print queue.
/// </summary>
/// <param name="queue">the printer whose user-default PrintTicket setting needs to be
changed</param>
static private void ChangePrintTicketSetting(PrintQueue queue)
{
// Obtain the printer's PrintCapabilities so we can determine whether or not
// duplexing printing is supported by the printer.
PrintCapabilities printcap = queue.GetPrintCapabilities();
// The printer's duplexing capability is returned as a read-only collection of
// duplexing options that can be supported by the printer. If the collection returned
// contains the duplexing option we want to set, it means the duplexing option we want
// to set is supported by the printer,
// so we can make the user-default PrintTicket setting change.
if (printcap.DuplexingCapability.Contains(Duplexing.TwoSidedLongEdge))
{
// To change the user-default PrintTicket, we can first create a delta PrintTicket
// with the new duplexing setting.
PrintTicket deltaTicket = new PrintTicket();
deltaTicket.Duplexing = Duplexing.TwoSidedLongEdge;
// Then merge the delta PrintTicket onto the printer's current user-default
// PrintTicket, and validate the merged PrintTicket to get the new PrintTicket we
// want to set as the printer's new user-default PrintTicket.
ValidationResult result = queue.MergeAndValidatePrintTicket(queue.UserPrintTicket,
deltaTicket);
// The duplexing option we want to set could be constrained by other PrintTicket
// settings or device settings. We can check the validated merged PrintTicket to
// see whether the validation process has kept the duplexing option we want to set
// unchanged.
if (result.ValidatedPrintTicket.Duplexing == Duplexing.TwoSidedLongEdge)
{
// Set the printer's user-default PrintTicket and commit the set operation.
queue.UserPrintTicket = result.ValidatedPrintTicket;
queue.Commit();
Console.WriteLine("PrintTicket new duplexing setting is set on '{0}'.",

MCT: Luis Dueas

Pag 305 de 473

Manual de Windows Presentation Foundation


queue.FullName);
}
else
{
// The duplexing option we want to set has been changed by the validation
// process when it was resolving setting constraints.
Console.WriteLine("PrintTicket new duplexing setting is constrained on '{0}'.",
queue.FullName);
}
}
else
{
// If the printer doesn't support the duplexing option we want to set, skip it.
Console.WriteLine("PrintTicket new duplexing setting is not supported on '{0}'.",
queue.FullName);
}
}
Para que pueda probar rpidamente este ejemplo, se presenta el resto del mismo a continuacin. Cree un
proyecto y un espacio de nombres y, a continuacin, pegue los dos fragmentos de cdigo de este artculo en el
bloque de espacio de nombres.
/// <summary>
/// Displays the correct command line syntax to run this sample program.
/// </summary>
static private void DisplayUsage()
{
Console.WriteLine();
Console.WriteLine("Usage #1: printticket.exe -l \"<printer_name>\"");
Console.WriteLine("
Run program on the specified local printer");
Console.WriteLine();
Console.WriteLine("Quotation marks may be omitted if there are no spaces in
printer_name.");
Console.WriteLine();
Console.WriteLine("Usage #2: printticket.exe -r "\\\\<server_name>\\<printer_name>\"");
Console.WriteLine("
Run program on the specified network printer");
Console.WriteLine();
Console.WriteLine("Quotation marks may be omitted if there are no spaces in server_name
or printer_name.");
Console.WriteLine();
Console.WriteLine("Usage #3: printticket.exe -a");
Console.WriteLine("
Run program on all installed printers");
Console.WriteLine();
}
[STAThread]
static public void Main(string[] args)
{
try
{
if ((args.Length == 1) && (args[0] == "-a"))
{
// Change PrintTicket setting for all local and network printer connections.
LocalPrintServer server = new LocalPrintServer();
EnumeratedPrintQueueTypes[] queue_types = {EnumeratedPrintQueueTypes.Local,
EnumeratedPrintQueueTypes.Connections};
// Enumerate through all the printers.
foreach (PrintQueue queue in server.GetPrintQueues(queue_types))
{
// Change the PrintTicket setting queue by queue.
ChangePrintTicketSetting(queue);
}
}//end if -a
else if ((args.Length == 2) && (args[0] == "-l"))
{

MCT: Luis Dueas

Pag 306 de 473

Manual de Windows Presentation Foundation


// Change PrintTicket setting only for the specified local printer.
LocalPrintServer server = new LocalPrintServer();
PrintQueue queue = new PrintQueue(server, args[1]);
ChangePrintTicketSetting(queue);
}//end if -l
else if ((args.Length == 2) && (args[0] == "-r"))
{
// Change PrintTicket setting only for the specified remote printer.
String serverName = args[1].Remove(args[1].LastIndexOf(@"\"));
String printerName = args[1].Remove(0, args[1].LastIndexOf(@"\")+1);
PrintServer ps = new PrintServer(serverName);
PrintQueue queue = new PrintQueue(ps, printerName);
ChangePrintTicketSetting(queue);
}//end if -r
else
{
// Unrecognized command line.
// Show user the correct command line syntax to run this sample program.
DisplayUsage();
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
// Show inner exception information if it's provided.
if (e.InnerException != null)
{
Console.WriteLine("--- Inner Exception ---");
Console.WriteLine(e.InnerException.Message);
Console.WriteLine(e.InnerException.StackTrace);
}
}
finally
{
Console.WriteLine("Press Return to continue...");
Console.ReadLine();
}
}//end Main

7. Grficos y Multimedia
Windows Presentation Foundation (WPF) incluye compatibilidad con grficos 2D y 3D, animaciones y multimedia
de gran calidad. Algunas de las caractersticas clave de la plataforma grfica son:

Compatibilidad con grficos vectoriales.


Aceleracin de hardware.
Grficos independientes de la resolucin e independientes del dispositivo.
Actualizacin de pantalla mnima y sistema de animacin integrado.

7.1. Informacin General sobre Caractersticas de Grficos, Animacin y


Multimedia en WPF
En este tema se presentan las caractersticas de grficos, animacin y multimedia de Windows Presentation
Foundation (WPF), que permite agregar grficos, efectos de transicin, sonido y vdeo a las aplicaciones.
WPF ofrece caractersticas avanzadas de dibujo y animacin, caractersticas que antes solamente estaban
disponibles en bibliotecas especializadas, en concreto Interfaz de dispositivo grfico de Microsoft Windows
(GDI) y Microsoft Windows GDI+. WPF ofrece ahora compatibilidad integrada para multimedia, grficos
vectoriales, animacin y composicin de contenidos, lo que facilita a los programadores la tarea de generar

MCT: Luis Dueas

Pag 307 de 473

Manual de Windows Presentation Foundation


interesantes interfaces de usuario y contenidos. Utilizando Microsoft Visual Studio .NET o, incluso, un editor de
texto como Bloc de notas de Microsoft, puede crear grficos vectoriales o animacin complejas e integrar
multimedia en las aplicaciones.
Novedades de grficos y multimedia en WPF
WPF presenta a los programadores de Windows nuevas caractersticas de grficos que tienen las ventajas
siguientes:

Grficos independientes de la resolucin e independientes del dispositivo. El sistema de


grficos de WPF utiliza unidades independientes del dispositivo para que sean independientes de la
resolucin y del dispositivo. Cada pxel independiente del dispositivo ajusta su escala automticamente
a la configuracin de puntos por pulgada del sistema.

Precisin mejorada. El sistema de coordenadas de WPF utiliza valores de tipo double en lugar de
float. Las transformaciones y los valores de opacidad tambin se expresan usando valores de tipo
double. WPF admite adems una gama de color ms amplia (scRGB) y ofrece compatibilidad integrada
para la administracin de entradas de diferentes espacios de color.

Compatibilidad con grficos avanzados y animacin. WPF simplifica la programacin de grficos


administrando por usted la representacin grfica de la escena; ya no hay que preocuparse de
procesados

de

escenas,

bucles

de

representacin

interpolaciones

bilineales.

WPF

ofrece

compatibilidad con pruebas de posicionamiento, un sistema de animacin integrado y compatibilidad


total con composicin alfa.

Aceleracin de hardware. El sistema de grficos de WPF se ha diseado de modo que aproveche el


hardware grfico para minimizar el uso de la CPU.

Formas 2D
WPF proporciona una biblioteca de formas 2D de uso comn, dibujadas mediante vectores, tales como
rectngulos y elipses que se muestran en la ilustracin siguiente.

Estas formas intrnsecas de WPF no son solamente formas: son elementos programables que implementan
muchas de las caractersticas que se esperan de la mayora de los controles comunes, incluida la entrada de
teclado y mouse.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Window1" >
<Ellipse Fill="LightBlue" MouseUp="ellipseButton_MouseUp" />
</Window>
public partial class Window1 : Window
{
void ellipseButton_MouseUp(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Me, a simple ellipse, was mouse clicked!");
}
}
La ilustracin siguiente muestra el resultado del marcado XAML y del cdigo subyacente anterior.

MCT: Luis Dueas

Pag 308 de 473

Manual de Windows Presentation Foundation

Geometras 2D
Cuando las formas 2D que proporciona WPF no sean suficientes, puede utilizar la compatibilidad de WPF con
geometras y trayectorias para crear formas propias. La ilustracin siguiente muestra cmo puede utilizar
geometras para crear formas, como un pincel de dibujo, y para recortar otros elementos de WPF.

Efectos 2D
WPF proporciona una biblioteca de clases 2D que puede utilizar para crear diversos efectos. La capacidad de
representacin 2D de WPF ofrece la capacidad de pintar elementos interfaz de usuario con degradados, mapas
de bits, dibujos y vdeos; tambin de manipularlos utilizando giro, escalado y sesgo. La ilustracin siguiente
proporciona un ejemplo de los muchos efectos que puede lograr utilizando los pinceles de WPF.

Representacin 3D
WPF proporciona un conjunto de capacidades de representacin 3D que se integran con la compatibilidad con
grficos 2D en WPF para crear interesantes diseos, interfaz de usuario y visualizaciones de datos. En un
extremo del espectro, WPF permite representar imgenes 2D en las superficies de formas 3D, que se muestran
en la siguiente ilustracin.

Animacin
Utilice animaciones para hacer que los controles y los elementos crezcan, se agiten, giren y se desvanezcan,
para crear interesantes transiciones de pgina y para otros efectos. Dado que WPF permite animar la mayora
de las propiedades, no solamente podr animar la mayora de los objetos de WPF, sino que tambin puede
utilizar WPF para animar los objetos personalizados que cree.

MCT: Luis Dueas

Pag 309 de 473

Manual de Windows Presentation Foundation

Multimedia
Las imgenes, el vdeo y el audio son medios multimedia para la difusin de informacin y experiencias de
usuario.
Imgenes
Las imgenes, entre las que se incluyen iconos, fondos e incluso partes de animaciones, son una parte bsica
de la mayora de las aplicaciones. Dado que frecuentemente necesitar utilizar imgenes, WPF expone la
capacidad de trabajar con ellas de diversas maneras. La ilustracin siguiente muestra solamente una de esas
maneras.

Vdeo y audio
Una caracterstica bsica de las capacidades grficas de WPF es la compatibilidad nativa para el trabajo con
multimedia, lo que incluye vdeo y audio. El ejemplo siguiente muestra cmo insertar un reproductor
multimedia en una aplicacin.
<MediaElement Source="media\numbers.wmv" Width="450" Height="250" />
MediaElement es capaz de reproducir tanto vdeo como audio, y es lo suficientemente extensible como para
permitir la creacin fcil de UIs personalizadas.

7.2. Informacin General sobre la Representacin de Grficos en WP


En este tema se proporciona informacin general sobre la capa visual de WPF. Se centra en la funcin de la
clase Visual para admitir la representacin del modelo de WPF.
Funcin de los objetos visuales
La clase Visual es la abstraccin bsica de la que se derivan todos los objetos FrameworkElement. Tambin
acta como punto de entrada para escribir nuevos controles en WPF, y en muchos sentidos se puede considerar
como el identificador de ventana (HWND) del modelo de aplicaciones de Win32.
El objeto Visual es un objeto bsico de WPF, cuya funcin principal es proporcionar la compatibilidad con la
representacin. Los controles de interfaz de usuario, tales como Button y TextBox, se derivan de la clase Visual
y la utilizan para conservar sus datos de representacin. El objeto Visual proporciona compatibilidad con:

Presentacin de salida: representacin del contenido conservado y serializado de un elemento visual.

Transformaciones: realizacin de transformaciones de un elemento visual.

Recorte: compatibilidad con la zona de recorte para un elemento visual.

Pruebas de posicionamiento: determinacin de si una coordenada o geometra est contenida dentro


de los lmites de un elemento visual.

Clculos del rectngulo de seleccin: determinacin del rectngulo delimitador de un elemento visual.

MCT: Luis Dueas

Pag 310 de 473

Manual de Windows Presentation Foundation


Sin embargo, el objeto Visual no incluye compatibilidad con caractersticas que no son de representacin, tales
como:

Control de eventos
Diseo
Estilos
Enlace de datos
Globalizacin

Visual se expone como una clase abstracta pblica de la que se deben derivar las clases secundarias. En la
ilustracin siguiente se muestra la jerarqua de los objetos visuales que se exponen en WPF.
Jerarqua de la clase Visual

Clase DrawingVisual
DrawingVisual es una clase de dibujo ligera que se utiliza para representar formas, imgenes o texto. Esta clase
se considera ligera porque no proporciona administracin del diseo ni control de eventos, lo que mejora su
rendimiento en tiempo de ejecucin. Por esta razn, los dibujos son idneos para fondos e imgenes
prediseadas. DrawingVisual se puede utilizar para crear un objeto visual personalizado.
Clase Viewport3DVisual
Viewport3DVisual proporciona un puente entre los objetos 2D Visual y Visual3D. La clase Visual3D es la clase
base para los elementos visuales 3D. Viewport3DVisual requiere que se defina un valor Camera y un valor
Viewport. La cmara permite ver la escena. El rea de visualizacin establece dnde se asigna la proyeccin a
la superficie 2D.
Clase ContainerVisual
La clase ContainerVisual se utiliza como contenedor de una coleccin de objetos Visual. La clase DrawingVisual
se deriva de la clase ContainerVisual, lo que le permite contener una coleccin de objetos visuales.
Dibujar contenido en objetos visuales
Un objeto Visual almacena sus datos de representacin como una lista de instrucciones de grficos
vectoriales. Cada elemento de la lista de instrucciones representa un conjunto de bajo nivel de datos de los
grficos y de recursos asociados en un formato serializado. Hay cuatro tipos diferentes de datos de
representacin que pueden incluir contenido de dibujo.
Tipo de contenido de
los dibujos

Descripcin

Grficos vectoriales

Representa datos de grficos vectoriales y e informacin de cualquier Brush y


Pen asociado.

Imagen

Representa una imagen dentro de una regin definida por un Rect.

Glifo

Representa un dibujo que representa un GlyphRun, que es una secuencia de


glifos de un recurso de fuente especificado. As es como se representa el texto.

Vdeo

Representa un dibujo que representa el vdeo.

DrawingContext permite rellenar un objeto Visual con contenido visual. Cuando se utilizan los comandos de
dibujo de un objeto DrawingContext, en realidad se est almacenando un conjunto de datos de representacin
que el sistema de grficos utilizar ms adelante; no se dibuja en la pantalla en tiempo real.

MCT: Luis Dueas

Pag 311 de 473

Manual de Windows Presentation Foundation


Al crear un control WPF, como Button, el control genera implcitamente datos de representacin para dibujarse
a s mismo. Por ejemplo, al establecer la propiedad Content de Button hace que el control almacene una
representacin de la representacin de un glifo.
Un objeto Visual describe su contenido como uno o ms objetos Drawing contenidos dentro de un
DrawingGroup. DrawingGroup tambin describe las mscaras de opacidad, las transformaciones, los efectos de
mapa de bits y otras operaciones que se aplican a su contenido. Las operaciones de DrawingGroup se aplican
en el orden siguiente al representar contenido: OpacityMask, Opacity, BitmapEffect, ClipGeometry, GuidelineSet
y, por ltimo, Transform.
En la ilustracin siguiente se muestra el orden en el que se aplican las operaciones de DrawingGroup durante la
secuencia de representacin.
Orden de las operaciones de DrawingGroup

Dibujar contenido en la capa visual


Nunca se crea directamente una instancia de DrawingContext; sin embargo, se puede adquirir un contexto de
dibujo a partir de determinados mtodos, como DrawingGroup.Open y DrawingVisual.RenderOpen. En el
ejemplo siguiente se recupera un objeto DrawingContext de un objeto DrawingVisual y se utiliza para dibujar
un rectngulo.
// Create a DrawingVisual that contains a rectangle.
private DrawingVisual CreateDrawingVisualRectangle()
{
DrawingVisual drawingVisual = new DrawingVisual();
// Retrieve the DrawingContext in order to create new drawing content.
DrawingContext drawingContext = drawingVisual.RenderOpen();
// Create a rectangle and draw it in the DrawingContext.
Rect rect = new Rect(new System.Windows.Point(160, 100),
new System.Windows.Size(320, 80));
drawingContext.DrawRectangle(System.Windows.Media.Brushes.LightBlue,
(System.Windows.Media.Pen)null, rect);
// Persist the drawing content.
drawingContext.Close();
return drawingVisual;
}
Enumerar contenido de dibujo en la capa visual
Adems de sus otros usos, los objetos Drawing tambin proporcionan un modelo de objetos para enumerar el
contenido de Visual.
Nota:
Al enumerar el contenido del elemento visual, se recuperan objetos Drawing, no la representacin
subyacente de los datos de representacin como una lista de instrucciones de grficos vectoriales.
En el ejemplo siguiente se utiliza el mtodo GetDrawing para recuperar el valor de DrawingGroup de un objeto
Visual y enumerarlo.

MCT: Luis Dueas

Pag 312 de 473

Manual de Windows Presentation Foundation


public void RetrieveDrawing(Visual v)
{
DrawingGroup dGroup = VisualTreeHelper.GetDrawing(v);
EnumDrawingGroup(dGroup);
}
// Enumerate the drawings in the DrawingGroup.
public void EnumDrawingGroup(DrawingGroup drawingGroup)
{
DrawingCollection dc = drawingGroup.Children;
// Enumerate the drawings in the DrawingCollection.
foreach (Drawing drawing in dc)
{
// If the drawing is a DrawingGroup, call the function recursively.
if (drawing.GetType() == typeof(DrawingGroup))
{
EnumDrawingGroup((DrawingGroup)drawing);
}
else if (drawing.GetType() == typeof(GeometryDrawing))
{
// Perform action based on drawing type.
}
else if (drawing.GetType() == typeof(ImageDrawing))
{
// Perform action based on drawing type.
}
else if (drawing.GetType() == typeof(GlyphRunDrawing))
{
// Perform action based on drawing type.
}
else if (drawing.GetType() == typeof(VideoDrawing))
{
// Perform action based on drawing type.
}
}
}
Cmo utilizar los objetos visuales para crear controles
Muchos de los objetos de WPF estn compuestos de otros objetos visuales, lo que significa que pueden
contener diversas jerarquas de objetos descendientes. Muchos de los elementos de interfaz de usuario de WPF,
como los controles, estn compuestos de varios objetos visuales, que constituyen diferentes tipos de de
representar los elementos. Por ejemplo, el control Button puede contener varios otros objetos, incluso
ClassicBorderDecorator, ContentPresenter y TextBlock.
En el ejemplo de cdigo siguiente se muestra un control Button definido en el marcado.
<Button Click="OnClick">OK</Button>
Si enumerase los objetos visuales que comprenden el control Button predeterminado, hallara la jerarqua de
objetos visuales ilustrada a continuacin:
Diagrama de jerarqua de rbol de elementos visuales

El control Button contiene un elemento ClassicBorderDecorator que, a su vez, contiene un elemento


ContentPresenter. El elemento ClassicBorderDecorator es responsable de dibujar un borde y un fondo para
Button. El elemento ContentPresenter es responsable de mostrar el contenido de Button. En este caso, puesto
que se muestra texto, el elemento ContentPresenter contiene un elemento TextBlock. El hecho de que el
control Button utilice ContentPresenter quiere decir que el contenido podra representarse por otros elementos,
como Image o por una geometra, como EllipseGeometry.
Plantillas de control

MCT: Luis Dueas

Pag 313 de 473

Manual de Windows Presentation Foundation


La clave para la expansin de un control en una jerarqua de controles es ControlTemplate. Una plantilla de
control especifica la jerarqua visual predeterminada de un control. Al hacer referencia explcitamente a un
control, se hace referencia implcitamente a su jerarqua visual. Puede invalidar los valores predeterminados de
una plantilla a fin de crear un aspecto visual personalizado para un control. Por ejemplo, podra modificar el
valor de color de fondo del control Button de modo que utilice un valor de color de degradado lineal en lugar de
un valor de color slido.
Un elemento de interfaz de usuario, como un control Button, contiene varias listas de instrucciones de grficos
vectoriales que describen la definicin de representacin completa de un control. En el ejemplo de cdigo
siguiente se muestra un control Button definido en el marcado.
<Button Click="OnClick">
<Image Source="images\greenlight.jpg"></Image>
</Button>
Si enumerase los objetos visuales y las listas de instrucciones de grficos vectoriales que comprenden el control
Button, hallara la jerarqua de objetos ilustrada a continuacin:
Diagrama de rbol visual y datos de representacin

El control Button contiene un elemento ClassicBorderDecorator que, a su vez, contiene un elemento


ContentPresenter. El elemento ClassicBorderDecorator es responsable de dibujar todos los elementos grficos
discretos que constituyen el borde y el fondo de un botn. El elemento ContentPresenter es responsable de
mostrar el contenido de Button. En este caso, puesto que se muestra una imagen, el elemento
ContentPresenter contiene un elemento Image.
Deben tenerse en cuenta varios puntos sobre la jerarqua de objetos visuales y las listas de instrucciones de
grficos vectoriales:

El orden en la jerarqua representa el orden de representacin de la informacin de dibujo. Partiendo


del elemento visual raz, los elementos secundarios se recorren de izquierda a derecha y de arriba
abajo. Si un elemento tiene elementos visuales secundarios, se recorren antes que los elementos del
mismo nivel.

Los elementos de nodos que no sean de hoja de la jerarqua, tales como ContentPresenter, se utilizan
para contener elementos secundarios, no contienen listas de instrucciones.

Si un elemento visual contiene una lista de instrucciones de grficos vectoriales y elementos visuales
secundarios, la lista de instrucciones del elemento visual primario se representar antes que los
dibujos de cualquiera de los objetos visuales secundarios.

Los elementos de la lista de instrucciones de grficos vectoriales se representan de izquierda a


derecha.

rbol visual
El rbol visual contiene todos los elementos visuales utilizados en la interfaz de usuario de una aplicacin.
Puesto que un elemento visual contiene informacin de dibujo conservada, puede considerar el rbol visual
como una representacin grfica de la escena, que contiene toda la informacin de representacin necesaria

MCT: Luis Dueas

Pag 314 de 473

Manual de Windows Presentation Foundation


para crear la salida al dispositivo de pantalla. Este rbol es la acumulacin de todos los elementos visuales
creados directamente por la aplicacin, ya sea en el cdigo o en el marcado. El rbol visual tambin contiene
todos los elementos visuales creados por la expansin de la plantilla de elementos tales como controles y
objetos de datos.
En el ejemplo de cdigo siguiente se muestra un elemento StackPanel definido en el marcado.
<StackPanel>
<Label>User name:</Label>
<TextBox />
<Button Click="OnClick">OK</Button>
</StackPanel>
Si enumerase los objetos visuales que comprenden el elemento StackPanel del ejemplo de marcado, hallara la
jerarqua de objetos visuales ilustrada a continuacin:
Diagrama de jerarqua de rbol de elementos visuales

Orden de representacin
El rbol visual determina el orden de representacin de los objetos visuales y de dibujo de WPF. El orden del
recorrido comienza en el elemento visual raz, que es el nodo del nivel superior del rbol visual. A continuacin
se recorren los elementos secundarios del elemento visual raz, de izquierda a derecha. Si un elemento visual
tiene elementos secundarios, stos ltimos se recorren antes que los elementos que estn en el mismo nivel
que el elemento visual. Esto significa que el contenido de un elemento visual secundario se representa delante
del propio contenido del elemento visual.
Diagrama del orden de representacin del rbol visual

Elemento visual raz


El elemento visual raz es el elemento de nivel superior de la jerarqua de un rbol visual. En la mayora de
las aplicaciones, la clase base del elemento visual raz es Window o NavigationWindow. Sin embargo, si
hospedase objetos visuales en una aplicacin de Win32, el elemento visual raz sera el elemento visual de nivel
superior que hospedase en la ventana de Win32.
Relaciones con el rbol lgico
El rbol lgico de WPF representa los elementos de una aplicacin en tiempo de ejecucin. Aunque no se
manipula directamente este rbol, esta vista de la aplicacin es til para entender la herencia de propiedades y
el enrutamiento de eventos. A diferencia del rbol visual, el rbol lgico puede representar objetos de datos no
visuales, tales como ListItem. En muchos casos, el rbol lgico se corresponde muy estrechamente con las
definiciones de marcado de una aplicacin. En el ejemplo de cdigo siguiente se muestra un elemento
DockPanel definido en el marcado.
<DockPanel>
<ListBox>
<ListBoxItem>Dog</ListBoxItem>
<ListBoxItem>Cat</ListBoxItem>

MCT: Luis Dueas

Pag 315 de 473

Manual de Windows Presentation Foundation


<ListBoxItem>Fish</ListBoxItem>
</ListBox>
<Button Click="OnClick">OK</Button>
</DockPanel>
Si enumerase los objetos lgicos que comprenden el elemento DockPanel del ejemplo de marcado, hallara la
jerarqua de objetos lgicos ilustrada a continuacin:
Diagrama de rbol lgico

El rbol visual y el rbol lgico se sincronizan con el conjunto actual de elementos de aplicacin, con lo que se
refleja cualquier adicin, eliminacin o modificacin de elementos. Sin embargo, los rboles presentan vistas
diferentes de la aplicacin. A diferencia del rbol visual, el rbol lgico no expande el elemento
ContentPresenter de un control. Esto significa que no existe una correspondencia unvoca directa entre un rbol
lgico y un rbol visual para el mismo conjunto de objetos. De hecho, al invocar el mtodo GetChildren del
objeto LogicalTreeHelper y el mtodo GetChildVisualTreeHelper del objeto utilizando el mismo elemento
como parmetro se obtienen resultados diferentes.
Ver el rbol visual con XamlPad
La herramienta XamlPad de WPF proporciona una opcin para ver y explorar el rbol visual que corresponde al
contenido de XAML actualmente definido. Haga clic en el botn Mostrar rbol visual en la barra de mens para
mostrar el rbol visual. A continuacin se ilustra la expansin del contenido de XAML en los nodos del rbol
visual en el panel Visual Tree Explorer de XamlPad:
Panel del explorador de rboles visuales de XamlPad

Observe que cada uno de los controles Label, TextBox y Button muestra una jerarqua de objetos visuales
independiente en el panel del explorador de rboles visuales de XamlPad. Esto se debe a que los controles de
WPF tienen una ControlTemplate que contiene el rbol visual de ese control. Al hacer referencia explcitamente
a un control, se hace referencia implcitamente a su jerarqua visual.
Crear perfiles de rendimiento visual
WPF proporciona un conjunto de herramientas de creacin de perfiles de rendimiento que permiten analizar el
funcionamiento en tiempo de ejecucin de la aplicacin y determinar los tipos de optimizacin del rendimiento
que se pueden aplicar. La herramienta Visual Profiler proporciona una vista grfica y completa de los datos de
rendimiento a travs de una asignacin directa al rbol visual de la aplicacin. En esta captura de pantalla, la

MCT: Luis Dueas

Pag 316 de 473

Manual de Windows Presentation Foundation


seccin CPU Usage de Visual Profiler ofrece un desglose preciso del uso, por parte de un objeto, de servicios de
WPF tales como la representacin y el diseo.
Resultados de la presentacin de Visual Profiler

Comportamiento de la representacin visual


WPF introduce varias caractersticas que afectan al comportamiento de representacin de los objetos visuales:
grficos de modo retenido, grficos vectoriales y grficos independientes del dispositivo.
Grficos de modo retenido
Una de las claves para entender la funcin de los objetos visuales es comprender la diferencia entre los
sistemas de grficos de modo inmediato y de modo retenido. Una aplicacin de Win32 estndar basada en
GDI o GDI+ utiliza un sistema de grficos de modo inmediato. Esto significa que la aplicacin es responsable de
volver a dibujar la parte del rea cliente que se ha invalidado, ya sea debido a una accin como el cambio de
tamao de una ventana, o a la modificacin del aspecto visual de un objeto.
Diagrama de la secuencia de representacin de Win32

En cambio, WPF utiliza un sistema de modo retenido. Esto significa que los objetos de la aplicacin que tienen
aspecto visual definen un conjunto de datos de dibujo serializados. Una vez definidos los datos de dibujo, el
sistema se hace responsable en lo sucesivo de responder a todas las solicitudes de repeticin del dibujo para
representar los objetos de aplicacin. Incluso en tiempo de ejecucin, se puede modificar o crear los objetos de
aplicacin y, an as, confiar en que el sistema responder a las solicitudes de dibujo. La eficacia de un sistema
de grficos de modo retenido reside en que la aplicacin conserva en todo momento la informacin de dibujo
siempre en un estado serializado, pero deja al sistema la responsabilidad de la representacin. En el diagrama
siguiente se muestra cmo delega la aplicacin en WPF para que responda a las solicitudes de dibujo.
Diagrama de la secuencia de representacin de WPF

MCT: Luis Dueas

Pag 317 de 473

Manual de Windows Presentation Foundation


Actualizacin inteligente del dibujo
Una de las mayores ventajas que aporta el uso de grficos de modo retenidos es que WPF puede optimizar con
eficacia lo que es preciso volver a dibujar en la aplicacin. Aunque se trate de una escena compleja con varios
niveles de opacidad, generalmente no se necesita escribir cdigo especfico para optimizar la actualizacin del
dibujo. Compare esto con la programacin de Win32, en la que se puede llegar a dedicar mucho esfuerzo a
optimizar la aplicacin minimizando la cantidad de actualizaciones del dibujo que se efectan en la regin de
actualizacin.
Grficos vectoriales
WPF utiliza grficos vectoriales como su formato de representacin de datos. Los grficos vectoriales, que
incluyen grficos vectoriales escalables (SVG), metarchivos de Windows (.wmf) y fuentes TrueType, almacenan
los datos de representacin y los transmiten como una lista de instrucciones que describen cmo volver a crear
la imagen mediante los elementos primitivos de los grficos. Por ejemplo, las fuentes TrueType son fuentes de
contorno que describen un conjunto de lneas, curvas y comandos, en lugar de una matriz de pxeles. Una de
las ventajas fundamentales de los grficos vectoriales es la capacidad de adaptar su escala a cualquier tamao
y resolucin.
Al contrario que los grficos vectoriales, los grficos de mapa de bits almacenan los datos de representacin
como una representacin pxel por pxel de una imagen, previamente representada para una resolucin
especfica. Una de las diferencias fundamentales entre los formatos de grficos de mapa de bits y vectorial es la
fidelidad con respecto a la imagen de origen inicial. Por ejemplo, cuando se modifica el tamao de la imagen de
origen, los sistemas de grficos de mapa de bits expanden la imagen, mientras que los sistemas de grficos
vectoriales adaptan su escala, conservando la fidelidad con la imagen inicial.
En la ilustracin siguiente se muestra una imagen de origen cuyo tamao se ha cambiado al 300%. Observe las
distorsiones que aparecen cuando se ampla la imagen de origen como imagen de grficos de mapa de bits en
lugar de ajustarla a escala como imagen de grficos vectoriales.
Diferencias entre grficos de tramas y vectoriales

En el marcado siguiente se muestran dos elementos Path definidos. El segundo elemento utiliza una
ScaleTransform para cambiar el tamao de las instrucciones de dibujo del primer elemento al 300%. Observe
que las instrucciones de dibujo de los elementos Path no sufren cambio alguno.
<Path Data="M10,100 C 60,0 100,200 150,100 z" Fill="{StaticResource
linearGradientBackground}" Stroke="Black" StrokeThickness="2" />
<Path Data="M10,100 C 60,0 100,200 150,100 z" Fill="{StaticResource
linearGradientBackground}" Stroke="Black" StrokeThickness="2" >
<Path.RenderTransform>
<ScaleTransform ScaleX="3.0" ScaleY="3.0" />
</Path.RenderTransform>
</Path>
Grficos independientes de la resolucin y del dispositivo

MCT: Luis Dueas

Pag 318 de 473

Manual de Windows Presentation Foundation


Existen dos factores del sistema que determinan el tamao del texto y los grficos en la pantalla: la resolucin
y los pxeles por pulgada (PPP). La resolucin describe el nmero de pxeles que aparecen en la pantalla. A
medida que aumenta la resolucin, se reducen los pxeles, lo que hace que los grficos y el texto parezcan
menores. Un grfico mostrado en un monitor configurado con una resolucin de 1024 x 768 parece mucho
menor cuando la resolucin se cambia a 1600 x 1200.
El otro valor del sistema, PPP, describe el tamao de una pulgada de pantalla en pxeles. La mayora de los
sistemas Windows tienen un valor de PPP de 96, lo que significa que pulgada de pantalla tiene 96 pxeles. Al
aumentar el valor de PPP se incrementa el tamao de la pulgada de pantalla; al disminuir el valor de PPP se
reduce el tamao de la pulgada de pantalla. Esto significa que una pulgada de pantalla no tiene el mismo
tamao que una pulgada real; en la mayora de los sistemas, probablemente ambas pulgadas no sean iguales.
Cuando se incrementa el valor de PPP, los grficos y el texto que tienen en cuenta los PPP aumentan, porque se
ha incrementado el tamao de la pulgada de pantalla. Al aumentar el valor de PPP se puede facilitar la lectura
del texto, especialmente con las resoluciones ms altas.
No todas las aplicaciones tienen en cuenta el valor de PPP: algunas utilizan los pxeles de hardware como
unidad de medida principal; en estas aplicaciones, cambiar los PPP del sistema no surte ningn efecto. Muchas
otras aplicaciones utilizan unidades que tienen en cuenta el valor de PPP para describir los tamaos de fuente,
pero utilizan pxeles para describir todo lo dems. Un valor de PPP demasiado pequeo o demasiado grande
puede causar problemas del diseo en estas aplicaciones, porque el texto de la aplicacin adapta su escala de
acuerdo con el valor de PPP del sistema, pero, en cambio, la interfaz de usuario de la aplicacin no lo hace. Este
problema se ha eliminado para las aplicaciones programadas mediante WPF.
WPF admite el ajuste automtico de la escala, ya que utiliza pxeles independientes del dispositivo como unidad
de medida principal, en lugar de los pxeles del hardware; la escala de los grficos y del texto se adapta
correctamente sin que el desarrollador de la aplicacin tenga que realizar ninguna labor adicional. En la
ilustracin siguiente se muestra un ejemplo de cmo aparecen el texto y los grficos de WPF con distintos
valores de PPP.
Grficos y texto con distintos valores de PPP

Clase VisualTreeHelper
La clase VisualTreeHelper es una clase auxiliar esttica que proporciona funcionalidad de bajo nivel para
programar en el nivel de objetos visuales, lo que resulta til en escenarios muy concretos, como el de la
programacin de controles personalizados de alto rendimiento. En la mayora de los casos, los objetos del
marco de trabajo WPF de nivel superior, tales como Canvas y TextBlock, proporcionan mayor flexibilidad y
facilidad de uso.

MCT: Luis Dueas

Pag 319 de 473

Manual de Windows Presentation Foundation


Pruebas de posicionamiento
La clase VisualTreeHelper proporciona mtodos para efectuar las pruebas de posicionamiento de los objetos
visuales la compatibilidad de pruebas de posicionamiento predeterminada no satisface sus necesidades. Puede
utilizar los mtodos HitTest en la clase VisualTreeHelper para determinar si una geometra o el valor de
coordenada de punto est dentro del lmite de un objeto determinado, como un control o elemento grfico. Por
ejemplo, podra utilizar las pruebas de posicionamiento para determinar si un clic del mouse dentro del
rectngulo delimitador de un objeto pertenece a la geometra de un crculo. Si lo desea, tambin puede
invalidar la implementacin predeterminada de las pruebas de posicionamiento y realizar sus propios clculos
de pruebas de posicionamiento personalizadas.
Enumerar el rbol visual
La clase VisualTreeHelper proporciona la funcionalidad de enumerar los miembros de un rbol visual. Para
recuperar un objeto primario, llame al mtodo GetParent. Para recuperar un elemento secundario o un
descendiente directo de un objeto visual, llame al mtodo GetChild. Este mtodo devuelve el elemento
secundario Visual del elemento primario en el ndice especificado.
En el ejemplo siguiente se muestra cmo enumerar todos los descendientes de un objeto visual, que es una
tcnica que se puede utilizar si se desea serializar toda la informacin de representacin de una jerarqua de
objetos visuales.
// Enumerate all the descendants of the visual object.
static public void EnumVisual(Visual myVisual)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++)
{
// Retrieve child visual at specified index value.
Visual childVisual = (Visual)VisualTreeHelper.GetChild(myVisual, i);
// Do processing of the child visual object.
// Enumerate children of the child visual object.
EnumVisual(childVisual);
}
}
En la mayora de los casos, el rbol lgico constituye una representacin ms til de los elementos de una
aplicacin WPF. Aunque no se modifica directamente el rbol lgico, esta vista de la aplicacin es til para
entender la herencia de propiedades y el enrutamiento de eventos. A diferencia del rbol visual, el rbol lgico
puede representar objetos de datos no visuales, tales como ListItem.
La clase VisualTreeHelper proporciona mtodos para devolver el rectngulo delimitador de los objetos visuales.
Puede devolver el rectngulo delimitador de un objeto visual llamando a GetContentBounds. Puede devolver el
rectngulo delimitador de todos los descendientes de un objeto visual, incluido el propio objeto visual, llamando
a GetDescendantBounds. En el cdigo siguiente se muestra cmo se calculara el rectngulo delimitador de un
objeto visual y todos sus descendientes.
// Return the bounding rectangle of the parent visual object and all of its descendants.
Rect rectBounds = VisualTreeHelper.GetDescendantBounds(parentVisual);

7.3. Grficos
Windows Presentation Foundation (WPF) proporciona compatibilidad integrada con multimedia, grficos
vectoriales, animacin y creacin de contenido, lo que facilita a los programadores la creacin de interfaces de
usuario y contenido interesantes.

7.3.1. Efectos de Mapa de Bits


En el tema de esta seccin se describe cmo aplicar efectos visuales a las imgenes de mapa de bits utilizando
Windows Presentation Foundation (WPF).

7.3.1.1. Informacin General sobre Efectos de Mapa de Bits

MCT: Luis Dueas

Pag 320 de 473

Manual de Windows Presentation Foundation


Los efectos de mapa de bits permiten a los diseadores y programadores aplicar efectos visuales al contenido
Windows Presentation Foundation (WPF) representado. Por ejemplo, los efectos de mapa de bits permiten
aplicar con facilidad un efecto DropShadowBitmapEffect o un efecto de desenfoque a una imagen o un botn.
Efectos de mapa de bits en WPF
Los efectos de mapa de bits (objeto BitmapEffect) son operaciones simples de procesamiento de pxeles. Un
efecto de mapa de bits acepta un objeto BitmapSource como entrada y genera un nuevo BitmapSource despus
de aplicar el efecto, como un desenfoque o una sombra. Cada efecto de mapa de bits expone propiedades que
pueden controlar las propiedades de filtrado, como Radius de BlurBitmapEffect.
Como caso especial, en WPF, los efectos se pueden establecer como propiedades en los objetos Visual activos,
como Button o TextBox. El procesamiento de pxeles se aplica y representa en tiempo de ejecucin. En este
caso, al realizar la representacin, un objeto Visual se convierte automticamente en su BitmapSource
equivalente y se utiliza como datos de entrada en BitmapEffect. El resultado reemplaza el comportamiento de
representacin predeterminado del objeto Visual. Por este motivo, los objetos BitmapEffect fuerzan la
representacin de los objetos visuales nicamente en software, es decir, no se aplica ninguna aceleracin de
hardware a los elementos visuales cuando se aplican efectos.

BlurBitmapEffect simula la apariencia de un objeto a travs de una lente desenfocada.


OuterGlowBitmapEffect crea un halo de color alrededor del permetro de un objeto.
DropShadowBitmapEffect crea una sombra detrs de un objeto.
BevelBitmapEffect crea un ngulo oblicuo que eleva la superficie de una imagen de acuerdo con una
curva especificada.

EmbossBitmapEffect crea un mapa de rugosidad de un objeto Visual a fin de dar la impresin de


profundidad y textura a partir de una fuente de luz artificial.

Nota:
Los efectos de mapa de bits de WPF se representan en modo de software. Cualquier objeto que aplique un
efecto tambin se representar en software. Cuando ms se degrada el rendimiento es al utilizar efectos de
mapa de bits en objetos visuales grandes o al animar las propiedades de un efecto de mapa de bits. Esto no
quiere decir que no haya que utilizar nunca los efectos de mapa de bits, pero s debe extremar las
precauciones y efectuar pruebas exhaustivas para asegurarse de que los usuarios obtengan la experiencia
esperada.
Nota:
Los efectos de mapa de bits de WPF no admiten la ejecucin de confianza parcial. Una aplicacin debe tener
permisos de plena confianza para usar efectos de mapa de bits.
Cmo aplicar un efecto
BitmapEffect es una propiedad de Visual. As pues, aplicar efectos a los objetos visuales, tales como Button,
Image, DrawingVisual o UIElement, es tan sencillo como establecer la propiedad. BitmapEffect se puede
establecer en un solo objeto BitmapEffect, pero tambin se pueden encadenar varios efectos mediante el objeto
BitmapEffectGroup.
En el ejemplo de cdigo siguiente se muestra cmo se aplica BitmapEffect en Lenguaje de marcado de
aplicaciones extensible (XAML).
<Button Width="200">You Can't Read This!
<Button.BitmapEffect>
<!-- <BitmapEffectGroup> would go here if you wanted to apply more
then one effect to the Button. However, in this example only
one effect is being applied so BitmapEffectGroup does not need
to be included. -->
<!-- The larger the Radius, the more blurring. The default range is 20.
In addition, the KernelType is set to a box kernel. A box kernel
creates less disruption (less blur) then the default Gaussian kernel. -->
<BlurBitmapEffect Radius="10" KernelType="Box" />
</Button.BitmapEffect>
</Button>
En el ejemplo de cdigo siguiente se muestra cmo se aplica BitmapEffect mediante cdigo.

MCT: Luis Dueas

Pag 321 de 473

Manual de Windows Presentation Foundation


// Get a reference to the Button.
Button myButton = (Button)sender;
// Initialize a new BlurBitmapEffect that will be applied
// to the Button.
BlurBitmapEffect myBlurEffect = new BlurBitmapEffect();
// Set the Radius property of the blur. This determines how
// blurry the effect will be. The larger the radius, the more
// blurring.
myBlurEffect.Radius = 10;
// Set the KernelType property of the blur. A KernalType of "Box"
// creates less blur than the Gaussian kernal type.
myBlurEffect.KernelType = KernelType.Box;
// Apply the bitmap effect to the Button.
myButton.BitmapEffect = myBlurEffect;
Nota:
Cuando BitmapEffect se aplica a un contenedor de diseo, como DockPanel o Canvas, el efecto se aplica al
rbol visual del elemento u objeto visual, incluidos todos sus elementos secundarios.
Crear efectos personalizados
WPF tambin proporciona interfaces no administradas para crear efectos personalizados que se pueden usar en
aplicaciones de WPF administradas. Si desea obtener material de referencia adicional para crear efectos de
mapa de bits personalizados, consulte la documentacin de Unmanaged WPF Bitmap Effect.

7.3.1.2. Temas Cmo de Efectos de Mapa de Bits


En los siguientes temas se muestra cmo aplicar efectos visuales a las imgenes de mapa de bits utilizando
Windows Presentation Foundation (WPF).

7.3.1.2.1. Cmo: Crear un Efecto de Resplandor en el Margen Externo de un


Objeto
En este tema se explica cmo crear un efecto de resplandor en el borde exterior de un objeto.
Ejemplo
Puede utilizar la clase OuterGlowBitmapEffect para crear un efecto de resplandor alrededor de un objeto visible.
En este ejemplo se muestra cmo realizar las acciones siguientes:

Usar marcado simple para aplicar el efecto de resplandor a un objeto.


Usar Style para aplicar el efecto de resplandor a uno o ms objetos.
Usar marcado con cdigo subyacente para aplicar el efecto de resplandor a un objeto.
Usar una animacin para animar las propiedades de un efecto de resplandor que se aplica a un objeto.

Nota:
En todos los ejemplos siguientes se aplica un solo efecto a un objeto. Para aplicar varios efectos, utilice
BitmapEffectGroup.
En el ejemplo siguiente se muestra cmo utilizar OuterGlowBitmapEffect para crear un resplandor azul
alrededor del borde exterior de TextBox.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBox Width="200">
<TextBox.BitmapEffect>
<!-- <BitmapEffectGroup> would go here if you wanted to apply more
then one effect to the TextBox. However, in this example only
one effect is being applied so BitmapEffectGroup does not need
to be included. -->
<!-- The OuterGlow is blue, extends out 30 pixels, has the
maximum noise possible, and is 40% Opaque. -->
<OuterGlowBitmapEffect GlowColor="Blue" GlowSize="30" Noise="1"
Opacity="0.4" />
</TextBox.BitmapEffect>
</TextBox>
</StackPanel>
</Page>
La ilustracin siguiente muestra el efecto de resplandor exterior creado en el ejemplo anterior.

MCT: Luis Dueas

Pag 322 de 473

Manual de Windows Presentation Foundation

En el ejemplo siguiente se muestra cmo utilizar la clase Style para aplicar OuterGlowBitmapEffect a cualquier
TextBox de la pgina que recibe el foco.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Resources define Styles for the entire page. -->
<Page.Resources>
<!-- This style applies to any TextBox on the page. -->
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<!-- When the TextBox gains focus such as when the cursor appears
in the TextBox, apply the OuterGlowBitmapEffect to the TextBox. -->
<Trigger Property="IsFocused" Value="True">
<Setter Property = "BitmapEffect" >
<Setter.Value>
<!-- The OuterGlow is blue, extends out 30 pixels, has the
maximum noise possible, and is 40% Opaque. -->
<OuterGlowBitmapEffect GlowColor="Blue" GlowSize="30" Noise="1"
Opacity="0.4" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Page.Resources>
<StackPanel>
<!-- The Style defined above applies to this TextBox which creates
an outer glow around the it. -->
<TextBox Name="textBox1" Width="200" />
</StackPanel>
</Page>
En el ejemplo siguiente se muestra cmo utilizar marcado con cdigo subyacente para aplicar OuterGlowBitmap
Effect a TextBox. El efecto de resplandor aparece cuando TextBox recibe el foco. En este ejemplo se muestra el
marcado.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.OuterGlowExample" >
<StackPanel>
<!-- When this TextBox gains focus, a blue glow surrounds it. -->
<TextBox Width="200" GotFocus="OnFocusCreateGlow"></TextBox>
</StackPanel>
</Page>
En el ejemplo siguiente se muestra el cdigo subyacente que controla el evento para el marcado anterior.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Media.Effects;
namespace SDKSample
{
public partial class OuterGlowExample : Page
{
// Add OuterGlow effect.
void OnFocusCreateGlow(object sender, RoutedEventArgs args)
{
// Get a reference to the TextBox.
TextBox myTextBox = (TextBox)sender;
// Initialize a new OuterGlowBitmapEffect that will be applied
// to the TextBox.
OuterGlowBitmapEffect myGlowEffect = new OuterGlowBitmapEffect();
// Set the size of the glow to 30 pixels.
myGlowEffect.GlowSize = 30;
// Set the color of the glow to blue.
Color myGlowColor = new Color();
myGlowColor.ScA = 1;
myGlowColor.ScB = 1;
myGlowColor.ScG = 0;
myGlowColor.ScR = 0;
myGlowEffect.GlowColor = myGlowColor;
// Set the noise of the effect to the maximum possible (range 0-1).
myGlowEffect.Noise = 1;

MCT: Luis Dueas

Pag 323 de 473

Manual de Windows Presentation Foundation


// Set the Opacity of the effect to 40%. Note that the same effect
// could be done by setting the ScA property of the Color to 0.4.
myGlowEffect.Opacity = 0.4;
// Apply the bitmap effect to the TextBox.
myTextBox.BitmapEffect = myGlowEffect;
}
}

En el ejemplo siguiente se muestra cmo animar la propiedad GlowSize de OuterGlowBitmapEffect para hacer
que el resplandor se anime hacia fuera cuando TextBox recibe el foco.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel>
<TextBox Width="200">
<TextBox.BitmapEffect>
<!-- This BitmapEffect is targeted by the animation. -->
<OuterGlowBitmapEffect x:Name="myOuterGlowBitmapEffect"
GlowColor="Blue" GlowSize="0" />
</TextBox.BitmapEffect>
<TextBox.Triggers>
<EventTrigger RoutedEvent="TextBox.GotFocus">
<BeginStoryboard>
<Storyboard>
<!-- Animate the GlowSize from 0 to 40 over half a second. -->
<DoubleAnimation Storyboard.TargetName="myOuterGlowBitmapEffect"
Storyboard.TargetProperty="GlowSize" From="0" To="40" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBox.Triggers>
</TextBox>
</StackPanel>
</Page>

7.3.1.2.2. Cmo: Animar un Efecto de Resplandor


En este tema se describe cmo animar la propiedad GlowSize del efecto OuterGlowBitmapEffect.
Ejemplo
En el ejemplo siguiente se animan los cambios a la propiedad GlowSize del efecto OuterGlowBitmapEffect.
Como resultado, el resplandor se anima hacia el exterior cuando TextBox obtiene el foco.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel>
<TextBox Width="200">
<TextBox.BitmapEffect>
<!-- This BitmapEffect is targeted by the animation. -->
<OuterGlowBitmapEffect x:Name="myOuterGlowBitmapEffect"
GlowColor="Blue" GlowSize="0" />
</TextBox.BitmapEffect>
<TextBox.Triggers>
<EventTrigger RoutedEvent="TextBox.GotFocus">
<BeginStoryboard>
<Storyboard>
<!-- Animate the GlowSize from 0 to 40 over half a second. -->
<DoubleAnimation Storyboard.TargetName="myOuterGlowBitmapEffect"
Storyboard.TargetProperty="GlowSize" From="0" To="40" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBox.Triggers>
</TextBox>
</StackPanel>
</Page>

7.3.1.2.3. Cmo: Animar Efectos de Mapa de Bits


En

el

ejemplo

siguiente

se

muestra

cmo

animar

las

propiedades

ShadowDepth

Softness

de

DropShadowBitmapEffect y la propiedad Radius de BlurBitmapEffect para crear la ilusin de que el botn se


eleva desde la pantalla.
Ejemplo
using
using
using
using

System;
System.Windows;
System.Windows.Controls;
System.Windows.Media;

MCT: Luis Dueas

Pag 324 de 473

Manual de Windows Presentation Foundation


using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
namespace SDKSample
{
public partial class MultipleEffectAnimationExample : Page
{
public MultipleEffectAnimationExample()
{
// Create a NameScope for this page so that
// Storyboards can be used.
NameScope.SetNameScope(this, new NameScope());
Button myButton = new Button();
myButton.Content = "Click Me to Animate Drop Shadow!";
myButton.Margin = new Thickness(50);
myButton.Width = 300;
ScaleTransform myScaleTransform = new ScaleTransform();
// Assign the ScaleTransform a name so that
// it can be targeted by a Storyboard.
this.RegisterName("MyAnimatedScaleTransform", myScaleTransform);
myScaleTransform.ScaleX = 1;
myScaleTransform.ScaleY = 1;
myScaleTransform.CenterX = 100;
// Associate the transform to the button.
myButton.RenderTransform = myScaleTransform;
// Create BitmapEffects that will be animated.
BlurBitmapEffect myBlurBitmapEffect = new BlurBitmapEffect();
// Assign the BlurBitmapEffect a name so that
// it can be targeted by a Storyboard.
this.RegisterName("myBlurBitmapEffect", myBlurBitmapEffect);
myBlurBitmapEffect.Radius = 0;
myBlurBitmapEffect.KernelType = KernelType.Box;
DropShadowBitmapEffect myDropShadowBitmapEffect = new DropShadowBitmapEffect();
// Assign the BlurBitmapEffect a name so that
// it can be targeted by a Storyboard.
this.RegisterName("myDropShadowBitmapEffect", myDropShadowBitmapEffect);
myDropShadowBitmapEffect.Color = Colors.Black;
myDropShadowBitmapEffect.ShadowDepth = 0;
BitmapEffectGroup myBitmapEffectGroup = new BitmapEffectGroup();
myBitmapEffectGroup.Children.Add(myBlurBitmapEffect);
myBitmapEffectGroup.Children.Add(myDropShadowBitmapEffect);
myButton.BitmapEffect = myBitmapEffectGroup;
// Create an animation to animate the ScaleX property of the
// ScaleTransform applied to the button.
DoubleAnimation myDoubleAnimationScaleX = new DoubleAnimation();
myDoubleAnimationScaleX.Duration = TimeSpan.FromSeconds(1);
myDoubleAnimationScaleX.AutoReverse = true;
myDoubleAnimationScaleX.To = 5;
// Set the animation to target the ScaleX property of
// the object named "MyAnimatedScaleTransform. This makes the
// button get larger and smaller on the horizontal axis."
Storyboard.SetTargetName(myDoubleAnimationScaleX, "MyAnimatedScaleTransform");
Storyboard.SetTargetProperty(
myDoubleAnimationScaleX, new PropertyPath(ScaleTransform.ScaleXProperty));
// Set the animation to target the ScaleY property of
// the object named "MyAnimatedScaleTransform. This makes the
// button get larger and smaller on the vertical axis."
DoubleAnimation myDoubleAnimationScaleY = new DoubleAnimation();
myDoubleAnimationScaleY.Duration = TimeSpan.FromSeconds(1);
myDoubleAnimationScaleY.AutoReverse = true;
myDoubleAnimationScaleY.To = 5;
Storyboard.SetTargetName(myDoubleAnimationScaleY, "MyAnimatedScaleTransform");
Storyboard.SetTargetProperty(myDoubleAnimationScaleY, new
PropertyPath(ScaleTransform.ScaleYProperty));
// Set the animation to target the ShadowDepth property of
// the object named "myDropShadowBitmapEffect. This makes the
// button appear to be lifting off the screen as its shadow moves."
DoubleAnimation myDoubleAnimationShadowDepth = new DoubleAnimation();
myDoubleAnimationShadowDepth.Duration = TimeSpan.FromSeconds(1);
myDoubleAnimationShadowDepth.AutoReverse = true;
myDoubleAnimationShadowDepth.From = 0;
myDoubleAnimationShadowDepth.To = 50;
Storyboard.SetTargetName(myDoubleAnimationShadowDepth, "myDropShadowBitmapEffect");
Storyboard.SetTargetProperty(myDoubleAnimationShadowDepth, new
PropertyPath(DropShadowBitmapEffect.ShadowDepthProperty));
// Animate the blur to make the object appear to
// be comming out of the screen. Use a spline key
// frame to make the blur animate suddenly at the
// very end of the animation.
DoubleAnimationUsingKeyFrames myDoubleAnimationUsingKeyFrames = new
DoubleAnimationUsingKeyFrames();
myDoubleAnimationUsingKeyFrames.AutoReverse = true;
// Set the animation to target the Radius property
// of the object named "myBlurBitmapEffect."
Storyboard.SetTargetName(myDoubleAnimationUsingKeyFrames, "myBlurBitmapEffect");
Storyboard.SetTargetProperty(myDoubleAnimationUsingKeyFrames, new
PropertyPath(BlurBitmapEffect.RadiusProperty));

MCT: Luis Dueas

Pag 325 de 473

Manual de Windows Presentation Foundation


myDoubleAnimationUsingKeyFrames.KeyFrames.Add(new SplineDoubleKeyFrame(
30, // Target value (KeyValue)
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(1)), // KeyTime
new KeySpline(0.6, 0.0, 0.9, 0.0) // KeySpline
)
);
// Create a storyboard and add the animations to it.
Storyboard myStoryboard = new Storyboard();
myStoryboard.Children.Add(myDoubleAnimationScaleX);
myStoryboard.Children.Add(myDoubleAnimationScaleY);
myStoryboard.Children.Add(myDoubleAnimationShadowDepth);
myStoryboard.Children.Add(myDoubleAnimationUsingKeyFrames);
// Start the storyboard when button is clicked.
myButton.Click += delegate(object sender, RoutedEventArgs e)
{
myStoryboard.Begin(this);
};
StackPanel myStackPanel = new StackPanel();
myStackPanel.Children.Add(myButton);
this.Content = myStackPanel;
}
}

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel>
<Button Margin="50" Width="200" Name="myButton">
Click Me to Animate Drop Shadow!
<Button.RenderTransform>
<ScaleTransform x:Name="MyAnimatedScaleTransform"
ScaleX="1" ScaleY="1" CenterX="100" />
</Button.RenderTransform>
<Button.BitmapEffect>
<BitmapEffectGroup>
<BlurBitmapEffect x:Name="myBlurBitmapEffect" Radius="0" KernelType="Box" />
<DropShadowBitmapEffect x:Name="myDropShadowBitmapEffect" Color="Black"
ShadowDepth="0" />
</BitmapEffectGroup>
</Button.BitmapEffect>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<!-- Animate the ScaleX property to make the button
get larger and smaller in the horizontal axis. -->
<DoubleAnimation
Storyboard.TargetName="MyAnimatedScaleTransform"
Storyboard.TargetProperty="ScaleX"
To="5.0" Duration="0:0:1" AutoReverse="True" />
<!-- Animate the ScaleY property to make the button
get larger and smaller in the vertical axis. -->
<DoubleAnimation Storyboard.TargetName="MyAnimatedScaleTransform"
Storyboard.TargetProperty="ScaleY"
To="5.0" Duration="0:0:1" AutoReverse="True" />
<!-- Animate the blur to make the object appear to
be comming out of the screen. Use a spline key
frame to make the blur animate suddenly at the
very end of the animation. -->
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="myBlurBitmapEffect"
Storyboard.TargetProperty="Radius" AutoReverse="True">
<DoubleAnimationUsingKeyFrames.KeyFrames>
<SplineDoubleKeyFrame KeySpline="0.6,0.0 0.9,0.00" Value="30" KeyTime="0:0:1"/>
</DoubleAnimationUsingKeyFrames.KeyFrames>
</DoubleAnimationUsingKeyFrames>
<!-- Animate shadow depth of the effect. -->
<DoubleAnimation Storyboard.TargetName="myDropShadowBitmapEffect"
Storyboard.TargetProperty="ShadowDepth"
From="0" To="50" Duration="0:0:1" AutoReverse="True" />
<!-- Animate shadow softness. As the object gets
farther away, the shadow gets softer. -->
<DoubleAnimation
Storyboard.TargetName="myDropShadowBitmapEffect"
Storyboard.TargetProperty="Softness"
From="0" To="1" Duration="0:0:1"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
</Page>

MCT: Luis Dueas

Pag 326 de 473

Manual de Windows Presentation Foundation

7.3.1.2.4. Cmo: Aplicar un Efecto de Desenfoque a un Objeto Visual


BlurBitmapEffect se puede utilizar para desenfocar un objeto visible. A continuacin figuran varios ejemplos que
muestran lo siguiente:

Cmo utilizar el marcado simple para aplicar el efecto a un objeto


Cmo utilizar un estilo Style para aplicar el efecto a uno o ms objetos
Cmo utilizar el cdigo para aplicar el efecto a un objeto
Cmo utilizar una animacin para animar las propiedades de un efecto aplicado a un objeto

Nota: en todos los ejemplos siguientes se aplica un solo efecto a un objeto. Para aplicar varios efectos, puede
utilizar BitmapEffectGroup.
Ejemplo
En el ejemplo siguiente se muestra cmo utilizar un efecto BlurBitmapEffect para crear un control Button
desenfocado.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Button Width="200">You Can't Read This!
<Button.BitmapEffect>
<!-- <BitmapEffectGroup> would go here if you wanted to apply more
then one effect to the Button. However, in this example only
one effect is being applied so BitmapEffectGroup does not need
to be included. -->
<!-- The larger the Radius, the more blurring. The default range is 20.
In addition, the KernelType is set to a box kernel. A box kernel
creates less disruption (less blur) then the default Gaussian kernel. -->
<BlurBitmapEffect Radius="10" KernelType="Box" />
</Button.BitmapEffect>
</Button>
</StackPanel>
</Page>
En la ilustracin siguiente se muestra el efecto creado en el ejemplo anterior.

En el ejemplo siguiente se muestra cmo utilizar Style para aplicar el efecto BlurBitmapEffect a un objeto
Button en la pgina mientras se presiona.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Resources define Styles for the entire page. -->
<Page.Resources>
<!-- This style applies to any Button on the page. -->
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<!-- When the Button is pressed, apply the blur. -->
<Trigger Property="IsPressed" Value="true">
<Setter Property = "BitmapEffect" >
<Setter.Value>
<BlurBitmapEffect Radius="10" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Page.Resources>
<StackPanel>
<!-- The Style defined above applies to this Button which makes
the Button appear blurred while it is pressed. -->
<Button Width="200" >Blurning down the House!</Button>
</StackPanel>
</Page>
En el ejemplo siguiente se muestra cmo utilizar el cdigo para aplicar un efecto BlurBitmapEffect a un objeto
Button cuando se hace clic en l.
A continuacin se muestra el marcado Lenguaje de marcado de aplicaciones extensible (XAML) del ejemplo.

MCT: Luis Dueas

Pag 327 de 473

Manual de Windows Presentation Foundation


<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.BlurExample" >
<StackPanel>
<Button Click="OnClickBlurButton" Width="200">Click to Blur!</Button>
</StackPanel>
</Page>
El ejemplo siguiente es el cdigo que controla el evento para el marcado Lenguaje de marcado de aplicaciones
extensible (XAML).
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Media.Effects;
namespace SDKSample
{
public partial class BlurExample : Page
{
// Add Blur effect.
void OnClickBlurButton(object sender, RoutedEventArgs args)
{
// Toggle effect
if (((Button)sender).BitmapEffect != null)
{
((Button)sender).BitmapEffect = null;
}
else
{
// Get a reference to the Button.
Button myButton = (Button)sender;
// Initialize a new BlurBitmapEffect that will be applied
// to the Button.
BlurBitmapEffect myBlurEffect = new BlurBitmapEffect();
// Set the Radius property of the blur. This determines how
// blurry the effect will be. The larger the radius, the more
// blurring.
myBlurEffect.Radius = 10;
// Set the KernelType property of the blur. A KernalType of "Box"
// creates less blur than the Gaussian kernal type.
myBlurEffect.KernelType = KernelType.Box;
// Apply the bitmap effect to the Button.
myButton.BitmapEffect = myBlurEffect;
}
}
}
}
En el ejemplo siguiente se muestra cmo animar la propiedad Radius de BlurBitmapEffect para que el objeto
Button se desenfoque despus de hacer clic en l.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel>
<Button Width="200">
Click to Blur ME!
<Button.BitmapEffect>
<!-- This BitmapEffect is targeted by the animation. -->
<BlurBitmapEffect x:Name="myBlurBitmapEffect" Radius="0" />
</Button.BitmapEffect>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<!-- Blur the Button and then animate back to normal. -->
<DoubleAnimation Storyboard.TargetName="myBlurBitmapEffect"
Storyboard.TargetProperty="Radius" From="0" To="40" Duration="0:0:0.3"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
</Page>

7.3.1.2.5. Cmo: Animar un Efecto Visual de Desenfoque


En el ejemplo siguiente se muestra cmo animar la propiedad Radius de BlurBitmapEffect para que el objeto
Button se desenfoque despus de hacer clic en l.

MCT: Luis Dueas

Pag 328 de 473

Manual de Windows Presentation Foundation


Ejemplo
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel>
<Button Width="200">
Click to Blur ME!
<Button.BitmapEffect>
<!-- This BitmapEffect is targeted by the animation. -->
<BlurBitmapEffect x:Name="myBlurBitmapEffect" Radius="0" />
</Button.BitmapEffect>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<!-- Blur the Button and then animate back to normal. -->
<DoubleAnimation Storyboard.TargetName="myBlurBitmapEffect"
Storyboard.TargetProperty="Radius" From="0" To="40" Duration="0:0:0.3"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
</Page>

7.3.1.2.6. Cmo: Crear un Efecto Visual de Sombra Paralela


DropShadowBitmapEffect se puede utilizar para crear algo parecido a una sombra por detrs de un objeto
visible. A continuacin figuran varios ejemplos que muestran lo siguiente:

Cmo utilizar el marcado simple para aplicar el efecto a un objeto


Cmo utilizar un estilo Style para aplicar el efecto a uno o ms objetos
Cmo utilizar el cdigo para aplicar el efecto a un objeto
Cmo utilizar una animacin para animar las propiedades de un efecto aplicado a un objeto

Nota: en todos los ejemplos siguientes se aplica un solo efecto a un objeto. Para aplicar varios efectos, puede
utilizar BitmapEffectGroup.
Ejemplo
En el ejemplo siguiente se muestra cmo utilizar DropShadowBitmapEffect para crear el aspecto de una sombra
por detrs de un objeto Button.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Button Margin="50" Width="200">DropShadow Under this Button
<Button.BitmapEffect>
<!-- <BitmapEffectGroup> would go here if you wanted to apply more
then one effect to the TextBox. However, in this example only
one effect is being applied so BitmapEffectGroup does not need
to be included. -->
<!-- The DropShadowBitmapEffect has several important properties that
determine characteristics of the drop shadow:
- Color: Specifies the color of the drop shadow (in this example black).
- ShadowDepth: Specifies how far displaced the shadow is from the object
casting the shadow. Default is 20.
- Direction: Specifies in what direction the shadow is cast from the object.
It is an angle between 0 and 360 with 0 starting on the right hand side
and moving counter-clockwise around the object. The value of 320 in this
example casts the shadow on the lower right hand side of the button.
- Noise: Specifies how grainy the drop-shadow is. Range is between 0 and 1.
Default is 0.
- Softness: Specifies how soft the shadow. The range is between 0 and 1 with 1
being the softest. Default is 0.5.
- Opacity: Specifies how transparent the shadow is. The range is between 0
and 1 with 1 being fully opaque and 0 fully transparent (not visible). The
default is 1. -->
<DropShadowBitmapEffect Color="Black" Direction="320" ShadowDepth="25"
Softness="1" Opacity="0.5"/>
</Button.BitmapEffect>
</Button>
</StackPanel>
</Page>
En la ilustracin siguiente se muestra el efecto creado en el ejemplo anterior.

MCT: Luis Dueas

Pag 329 de 473

Manual de Windows Presentation Foundation

En el ejemplo siguiente se muestra cmo utilizar Style para aplicar el efecto DropShadowBitmapEffect a un
objeto Button en la pgina mientras se presiona.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Resources define Styles for the entire page. -->
<Page.Resources>
<!-- This style applies to any Button on the page. -->
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<!-- When the Button is pressed, apply the drop shadow. -->
<Trigger Property="IsPressed" Value="true">
<Setter Property = "BitmapEffect" >
<Setter.Value>
<DropShadowBitmapEffect Color="Black" Direction="320"
ShadowDepth="25" Softness="1" Opacity="0.5"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Page.Resources>
<StackPanel>
<!-- The Style defined above applies to this Button which creates
a drop shadow when the button is pressed down. -->
<Button Width="200" >Press Me!</Button>
</StackPanel>
</Page>
En el ejemplo siguiente se muestra cmo utilizar el cdigo para aplicar un efecto DropShadowBitmapEffect a un
objeto Button cuando se hace clic en l.
A continuacin se muestra el marcado Lenguaje de marcado de aplicaciones extensible (XAML) del ejemplo.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.DropShadowExample" >
<StackPanel>
<Button Click="OnClickDropShadowButton" Margin="50" Width="200">
Click to Create Drop Shadow!</Button>
</StackPanel>
</Page>
El ejemplo siguiente es el cdigo que controla el evento para el marcado del ejemplo.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Media.Effects;
namespace SDKSample
{
public partial class DropShadowExample : Page
{
// Add Blur effect.
void OnClickDropShadowButton(object sender, RoutedEventArgs args)
{
// Get a reference to the Button.
Button myButton = (Button)sender;
// Initialize a new DropShadowBitmapEffect that will be applied
// to the Button.
DropShadowBitmapEffect myDropShadowEffect = new DropShadowBitmapEffect();
// Set the color of the shadow to Black.
Color myShadowColor = new Color();
myShadowColor.ScA = 1;
// Note that the alpha value is ignored by Color property. The Opacity
// property is used to control the alpha.
myShadowColor.ScB = 0;
myShadowColor.ScG = 0;
myShadowColor.ScR = 0;
myDropShadowEffect.Color = myShadowColor;
// Set the direction of where the shadow is cast to 320 degrees.
myDropShadowEffect.Direction = 320;
// Set the depth of the shadow being cast.
myDropShadowEffect.ShadowDepth = 25;
// Set the shadow softness to the maximum (range of 0-1).

MCT: Luis Dueas

Pag 330 de 473

Manual de Windows Presentation Foundation


myDropShadowEffect.Softness = 1;
// Set the shadow opacity to half opaque or in other words - half transparent
// The range is 0-1.
myDropShadowEffect.Opacity = 0.5;
// Apply the bitmap effect to the Button.
myButton.BitmapEffect = myDropShadowEffect;
}

}
En el ejemplo siguiente se muestra cmo animar las propiedades ShadowDepth y Softness de DropShadow
BitmapEffect para que parezca que el objeto Button se eleva sobre la superficie de la pantalla despus de hace
clic en l.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel>
<Button Margin="50" Width="200" Name="myButton">
Click Me to Animate Drop Shadow!
<Button.BitmapEffect>
<!-- This BitmapEffect is targeted by the animation. -->
<DropShadowBitmapEffect x:Name="myDropShadowBitmapEffect" Color="Black"
ShadowDepth="0" />
</Button.BitmapEffect>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<!-- Animate the movement of the button. -->
<ThicknessAnimation
Storyboard.TargetProperty="Margin" Duration="0:0:0.5"
From="50,50,50,50" To="0,0,50,50" AutoReverse="True" />
<!-- Animate shadow depth of the effect. -->
<DoubleAnimation Storyboard.TargetName="myDropShadowBitmapEffect"
Storyboard.TargetProperty="ShadowDepth"
From="0" To="30" Duration="0:0:0.5" AutoReverse="True" />
<!-- Animate shadow softness of the effect. As
the Button appears to get farther from the shadow,
the shadow gets softer. -->
<DoubleAnimation Storyboard.TargetName="myDropShadowBitmapEffect"
Storyboard.TargetProperty="Softness" From="0" To="1" Duration="0:0:0.5"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
</Page>

7.3.1.2.7. Cmo: Animar un Efecto Visual de Sombra Paralela


En

el

ejemplo

siguiente

se

muestra

cmo

animar

las

propiedades

ShadowDepth

Softness

de

DropShadowBitmapEffect para que parezca que el objeto Button se eleva sobre la superficie de la pantalla
despus de hace clic en l.
Ejemplo
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel>
<Button Margin="50" Width="200" Name="myButton">
Click Me to Animate Drop Shadow!
<Button.BitmapEffect>
<!-- This BitmapEffect is targeted by the animation. -->
<DropShadowBitmapEffect x:Name="myDropShadowBitmapEffect" Color="Black"
ShadowDepth="0" />
</Button.BitmapEffect>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<!-- Animate the movement of the button. -->
<ThicknessAnimation Storyboard.TargetProperty="Margin" Duration="0:0:0.5"
From="50,50,50,50" To="0,0,50,50" AutoReverse="True" />
<!-- Animate shadow depth of the effect. -->
<DoubleAnimation Storyboard.TargetName="myDropShadowBitmapEffect"
Storyboard.TargetProperty="ShadowDepth"
From="0" To="30" Duration="0:0:0.5" AutoReverse="True" />
<!-- Animate shadow softness of the effect. As
the Button appears to get farther from the shadow,
the shadow gets softer. -->

MCT: Luis Dueas

Pag 331 de 473

Manual de Windows Presentation Foundation


<DoubleAnimation Storyboard.TargetName="myDropShadowBitmapEffect"
Storyboard.TargetProperty="Softness" From="0" To="1" Duration="0:0:0.5"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
</Page>

7.3.1.2.8. Cmo: Crear un Efecto Visual con Bisel


BevelBitmapEffect se puede utilizar para crear un bisel interno que eleve la superficie de un objeto visible de
acuerdo con una curva especificada (establecida por la propiedad EdgeProfile). A continuacin figuran varios
ejemplos que muestran lo siguiente:

Cmo utilizar el marcado simple para aplicar el efecto a un objeto


Cmo utilizar un estilo Style para aplicar el efecto a uno o ms objetos
Cmo utilizar el cdigo para aplicar el efecto a un objeto
Cmo utilizar una animacin para animar las propiedades de un efecto aplicado a un objeto

Nota: en todos los ejemplos siguientes se aplica un solo efecto a un objeto. Para aplicar varios efectos, puede
utilizar BitmapEffectGroup.
Ejemplo
En el ejemplo siguiente se muestra cmo utilizar un efecto BevelBitmapEffect para crear un bisel dentro de un
control Button.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Button Width="200" Height="80" Margin="50">
Bevelled Button
<Button.BitmapEffect>
<!-- <BitmapEffectGroup> would go here if you wanted to apply more
then one effect to the TextBox. However, in this example only
one effect is being applied so BitmapEffectGroup does not need
to be included. -->
<!-- The BevelBitmapEffect has several important properties that
determine characteristics of the bevel effect:
- BevelWidth: Specifies how wide the bevel is (size of the bevel). In this
example, the bevel is fairly wide at 15 (default is 5).
- EdgeProfile: Specifies the curve of the bevel. The default is "Linear".
- LightAngle: Specifies in what direction the "virtual light" is coming from
that create the shadows of the bevel. It is an angle between 0 and 360 with 0
starting on the right hand side and moving counter-clockwise around the object.
The shadows of the bevel are on the opposite side of where the light is cast.
The value of 320 in this example casts the light on the lower right hand side
of the bevel and shadow on the upper left.
- Relief: Specifies the height of the relief of the bevel. Range is between 0 and 1
with 1 having the most relief (darkest shadows). The default is 0.3.
- Smoothness: Specifies how smooth the shadows are. The range is between 0 and 1
with 1 being the softest. Default is 0.5. -->
<BevelBitmapEffect BevelWidth="15" EdgeProfile="CurvedIn" LightAngle="320"
Relief="0.4" Smoothness="0.4" />
</Button.BitmapEffect>
</Button>
</StackPanel>
</Page>
En la ilustracin siguiente se muestra el efecto creado en el ejemplo anterior.

En el ejemplo siguiente se muestra cmo utilizar un estilo Style para aplicar un efecto BevelBitmapEffect a
cualquiera de los objetos Button de la pgina cuando el puntero del mouse se mueve sobre l. Adems, cuando

MCT: Luis Dueas

Pag 332 de 473

Manual de Windows Presentation Foundation


se presiona el botn, se aplica otro efecto BevelBitmapEffect con un valor de BevelWidth diferente, lo que hace
que parezca que el botn se empuja hacia abajo.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Resources define Styles for the entire page. -->
<Page.Resources>
<!-- This style applies to any Button on the page. -->
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<!-- When the mouse pointer moves over the button, apply a bevel
with a wide BevelWidth. -->
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BitmapEffect" >
<Setter.Value>
<BevelBitmapEffect BevelWidth="15" />
</Setter.Value>
</Setter>
</Trigger>
<!-- When the mouse pointer is pressed, apply a bevel with a
narrower BevelWidth to make the button appear to get pressed. -->
<Trigger Property="IsPressed" Value="true">
<Setter Property="BitmapEffect" >
<Setter.Value>
<BevelBitmapEffect BevelWidth="5" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Page.Resources>
<StackPanel>
<!-- The Style defined above applies to this Button which makes
the Button become beveled while it is pressed. -->
<Button Width="200" Height="80" Margin="50">Press to Bevel</Button>
</StackPanel>
</Page>
En el ejemplo siguiente se muestra cmo utilizar el cdigo para aplicar un efecto BevelBitmapEffect a un objeto
Button cuando el puntero del mouse se mueve sobre l.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.BevelExample" >
<StackPanel>
<Button MouseEnter="OnMouseOverBevelButton" Width="200" Height="80" Margin="50">
MouseOver to Bevel!
</Button>
</StackPanel>
</Page>
En el cdigo siguiente se controla el evento del marcado anterior.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Media.Effects;
namespace SDKSample
{
public partial class BevelExample : Page
{
// Add Bevel effect.
void OnMouseOverBevelButton(object sender, RoutedEventArgs args)
{
// Get a reference to the Button.
Button myButton = (Button)sender;
// Initialize a new BevelBitmapEffect that will be applied
// to the Button.
BevelBitmapEffect myBevelEffect = new BevelBitmapEffect();
// Set the BevelWidth. The default for BevelWidth is 5.
myBevelEffect.BevelWidth = 15;
// Set the EdgeProfile. The default value is Linear.
myBevelEffect.EdgeProfile = EdgeProfile.CurvedIn;
// Set the LightAngle (direction where light is coming from) to 320 degrees.
myBevelEffect.LightAngle = 320;
// Set the Relief to an intermediate value of 0.5. Relief specifies the relief
// between light and shadow for the bevel. The default value is 0.3.
myBevelEffect.Relief = 0.4;
// Set the Smoothness. The default value is 0.5. This example sets
// the property to the maximum value which is 1.
myBevelEffect.Smoothness = 0.4;
// Apply the bitmap effect to the Button.
myButton.BitmapEffect = myBevelEffect;
}

MCT: Luis Dueas

Pag 333 de 473

Manual de Windows Presentation Foundation

En el ejemplo siguiente se muestra cmo animar las propiedades BevelWidth y LightAngle del efecto
BevelBitmapEffect para que, cuando el puntero del mouse se mueva sobre el objeto Button, suba el nivel
interior del bisel y la fuente de iluminacin artificial utilizada para el bisel gire alrededor de Button.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel>
<Button Width="200" Height="80" Margin="50">
MouseOver ME!
<Button.BitmapEffect>
<!-- This BitmapEffect is targeted by the animation. -->
<BevelBitmapEffect x:Name="myBevelBitmapEffect" BevelWidth="0"
EdgeProfile="CurvedIn" />
</Button.BitmapEffect>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.MouseEnter">
<BeginStoryboard>
<Storyboard>
<!-- Animate the BevelWidth from 0 to 15. -->
<DoubleAnimation Storyboard.TargetName="myBevelBitmapEffect"
Storyboard.TargetProperty="BevelWidth"
From="0" To="15" Duration="0:0:0.5" AutoReverse="True" />
<!-- Animate the LightAngle so that the light source and
corresponding bevel shadows move around the button. -->
<DoubleAnimation Storyboard.TargetName="myBevelBitmapEffect"
Storyboard.TargetProperty="LightAngle"
From="360" To="0" Duration="0:0:0.5" AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
</Page>

7.3.1.2.9. Cmo: Animar un Efecto Visual con Bisel


En este ejemplo se muestra cmo animar un objeto BevelBitmapEffect en un botn.
Ejemplo
En el ejemplo siguiente se muestra cmo animar las propiedades BevelWidth y LightAngle de un objeto
BevelBitmapEffect. Como resultado, cuando el puntero del mouse se mueve sobre el control Button, el nivel
interno del bisel mientras la fuente de luz artificial que se utiliza para el bisel gira alrededor del control Button.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel>
<Button Width="200" Height="80" Margin="50">
MouseOver ME!
<Button.BitmapEffect>
<!-- This BitmapEffect is targeted by the animation. -->
<BevelBitmapEffect x:Name="myBevelBitmapEffect" BevelWidth="0"
EdgeProfile="CurvedIn" />
</Button.BitmapEffect>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.MouseEnter">
<BeginStoryboard>
<Storyboard>
<!-- Animate the BevelWidth from 0 to 15. -->
<DoubleAnimation Storyboard.TargetName="myBevelBitmapEffect"
Storyboard.TargetProperty="BevelWidth"
From="0" To="15" Duration="0:0:0.5" AutoReverse="True" />
<!-- Animate the LightAngle so that the light source and
corresponding bevel shadows move around the button. -->
<DoubleAnimation Storyboard.TargetName="myBevelBitmapEffect"
Storyboard.TargetProperty="LightAngle"
From="360" To="0" Duration="0:0:0.5" AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
</Page>

7.3.1.2.10. Cmo: Crear un Efecto Visual con Relieves

MCT: Luis Dueas

Pag 334 de 473

Manual de Windows Presentation Foundation


EmbossBitmapEffect se puede utilizar para crear un mapa de rugosidad del objeto visual a fin de dar la
impresin de la profundidad y la textura procedentes de una fuente de luz artificial. A continuacin figuran
varios ejemplos que muestran lo siguiente:

Cmo utilizar el marcado simple para aplicar el efecto a un objeto


Cmo utilizar un estilo Style para aplicar el efecto a uno o ms objetos
Cmo utilizar el cdigo para aplicar el efecto a un objeto
Cmo utilizar una animacin para animar las propiedades de un efecto aplicado a un objeto

Nota: en todos los ejemplos siguientes se aplica un solo efecto a un objeto. Para aplicar varios efectos, puede
utilizar BitmapEffectGroup.
Ejemplo
En el ejemplo siguiente se muestra cmo utilizar un efecto EmbossBitmapEffect para crear una imagen con
relieves.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Image Width="360" Source="/images/WaterLilies.jpg" Margin="10" >
<Image.BitmapEffect>
<!-- <BitmapEffectGroup> would go here if you wanted to apply more
then one effect to the TextBox. However, in this example only
one effect is being applied so BitmapEffectGroup does not need
to be included. -->
<!-- The Relief property determines the amount of relief of the emboss.
The valid range of values is 0-1 with 0 having the least relief and
1 having the most. The default value is 0.44. The LightAngle determines
from what direction the artificial light is cast upon the embossed
object which effects shadowing. The valid range is from 0-360 (degrees)
with 0 specifying the right-hand side of the object and successive values
moving counter-clockwise around the object. -->
<EmbossBitmapEffect Relief="0.8" LightAngle="320" />
</Image.BitmapEffect>
</Image>
</StackPanel>
</Page>
En el ejemplo siguiente se muestra cmo utilizar el objeto Style para aplicar EmbossBitmapEffect a cualquier
Image de la pgina.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Resources define Styles for the entire page. -->
<Page.Resources>
<!-- This style applies to any Image on the page. -->
<Style TargetType="{x:Type Image}">
<Setter Property="BitmapEffect" >
<Setter.Value>
<EmbossBitmapEffect Relief="0.8" />
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<StackPanel>
<!-- The Style defined above applies to this Image which applies
the EmbossBitmapEffect. -->
<Image Width="360" Source="/images/WaterLilies.jpg" Margin="10" />
</StackPanel>
</Page>
En el ejemplo siguiente se muestra cmo utilizar el cdigo para aplicar EmbossBitmapEffect a un objeto Image
cuando se carga.
A continuacin se muestra el marcado Lenguaje de marcado de aplicaciones extensible (XAML) del ejemplo.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.EmbossExample" >
<StackPanel>
<!-- When this image loads, an EmbossBitmapEffect is applied to it. -->
<Image Width="360" Loaded="OnLoadEmbossImage" Source="/images/WaterLilies.jpg" />
</StackPanel>
</Page>
El ejemplo siguiente es el cdigo que controla el evento para el marcado.
using System;
using System.Windows;

MCT: Luis Dueas

Pag 335 de 473

Manual de Windows Presentation Foundation


using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Media.Effects;
namespace SDKSample
{
public partial class EmbossExample : Page
{
// Add Bevel effect.
void OnLoadEmbossImage(object sender, RoutedEventArgs args)
{
// Get a reference to the Button.
Image myImage = (Image)sender;
// Initialize a new BevelBitmapEffect that will be applied
// to the Button.
EmbossBitmapEffect myEmbossEffect = new EmbossBitmapEffect();
// The LightAngle determines from what direction the artificial
// light is cast upon the embossed object which effects shadowing.
// The valid range is from 0-360 (degrees)with 0 specifying the
// right-hand side of the object and successive values moving
// counter-clockwise around the object.
// Set the LightAngle to 320 degrees (lower right side).
myEmbossEffect.LightAngle = 320;
// The Relief property determines the amount of relief of the emboss.
// The valid range of values is 0-1 with 0 having the least relief and
// 1 having the most. The default value is 0.44.
myEmbossEffect.Relief = 0.8;
// Apply the bitmap effect to the Image.
myImage.BitmapEffect = myEmbossEffect;
}
}
}
En el ejemplo siguiente se muestra cmo animar la propiedad LightAngle de EmbossBitmapEffect para que la
luz artificial gire alrededor de la imagen con relieves, de manera que las sombras proyectadas por los relieves
se muevan en consecuencia.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel>
<Image Source="/images/WaterLilies.jpg" Width="600" Margin="10" >
<Image.BitmapEffect>
<EmbossBitmapEffect x:Name="myEmbossBitmapEffect" Relief="0.8" LightAngle="0"/>
</Image.BitmapEffect>
<Image.Triggers>
<EventTrigger RoutedEvent="Image.Loaded">
<BeginStoryboard>
<Storyboard>
<!-- Animate the LightAngle so that the artificial light
orbits around the embossed image which makes the
shadows cast by the emboss shift accordingly. -->
<DoubleAnimation Storyboard.TargetName="myEmbossBitmapEffect"
Storyboard.TargetProperty="LightAngle"
From="0" To="360" Duration="0:0:3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
</StackPanel>
</Page>

7.3.1.2.11. Cmo: Animar un Efecto Visual con Relieves


En este tema se explica cmo animar las propiedades de un efecto visual con relieve.
Ejemplo
En el ejemplo siguiente se anima la propiedad LightAngle de EmbossBitmapEffect de tal forma que los efectos
de sombreado resultantes de la posicin de la "luz" se colocarn encima del borde en relieve de la imagen.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel>
<Image Source="/images/WaterLilies.jpg" Width="600" Margin="10" >
<Image.BitmapEffect>
<EmbossBitmapEffect x:Name="myEmbossBitmapEffect" Relief="0.8" LightAngle="0"/>
</Image.BitmapEffect>
<Image.Triggers>
<EventTrigger RoutedEvent="Image.Loaded">
<BeginStoryboard>
<Storyboard>
<!-- Animate the LightAngle so that the artificial light

MCT: Luis Dueas

Pag 336 de 473

Manual de Windows Presentation Foundation


orbits around the embossed image which makes the
shadows cast by the emboss shift accordingly. -->
<DoubleAnimation Storyboard.TargetName="myEmbossBitmapEffect"
Storyboard.TargetProperty="LightAngle"
From="0" To="360" Duration="0:0:3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
</StackPanel>
</Page>

7.3.1.2.12. Cmo: Crear Varios Efectos Visuales


Se pueden aplicar varios efectos visuales a un mismo objeto visible mediante BitmapEffectGroup. En el ejemplo
siguiente se muestra cmo aplicar BlurBitmapEffect y DropShadowBitmapEffect para crear un botn
desenfocado con una sombra detrs de l.
Ejemplo
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Effects;
namespace SDKSample
{
public partial class MultipleEffectExample : Page
{
public MultipleEffectExample()
{
Button myButton = new Button();
myButton.Content = "DropShadow under this Button";
myButton.Margin = new Thickness(50);
myButton.Width = 300;
// Create the BitmapEffects to apply to the button.
BlurBitmapEffect myBlurBitmapEffect = new BlurBitmapEffect();
myBlurBitmapEffect.Radius = 2;
DropShadowBitmapEffect myDropShadowBitmapEffect = new DropShadowBitmapEffect();
myDropShadowBitmapEffect.Color = Colors.Black;
myDropShadowBitmapEffect.Direction = 320;
myDropShadowBitmapEffect.ShadowDepth = 30;
myDropShadowBitmapEffect.Softness = 1;
myDropShadowBitmapEffect.Opacity = 0.5;
BitmapEffectGroup myBitmapEffectGroup = new BitmapEffectGroup();
myBitmapEffectGroup.Children.Add(myBlurBitmapEffect);
myBitmapEffectGroup.Children.Add(myDropShadowBitmapEffect);
myButton.BitmapEffect = myBitmapEffectGroup;
StackPanel myStackPanel = new StackPanel();
myStackPanel.Children.Add(myButton);
this.Content = myStackPanel;
}
}
}
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Button Margin="50" Width="300">
DropShadow Under this Button
<Button.BitmapEffect>
<BitmapEffectGroup>
<BlurBitmapEffect Radius="2" />
<DropShadowBitmapEffect Color="Black" Direction="320" ShadowDepth="30"
Softness="1" Opacity="0.5"/>
</BitmapEffectGroup>
</Button.BitmapEffect>
</Button>
</StackPanel>
</Page>

7.3.1.2.13. Cmo: Animar Varios Efectos Visuales


En el ejemplo siguiente se muestra cmo animar las propiedades ShadowDepth y Softness de DropShadow
BitmapEffect y la propiedad Radius de BlurBitmapEffect para crear la ilusin de que el botn se eleva desde la
pantalla.

MCT: Luis Dueas

Pag 337 de 473

Manual de Windows Presentation Foundation


Ejemplo
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
namespace SDKSample
{
public partial class MultipleEffectAnimationExample : Page
{
public MultipleEffectAnimationExample()
{
// Create a NameScope for this page so that
// Storyboards can be used.
NameScope.SetNameScope(this, new NameScope());
Button myButton = new Button();
myButton.Content = "Click Me to Animate Drop Shadow!";
myButton.Margin = new Thickness(50);
myButton.Width = 300;
ScaleTransform myScaleTransform = new ScaleTransform();
// Assign the ScaleTransform a name so that
// it can be targeted by a Storyboard.
this.RegisterName("MyAnimatedScaleTransform", myScaleTransform);
myScaleTransform.ScaleX = 1;
myScaleTransform.ScaleY = 1;
myScaleTransform.CenterX = 100;
// Associate the transform to the button.
myButton.RenderTransform = myScaleTransform;
// Create BitmapEffects that will be animated.
BlurBitmapEffect myBlurBitmapEffect = new BlurBitmapEffect();
// Assign the BlurBitmapEffect a name so that
// it can be targeted by a Storyboard.
this.RegisterName("myBlurBitmapEffect", myBlurBitmapEffect);
myBlurBitmapEffect.Radius = 0;
myBlurBitmapEffect.KernelType = KernelType.Box;
DropShadowBitmapEffect myDropShadowBitmapEffect = new
DropShadowBitmapEffect();
// Assign the BlurBitmapEffect a name so that
// it can be targeted by a Storyboard.
this.RegisterName("myDropShadowBitmapEffect", myDropShadowBitmapEffect);
myDropShadowBitmapEffect.Color = Colors.Black;
myDropShadowBitmapEffect.ShadowDepth = 0;
BitmapEffectGroup myBitmapEffectGroup = new BitmapEffectGroup();
myBitmapEffectGroup.Children.Add(myBlurBitmapEffect);
myBitmapEffectGroup.Children.Add(myDropShadowBitmapEffect);
myButton.BitmapEffect = myBitmapEffectGroup;
// Create an animation to animate the ScaleX property of the
// ScaleTransform applied to the button.
DoubleAnimation myDoubleAnimationScaleX = new DoubleAnimation();
myDoubleAnimationScaleX.Duration = TimeSpan.FromSeconds(1);
myDoubleAnimationScaleX.AutoReverse = true;
myDoubleAnimationScaleX.To = 5;
// Set the animation to target the ScaleX property of
// the object named "MyAnimatedScaleTransform. This makes the
// button get larger and smaller on the horizontal axis."
Storyboard.SetTargetName(myDoubleAnimationScaleX, "MyAnimatedScaleTransform");
Storyboard.SetTargetProperty(myDoubleAnimationScaleX, new
PropertyPath(ScaleTransform.ScaleXProperty));
// Set the animation to target the ScaleY property of
// the object named "MyAnimatedScaleTransform. This makes the
// button get larger and smaller on the vertical axis."
DoubleAnimation myDoubleAnimationScaleY = new DoubleAnimation();
myDoubleAnimationScaleY.Duration = TimeSpan.FromSeconds(1);
myDoubleAnimationScaleY.AutoReverse = true;
myDoubleAnimationScaleY.To = 5;
Storyboard.SetTargetName(myDoubleAnimationScaleY, "MyAnimatedScaleTransform");
Storyboard.SetTargetProperty(myDoubleAnimationScaleY, new
PropertyPath(ScaleTransform.ScaleYProperty));
// Set the animation to target the ShadowDepth property of
// the object named "myDropShadowBitmapEffect. This makes the
// button appear to be lifting off the screen as its shadow moves."
DoubleAnimation myDoubleAnimationShadowDepth = new DoubleAnimation();
myDoubleAnimationShadowDepth.Duration = TimeSpan.FromSeconds(1);
myDoubleAnimationShadowDepth.AutoReverse = true;
myDoubleAnimationShadowDepth.From = 0;
myDoubleAnimationShadowDepth.To = 50;
Storyboard.SetTargetName(myDoubleAnimationShadowDepth, "myDropShadowBitmapEffect");
Storyboard.SetTargetProperty(myDoubleAnimationShadowDepth, new
PropertyPath(DropShadowBitmapEffect.ShadowDepthProperty));
// Animate the blur to make the object appear to
// be comming out of the screen. Use a spline key
// frame to make the blur animate suddenly at the
// very end of the animation.
DoubleAnimationUsingKeyFrames myDoubleAnimationUsingKeyFrames = new

MCT: Luis Dueas

Pag 338 de 473

Manual de Windows Presentation Foundation

DoubleAnimationUsingKeyFrames();
myDoubleAnimationUsingKeyFrames.AutoReverse = true;
// Set the animation to target the Radius property
// of the object named "myBlurBitmapEffect."
Storyboard.SetTargetName(myDoubleAnimationUsingKeyFrames,
"myBlurBitmapEffect");
Storyboard.SetTargetProperty(myDoubleAnimationUsingKeyFrames, new
PropertyPath(BlurBitmapEffect.RadiusProperty));
myDoubleAnimationUsingKeyFrames.KeyFrames.Add(new SplineDoubleKeyFrame(
30, // Target value (KeyValue)
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(1)), // KeyTime
new KeySpline(0.6, 0.0, 0.9, 0.0) // KeySpline
)
);
// Create a storyboard and add the animations to it.
Storyboard myStoryboard = new Storyboard();
myStoryboard.Children.Add(myDoubleAnimationScaleX);
myStoryboard.Children.Add(myDoubleAnimationScaleY);
myStoryboard.Children.Add(myDoubleAnimationShadowDepth);
myStoryboard.Children.Add(myDoubleAnimationUsingKeyFrames);
// Start the storyboard when button is clicked.
myButton.Click += delegate(object sender, RoutedEventArgs e)
{
myStoryboard.Begin(this);
};
StackPanel myStackPanel = new StackPanel();
myStackPanel.Children.Add(myButton);
this.Content = myStackPanel;

}
}
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel>
<Button Margin="50" Width="200" Name="myButton">
Click Me to Animate Drop Shadow!
<Button.RenderTransform>
<ScaleTransform x:Name="MyAnimatedScaleTransform"
ScaleX="1" ScaleY="1" CenterX="100" />
</Button.RenderTransform>
<Button.BitmapEffect>
<BitmapEffectGroup>
<BlurBitmapEffect x:Name="myBlurBitmapEffect" Radius="0" KernelType="Box" />
<DropShadowBitmapEffect x:Name="myDropShadowBitmapEffect" Color="Black"
ShadowDepth="0" />
</BitmapEffectGroup>
</Button.BitmapEffect>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<!-- Animate the ScaleX property to make the button
get larger and smaller in the horizontal axis. -->
<DoubleAnimation Storyboard.TargetName="MyAnimatedScaleTransform"
Storyboard.TargetProperty="ScaleX"
To="5.0" Duration="0:0:1" AutoReverse="True" />
<!-- Animate the ScaleY property to make the button
get larger and smaller in the vertical axis. -->
<DoubleAnimation Storyboard.TargetName="MyAnimatedScaleTransform"
Storyboard.TargetProperty="ScaleY"
To="5.0" Duration="0:0:1" AutoReverse="True" />
<!-- Animate the blur to make the object appear to
be comming out of the screen. Use a spline key
frame to make the blur animate suddenly at the
very end of the animation. -->
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="myBlurBitmapEffect"
Storyboard.TargetProperty="Radius" AutoReverse="True">
<DoubleAnimationUsingKeyFrames.KeyFrames>
<SplineDoubleKeyFrame KeySpline="0.6,0.0 0.9,0.00" Value="30"
KeyTime="0:0:1" />
</DoubleAnimationUsingKeyFrames.KeyFrames>
</DoubleAnimationUsingKeyFrames>
<!-- Animate shadow depth of the effect. -->
<DoubleAnimation Storyboard.TargetName="myDropShadowBitmapEffect"
Storyboard.TargetProperty="ShadowDepth"
From="0" To="50" Duration="0:0:1" AutoReverse="True" />
<!-- Animate shadow softness. As the object gets
farther away, the shadow gets softer. -->
<DoubleAnimation Storyboard.TargetName="myDropShadowBitmapEffect"
Storyboard.TargetProperty="Softness"
From="0" To="1" Duration="0:0:1" AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>

MCT: Luis Dueas

Pag 339 de 473

Manual de Windows Presentation Foundation


</Button.Triggers>
</Button>
</StackPanel>
</Page>

7.3.1.2.14. Cmo: Usar un Efecto Visual Personalizado


En este ejemplo se muestra cmo utilizar un efecto personalizado en Lenguaje de marcado de aplicaciones
extensible (XAML).
Los efectos personalizados se crean con API no administradas y crean una biblioteca de vnculos dinmicos
(DLL) Modelo de objetos componentes (COM). Para usar efectos personalizados en cdigo administrado, se
utiliza un ensamblado administrado para definir BitmapEffect e interactuar con la biblioteca Modelo de objetos
componentes (COM). Para utilizar este efecto personalizado en una aplicacin, es preciso hacer referencia al
ensamblado y el efecto debe utilizar el espacio de nombres al que hace referencia el ensamblado.
Ejemplo
En el ejemplo siguiente se muestra cmo definir un espacio de nombres y hacer referencia al ensamblado que
implementa el efecto personalizado.
<Window x:Class="RGBFilterEffectTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="RGBFilterEffectTest" Height="400" Width="300"
xmlns:RGBFilter="clr-namespace:RGBFilter;assembly=ManagedRGBFilterBitmapEffect"
xmlns:MappingPIGen1="clr-namespace:RGBFilter;assembly=ManagedRGBFilterBitmapEffect">
En el ejemplo siguiente se utiliza el efecto personalizado haciendo referencia al espacio de nombres
proporcionado en el ejemplo anterior.
<Image Name="RTB" Source="images/kittens.jpg">
<Image.BitmapEffect>
<RGBFilter:RGBFilterBitmapEffect >
<RGBFilter:RGBFilterBitmapEffect.Red>
<Binding ElementName="redSlider" Path="Value"/>
</RGBFilter:RGBFilterBitmapEffect.Red>
<RGBFilter:RGBFilterBitmapEffect.Green>
<Binding ElementName="greenSlider" Path="Value"/>
</RGBFilter:RGBFilterBitmapEffect.Green>
<RGBFilter:RGBFilterBitmapEffect.Blue>
<Binding ElementName="blueSlider" Path="Value"/>
</RGBFilter:RGBFilterBitmapEffect.Blue>
</RGBFilter:RGBFilterBitmapEffect >
</Image.BitmapEffect>
</Image>

7.3.1.2.15. Cmo: Aplicar un Efecto a Parte de una Imagen


En este ejemplo se muestra cmo aplicar BitmapEffect a una parte de un control Image mediante la propiedad
BitmapEffectInput.
Ejemplo
En el ejemplo siguiente se muestran dos maneras de aplicar un efecto a una parte del contenido visual.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<BitmapImage x:Key="sourceImage" UriSource="/images/WaterLilies.jpg"/>
</Page.Resources>
<StackPanel>
<DockPanel>
<StackPanel DockPanel.Dock="Left">
<TextBlock>AreaToApplyEffect=".25,.25,.50,.50"</TextBlock>
<TextBlock>AreaToApplyEffectUnits="RelativeToBoundingBox"</TextBlock>
</StackPanel>
<Image Width="360" Source="{StaticResource sourceImage}" Margin="10"
DockPanel.Dock="Left">
<Image.BitmapEffect>
<!-- <BitmapEffectGroup> would go here if you wanted to apply more
then one effect to the TextBox. However, in this example only
one effect is being applied so BitmapEffectGroup does not need
to be included. -->
<!-- Effect to Apply to the Image -->
<EmbossBitmapEffect Relief="0.8" LightAngle="320" />

MCT: Luis Dueas

Pag 340 de 473

Manual de Windows Presentation Foundation


</Image.BitmapEffect>
<Image.BitmapEffectInput>
<!-- BitmapEffectInput is used to apply the bitmap effect to a specified
region of the visual. When this property is not used, the effect
is applied to the entire visual.
AreaToApplyEffect is a rectangular area in which to apply the effect.
- When AreaToApplyEffectUnits is "Absolute", the rectangle is read as
pixel coordinates within the visual.
- When AreaToApplyEffectUnits is "RelativeToBoundingBox",
the rectangle values are relative to the entire bounding box.
Values range between 0 and 1, where (0,0) is the top-left corner
and (1,1) is the bottom-right corner. -->
<BitmapEffectInput AreaToApplyEffect=".25,.25,.50,.50"
AreaToApplyEffectUnits="RelativeToBoundingBox"/>
</Image.BitmapEffectInput>
</Image>
</DockPanel>
<DockPanel>
<StackPanel DockPanel.Dock="Left">
<TextBlock>AreaToApplyEffect="90,66,180,135"</TextBlock>
<TextBlock>AreaToApplyEffectUnits="Absolute"</TextBlock>
</StackPanel>
<Image Width="360" Source="{StaticResource sourceImage}" Margin="10"
DockPanel.Dock="Left">
<Image.BitmapEffect>
<!-- <BitmapEffectGroup> would go here if you wanted to apply more
then one effect to the TextBox. However, in this example only
one effect is being applied so BitmapEffectGroup does not need
to be included. -->
<!-- Effect to Apply to the Image -->
<EmbossBitmapEffect Relief="0.8" LightAngle="320" />
</Image.BitmapEffect>
<Image.BitmapEffectInput>
<!-- BitmapEffectInput is used to apply the bitmap effect to a specified
region of the visual. When this property is not used, the effect
is applied to the entire visual.
AreaToApplyEffect is a rectangular area in which to apply the effect.
- When AreaToApplyEffectUnits is "Absolute", the rectangle is read as
pixel coordinates within the visual.
- When AreaToApplyEffectUnits is "RelativeToBoundingBox",
the rectangle values are relative to the entire bounding box.
Values range between 0 and 1, where (0,0) is the top-left corner
and (1,1) is the bottom-left corner. -->
<BitmapEffectInput AreaToApplyEffect="90,66,180,135"
AreaToApplyEffectUnits="Absolute"/>
</Image.BitmapEffectInput>
</Image>
</DockPanel>
</StackPanel>
</Page>
En la ilustracin siguiente se muestra un efecto de relieve aplicado solamente a la parte central de una imagen.

7.3.1.2.16. Cmo: Animar un Efecto dentro de un BitmapEffectGroup


En este ejemplo se muestra cmo animar un objeto BitmapEffect dentro de un BitmapEffectGroup.
Ejemplo
A continuacin se muestran dos ejemplos de ruta de acceso de TargetProperty utilizada para animar la
propiedad Color de DropShadowBitmapEffect. Los desencadenadores de eventos MouseLeave y MouseEnter
utilizan rutas de acceso diferentes para mostrar las distintas maneras de tener acceso a un BitmapEffect dentro
de BitmapEffectGroup.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Page.Resources>

MCT: Luis Dueas

Pag 341 de 473

Manual de Windows Presentation Foundation


<Style TargetType="{x:Type Button}" x:Key="MyButtonStyle">
<Setter Property="Button.BitmapEffect">
<Setter.Value>
<BitmapEffectGroup>
<DropShadowBitmapEffect x:Name="myDropShadowBitMapEffect" Color="Black"
ShadowDepth="3" />
<OuterGlowBitmapEffect GlowColor="Gray"/>
<!-- BitmapEffect -->
</BitmapEffectGroup>
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="Button.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard >
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Button.BitmapEffect).
(BitmapEffectGroup.Children)[0].(DropShadowBitmapEffect.Color)"
From="Gray" To="Red" Duration="0:0:0.5" AutoReverse="False" />
<!-- <ColorAnimation Storyboard.TargetProperty="BitmapEffect.
Children[0].Color" From="Gray" To="Red" Duration="0:0:0.5"
AutoReverse="False" /> -->
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Button.MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard >
<Storyboard>
<!-- <ColorAnimation Storyboard.TargetProperty="(Button.BitmapEffect).
(BitmapEffectGroup.Children)[0].(DropShadowBitmapEffect.Color)"
From="Gray" To="Red" Duration="0:0:0.5" AutoReverse="False" /> -->
<ColorAnimation Storyboard.TargetProperty="BitmapEffect.Children[0].
Color" From="Red" To="Gray" Duration="0:0:0.5" AutoReverse="False" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</Page.Resources>
<StackPanel Margin="20">
<Button Style="{StaticResource MyButtonStyle}">Click Me</Button>
</StackPanel>
</Page>

7.3.2. Pinceles
En los temas siguientes se describe cmo utilizar los pinceles de Windows Presentation Foundation (WPF) para
"pintar" contenido en la pantalla

7.3.2.1. Informacin General sobre Pinceles de WPF


Todo lo que aparece visible en la pantalla lo est porque lo ha pintado un pincel. Por ejemplo, se utiliza un
pincel para describir el fondo de un botn, el primero plano del texto y el relleno de una forma. En este tema se
introducen los conceptos relativos a pintar con los pinceles de Windows Presentation Foundation (WPF) y se
proporcionan ejemplos. Los pinceles permiten pintar los objetos de una interfaz de usuario (UI) con cualquier
cosa desde simples colores slidos hasta conjuntos complejos de tramas e imgenes.
Pintar con un pincel
Un objeto Brush (pincel), "pinta" un rea con su salida. Los distintos pinceles tienen tipos de salidas diferentes.
Algunos pinceles pintan una rea con un color slido, otros con un degradado, una trama, una imagen o un
dibujo. En la ilustracin siguiente se muestran ejemplos de cada uno de los tipos de Brush diferentes.
Ejemplos de pinceles

MCT: Luis Dueas

Pag 342 de 473

Manual de Windows Presentation Foundation

La mayora de los objetos visuales permiten especificar cmo pintarlos. En la tabla siguiente se muestra una
lista de algunos objetos y propiedades comunes con los que puede utilizar un objeto Brush.
Clase

Propiedades de pincel

Border

BorderBrush, Background

Control

Background, Foreground

Panel

Background

Pen

Brush

Shape

Fill, Stroke

TextBlock

Background

En las secciones siguientes se describen los distintos tipos de Brush y se proporciona un ejemplo de cada uno.
Pintar con un color slido
Un objeto SolidColorBrush pinta un rea con un color (Color) slido. Existen varias maneras de especificar la
propiedad Color de SolidColorBrush: por ejemplo, puede especificar sus canales alfa, rojo, azul y verde, o
utilizar uno de los colores predefinidos proporcionados por la clase Colors.
En el ejemplo siguiente se usa un objeto SolidColorBrush para pintar la propiedad Fill de un objeto Rectangle.
En la siguiente ilustracin se muestra el rectngulo pintado.
Rectngulo pintado mediante SolidColorBrush

Rectangle exampleRectangle = new Rectangle();


exampleRectangle.Width = 75;
exampleRectangle.Height = 75;
// Create a SolidColorBrush and use it to
// paint the rectangle.
SolidColorBrush myBrush = new SolidColorBrush(Colors.Red);
exampleRectangle.Fill = myBrush;
<Rectangle Width="75" Height="75">
<Rectangle.Fill>
<SolidColorBrush Color="Red" />
</Rectangle.Fill>
</Rectangle>
Pintar con un degradado lineal
LinearGradientBrush pinta un rea con un degradado lineal. Un degradado lineal mezcla dos o ms colores a lo
largo de una lnea, el eje de degradado. Se utilizan objetos GradientStop para especificar los colores del
degradado y sus posiciones.
En el ejemplo siguiente se usa un objeto LinearGradientBrush para pintar la propiedad Fill de un objeto
Rectangle. En la siguiente ilustracin se muestra el rectngulo pintado.
Rectngulo pintado mediante LinearGradientBrush

MCT: Luis Dueas

Pag 343 de 473

Manual de Windows Presentation Foundation

Rectangle exampleRectangle = new Rectangle();


exampleRectangle.Width = 75;
exampleRectangle.Height = 75;
// Create a LinearGradientBrush and use it to
// paint the rectangle.
LinearGradientBrush myBrush = new LinearGradientBrush();
myBrush.GradientStops.Add(new GradientStop(Colors.Yellow, 0.0));
myBrush.GradientStops.Add(new GradientStop(Colors.Orange, 0.5));
myBrush.GradientStops.Add(new GradientStop(Colors.Red, 1.0));
exampleRectangle.Fill = myBrush;
<Rectangle Width="75" Height="75">
<Rectangle.Fill>
<LinearGradientBrush>
<GradientStop Color="Yellow" Offset="0.0" />
<GradientStop Color="Orange" Offset="0.5" />
<GradientStop Color="Red" Offset="1.0" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
Pintar con un degradado radial
RadialGradientBrush pinta un rea con un degradado radial. Un degradado radial mezcla dos o ms colores a lo
largo de un crculo. Al igual que con la clase LinearGradientBrush, se utilizan objetos GradientStop para
especificar los colores del degradado y sus posiciones.
En el ejemplo siguiente se usa un objeto RadialGradientBrush para pintar la propiedad Fill de un objeto
Rectangle. En la siguiente ilustracin se muestra el rectngulo pintado.
Rectngulo pintado mediante RadialGradientBrush

Rectangle exampleRectangle = new Rectangle();


exampleRectangle.Width = 75;
exampleRectangle.Height = 75;
// Create a RadialGradientBrush and use it to
// paint the rectangle.
RadialGradientBrush myBrush = new RadialGradientBrush();
myBrush.GradientOrigin = new Point(0.75, 0.25);
myBrush.GradientStops.Add(new GradientStop(Colors.Yellow, 0.0));
myBrush.GradientStops.Add(new GradientStop(Colors.Orange, 0.5));
myBrush.GradientStops.Add(new GradientStop(Colors.Red, 1.0));
exampleRectangle.Fill = myBrush;
<Rectangle Width="75" Height="75">
<Rectangle.Fill>
<RadialGradientBrush GradientOrigin="0.75,0.25">
<GradientStop Color="Yellow" Offset="0.0" />
<GradientStop Color="Orange" Offset="0.5" />
<GradientStop Color="Red" Offset="1.0" />
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>
Pintar con una imagen
Un objeto ImageBrush pinta una rea con un objeto ImageSource.
En el ejemplo siguiente se usa un objeto ImageBrush para pintar la propiedad Fill de un objeto Rectangle. En la
siguiente ilustracin se muestra el rectngulo pintado.
Rectngulo pintado mediante una imagen

Rectangle exampleRectangle = new Rectangle();


exampleRectangle.Width = 75;
exampleRectangle.Height = 75;
// Create an ImageBrush and use it to

MCT: Luis Dueas

Pag 344 de 473

Manual de Windows Presentation Foundation


// paint the rectangle.
ImageBrush myBrush = new ImageBrush();
myBrush.ImageSource =
new BitmapImage(new Uri(@"sampleImages\pinkcherries.jpg", UriKind.Relative));
exampleRectangle.Fill = myBrush;
<Rectangle Width="75" Height="75">
<Rectangle.Fill>
<ImageBrush ImageSource="sampleImages\pinkcherries.jpg"
</Rectangle.Fill>
</Rectangle>

/>

Pintar con un dibujo


Un objeto DrawingBrush pinta una rea con un objeto Drawing. Un objeto Drawing puede contener formas,
imgenes, texto y multimedia.
En el ejemplo siguiente se usa un objeto DrawingBrush para pintar la propiedad Fill de un objeto Rectangle. En
la siguiente ilustracin se muestra el rectngulo pintado.
Rectngulo pintado mediante DrawingBrush

Rectangle exampleRectangle = new Rectangle();


exampleRectangle.Width = 75;
exampleRectangle.Height = 75;
// Create a DrawingBrush and use it to paint the rectangle.
DrawingBrush myBrush = new DrawingBrush();
GeometryDrawing backgroundSquare = new GeometryDrawing(Brushes.White, null,
new RectangleGeometry(new Rect(0, 0, 100, 100)));
GeometryGroup aGeometryGroup = new GeometryGroup();
aGeometryGroup.Children.Add(new RectangleGeometry(new Rect(0, 0, 50, 50)));
aGeometryGroup.Children.Add(new RectangleGeometry(new Rect(50, 50, 50, 50)));
LinearGradientBrush checkerBrush = new LinearGradientBrush();
checkerBrush.GradientStops.Add(new GradientStop(Colors.Black, 0.0));
checkerBrush.GradientStops.Add(new GradientStop(Colors.Gray, 1.0));
GeometryDrawing checkers = new GeometryDrawing(checkerBrush, null, aGeometryGroup);
DrawingGroup checkersDrawingGroup = new DrawingGroup();
checkersDrawingGroup.Children.Add(backgroundSquare);
checkersDrawingGroup.Children.Add(checkers);
myBrush.Drawing = checkersDrawingGroup;
myBrush.Viewport = new Rect(0, 0, 0.25, 0.25);
myBrush.TileMode = TileMode.Tile;
exampleRectangle.Fill = myBrush;
<Rectangle Width="75" Height="75">
<Rectangle.Fill>
<DrawingBrush Viewport="0,0,0.25,0.25" TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="White">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,100,100" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing>
<GeometryDrawing.Geometry>
<GeometryGroup>
<RectangleGeometry Rect="0,0,50,50" />
<RectangleGeometry Rect="50,50,50,50" />
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Brush>
<LinearGradientBrush>
<GradientStop Offset="0.0" Color="Black" />
<GradientStop Offset="1.0" Color="Gray" />
</LinearGradientBrush>
</GeometryDrawing.Brush>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
Pintar con un objeto visual

MCT: Luis Dueas

Pag 345 de 473

Manual de Windows Presentation Foundation


Un objeto VisualBrush pinta un rea con un objeto Visual. Algunos ejemplos de objetos visuales son Button,
Page y MediaElement. VisualBrush tambin permite proyectar el contenido de una parte de la aplicacin en otra
rea; resulta muy til para crear efectos de reflexin y para ampliar partes de la pantalla.
En el ejemplo siguiente se usa un objeto VisualBrush para pintar la propiedad Fill de un objeto Rectangle. En la
siguiente ilustracin se muestra el rectngulo pintado.
Rectngulo pintado mediante VisualBrush

Rectangle exampleRectangle = new Rectangle();


exampleRectangle.Width = 75;
exampleRectangle.Height = 75;
// Create a VisualBrush and use it to paint the rectangle.
VisualBrush myBrush = new VisualBrush();
// Create the brush's contents.
StackPanel aPanel = new StackPanel();
// Create a DrawingBrush and use it to paint the panel.
DrawingBrush myDrawingBrushBrush = new DrawingBrush();
GeometryGroup aGeometryGroup = new GeometryGroup();
aGeometryGroup.Children.Add(new RectangleGeometry(new Rect(0, 0, 50, 50)));
aGeometryGroup.Children.Add(new RectangleGeometry(new Rect(50, 50, 50, 50)));
RadialGradientBrush checkerBrush = new RadialGradientBrush();
checkerBrush.GradientStops.Add(new GradientStop(Colors.MediumBlue, 0.0));
checkerBrush.GradientStops.Add(new GradientStop(Colors.White, 1.0));
GeometryDrawing checkers = new GeometryDrawing(checkerBrush, null, aGeometryGroup);
myDrawingBrushBrush.Drawing = checkers;
aPanel.Background = myDrawingBrushBrush;
// Create some text.
TextBlock someText = new TextBlock();
someText.Text = "Hello, World";
FontSizeConverter fSizeConverter = new FontSizeConverter();
someText.FontSize = (double)fSizeConverter.ConvertFromString("10pt");
someText.Margin = new Thickness(10);
aPanel.Children.Add(someText);
myBrush.Visual = aPanel;
exampleRectangle.Fill = myBrush;
<Rectangle Width="75" Height="75">
<Rectangle.Fill>
<VisualBrush TileMode="Tile">
<VisualBrush.Visual>
<StackPanel>
<StackPanel.Background>
<DrawingBrush>
<DrawingBrush.Drawing>
<GeometryDrawing>
<GeometryDrawing.Brush>
<RadialGradientBrush>
<GradientStop Color="MediumBlue" Offset="0.0" />
<GradientStop Color="White" Offset="1.0" />
</RadialGradientBrush>
</GeometryDrawing.Brush>
<GeometryDrawing.Geometry>
<GeometryGroup>
<RectangleGeometry Rect="0,0,50,50" />
<RectangleGeometry Rect="50,50,50,50" />
</GeometryGroup>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</StackPanel.Background>
<TextBlock FontSize="10pt" Margin="10">Hello, World!</TextBlock>
</StackPanel>
</VisualBrush.Visual>
</VisualBrush>
</Rectangle.Fill>
</Rectangle>
Pintar mediante pinceles predefinidos y del sistema
Para mayor comodidad, Windows Presentation Foundation (WPF) proporciona un conjunto de pinceles
predefinidos y del sistema que puede utilizar para pintar objetos.

Para obtener una lista de pinceles predefinidos disponibles, consulte la clase Brushes.
Para obtener una lista de pinceles del sistema disponibles, consulte la clase SystemColors.

MCT: Luis Dueas

Pag 346 de 473

Manual de Windows Presentation Foundation


Caractersticas comunes de los pinceles
Los objetos Brush proporcionan una propiedad Opacity que se puede utilizar para que un pincel sea
transparente total o parcialmente. Un valor de Opacity de 0 hace que el pincel sea completamente
transparente, mientras que un valor de Opacity de 1 lo hace totalmente opaco. En el ejemplo siguiente se
utiliza la propiedad Opacity para hacer que un objeto SolidColorBrush tenga una opacidad del 25 por ciento.
<Rectangle Width="100" Height="100">
<Rectangle.Fill>
<SolidColorBrush Color="Blue" Opacity="0.25" />
</Rectangle.Fill>
</Rectangle>
Rectangle myRectangle = new Rectangle();
myRectangle.Width = 100;
myRectangle.Height = 100;
SolidColorBrush partiallyTransparentSolidColorBrush
= new SolidColorBrush(Colors.Blue);
partiallyTransparentSolidColorBrush.Opacity = 0.25;
myRectangle.Fill = partiallyTransparentSolidColorBrush;
Si el pincel contiene colores que son parcialmente transparentes, el valor de opacidad del color se combina por
multiplicacin con el valor de opacidad del pincel. Por ejemplo, si un pincel tiene un valor de opacidad de 0,5 y
un color utilizado en el pincel tambin tiene un valor de opacidad de 0,5, el color de salida tiene un valor de
opacidad de 0,25.
Nota:
Es ms eficaz cambiar el valor de opacidad de un pincel que modificar la opacidad de un elemento completo
mediante su propiedad UIElement.Opacity.
El contenido de los pinceles se puede girar, sesgar y convertir, y se puede ajustar su escala, mediante las
propiedades Transform o RelativeTransform.
Al tratarse de objetos Animatable, los objetos Brush se pueden animar.
Caractersticas de elementos Freezable
Al heredar de la clase Freezable, la clase Brush proporciona varias caractersticas especiales: los objetos Brush
pueden declararse como recursos, compartirse entre varios objetos y clonarse. Adems, todos los tipos de
Brush, con excepcin de VisualBrush, pueden hacerse de slo lectura para mejorar el rendimiento y que sean
seguros para subprocesos.

7.3.2.2. Informacin General sobre la Transformacin de Pinceles


La clase Brush ofrece dos propiedades de transformacin: Transform y RelativeTransform. Estas propiedades
permiten girar, sesgar y convertir el contenido de un pincel, adems de ajustar su escala. En este tema se
describen las diferencias entre estas dos propiedades y se proporcionan ejemplos de su uso.
Diferencias entre las propiedades Transform y RelativeTransform
Cuando se aplica una transformacin a la propiedad Transform de un pincel, es preciso conocer el tamao del
rea pintada si se desea transformar el contenido del pincel sobre su centro. Supongamos que el rea pintada
tiene 200 pxeles independientes del dispositivo de ancho y 150 de alto. Si utiliza RotateTransform para girar la
salida del pincel 45 grados sobre su centro, se establece la propiedad CenterX en 100 y la propiedad CenterY en
75 para RotateTransform.
Cuando se aplica una transformacin a la propiedad RelativeTransform de un pincel, esa transformacin se
aplica al pincel antes de asignar su salida al rea pintada. En la lista siguiente se describe el orden en que se
procesan y transforman los contenidos de un pincel.
1.

Procese el contenido del pincel. Para GradientBrush, esto significa determinar el rea de degradado.
Para TileBrush, se asigna Viewbox a Viewport. sta se convierte en la salida del pincel.

2.

Proyecte la salida del pincel sobre el rectngulo de transformacin de 1 x 1.

MCT: Luis Dueas

Pag 347 de 473

Manual de Windows Presentation Foundation


3.

Aplique la propiedad RelativeTransform del pincel, si la tiene.

4.

Proyecte la salida transformada sobre el rea que desea pintar.

5.

Aplique la propiedad Transform del pincel, si la tiene.

Dado que RelativeTransform se aplica mientras la salida del pincel est asignada a un rectngulo de 1 x 1, el
centro de la transformacin y los valores de desplazamiento parecen ser relativos. Por ejemplo, si utiliz
RotateTransform para girar la salida del pincel 45 grados sobre su centro, se establecer la propiedad CenterX
en 0,5 y la propiedad CenterY en 0,5 para RotateTransform.
En la ilustracin siguiente se muestra la salida de varios pinceles que se han girado 45 grados mediante las
propiedades RelativeTransform y Transform.

Utilizar RelativeTransform con un objeto TileBrush


Debido a que los pinceles en mosaico son ms complejos que los dems pinceles, aplicar RelativeTransform a
uno de ellos puede dar lugar a resultados inesperados. Por ejemplo, tomemos la imagen siguiente.

En el ejemplo siguiente se utiliza ImageBrush para pintar una rea rectangular con la imagen anterior. Se
aplica RotateTransform a la propiedad RelativeTransform del objeto ImageBrush y se establece su propiedad
Stretch en UniformToFill, lo que debera conservar la relacin de aspecto de la imagen cuando se expanda para
rellenar completamente el rectngulo.
<Rectangle Width="200" Height="100" Stroke="Black" StrokeThickness="1">
<Rectangle.Fill>
<ImageBrush Stretch="UniformToFill">
<ImageBrush.ImageSource>
<BitmapImage UriSource="sampleImages\square.jpg" />
</ImageBrush.ImageSource>
<ImageBrush.RelativeTransform>
<RotateTransform CenterX="0.5" CenterY="0.5" Angle="90" />
</ImageBrush.RelativeTransform>
</ImageBrush>
</Rectangle.Fill>
</Rectangle>
Este ejemplo produce el siguiente resultado.

Observe que la imagen se distorsiona, aunque la propiedad Stretch del pincel se estableci en UniformToFill.
Esto se debe a que la transformacin relativa se aplica despus de asignar la propiedad Viewbox del pincel a su
propiedad Viewport. En la lista siguiente se describen todos los pasos del proceso:
1.

Proyectar el contenido del pincel (Viewbox) sobre su mosaico base (Viewport) utilizando el valor de
Stretch del pincel.

MCT: Luis Dueas

Pag 348 de 473

Manual de Windows Presentation Foundation

2.

Proyectar el mosaico base sobre el rectngulo de transformacin de 1 x 1.

3.

Aplicar RotateTransform.

4.

Proyectar el mosaico base transformado sobre el rea que se va a pintar.

Ejemplo: Girar un objeto ImageBrush 45 grados


En el ejemplo siguiente se aplica RotateTransform a la propiedad RelativeTransform de un objeto ImageBrush.
Las propiedades CenterX y CenterY del objeto RotateTransform se establecen en 0,5, las coordenadas relativas
del punto central del contenido. Como resultado, el contenido del pincel se gira sobre su centro.
// Create an ImageBrush with a relative transform and use it to paint a rectangle.
ImageBrush relativeTransformImageBrush = new ImageBrush();
relativeTransformImageBrush.ImageSource =
new BitmapImage(new Uri(@"sampleImages\pinkcherries.jpg", UriKind.Relative));
// Create a 45 rotate transform about the brush's center
// and apply it to the brush's RelativeTransform property.
RotateTransform aRotateTransform = new RotateTransform();
aRotateTransform.CenterX = 0.5;
aRotateTransform.CenterY = 0.5;
aRotateTransform.Angle = 45;
relativeTransformImageBrush.RelativeTransform = aRotateTransform;
// Use the brush to paint a rectangle.
Rectangle relativeTransformImageBrushRectangle = new Rectangle();
relativeTransformImageBrushRectangle.Width = 175;
relativeTransformImageBrushRectangle.Height = 90;
relativeTransformImageBrushRectangle.Stroke = Brushes.Black;
relativeTransformImageBrushRectangle.Fill = relativeTransformImageBrush;
<Rectangle Width="175" Height="90" Stroke="Black">
<Rectangle.Fill>
<ImageBrush ImageSource="sampleImages\pinkcherries.jpg">
<ImageBrush.RelativeTransform>
<RotateTransform CenterX="0.5" CenterY="0.5" Angle="45" />
</ImageBrush.RelativeTransform>
</ImageBrush>
</Rectangle.Fill>
</Rectangle>
En el ejemplo siguiente tambin se aplica RotateTransform a un objeto ImageBrush, pero se utiliza la propiedad
Transform en lugar de la propiedad RelativeTransform. Para girar el pincel sobre su centro, las propiedades
CenterX y CenterY del objeto RotateTransform deben establecerse en coordenadas absolutas. Dado que el
pincel pinta un rectngulo que es de 175 por 90 pxeles, su punto central es (87,5, 45).
// Create an ImageBrush with a transform and use it to paint a rectangle.
ImageBrush transformImageBrush = new ImageBrush();
transformImageBrush.ImageSource =
new BitmapImage(new Uri(@"sampleImages\pinkcherries.jpg", UriKind.Relative));
// Create a 45 rotate transform about the brush's center
// and apply it to the brush's Transform property.
RotateTransform anotherRotateTransform = new RotateTransform();
anotherRotateTransform.CenterX = 87.5;
anotherRotateTransform.CenterY = 45;
anotherRotateTransform.Angle = 45;
transformImageBrush.Transform = anotherRotateTransform;
// Use the brush to paint a rectangle.
Rectangle transformImageBrushRectangle = new Rectangle();
transformImageBrushRectangle.Width = 175;
transformImageBrushRectangle.Height = 90;

MCT: Luis Dueas

Pag 349 de 473

Manual de Windows Presentation Foundation


transformImageBrushRectangle.Stroke = Brushes.Black;
transformImageBrushRectangle.Fill = transformImageBrush;
<Rectangle Width="175" Height="90" Stroke="Black">
<Rectangle.Fill>
<ImageBrush ImageSource="sampleImages\pinkcherries.jpg">
<ImageBrush.Transform>
<RotateTransform CenterX="87.5" CenterY="45" Angle="45" />
</ImageBrush.Transform>
</ImageBrush>
</Rectangle.Fill>
</Rectangle>
En la ilustracin siguiente se muestra el pincel sin transformacin, con la transformacin aplicada a la propiedad
RelativeTransform y con la transformacin aplicada a la propiedad Transform.

7.3.2.3. Informacin General sobre las Mscaras de Opacidad


Las mscaras de opacidad permiten hacer que partes de un elemento o un objeto visual sean transparentes
total o parcialmente. Para crear una mscara de opacidad, se aplica un Brush a la propiedad OpacityMask de un
elemento o Visual. El pincel se asigna al elemento o al objeto visual y el valor de opacidad de cada pxel del
pincel se utiliza para determinar la opacidad resultante de cada pxel correspondiente del elemento u objeto
visual.
Crear efectos visuales con mscaras de opacidad
Una mscara de opacidad funciona asignando su contenido al elemento u objeto visual. A continuacin, se
utiliza el canal alfa de cada uno de los pxeles del pincel para determinar la opacidad resultante de los pxeles
correspondientes del elemento o el objeto visual; se omite el color real del pincel. Si una parte determinada del
pincel es transparente, la parte correspondiente del elemento u objeto visual se vuelve transparente. Si una
parte determinada del pincel es opaca, la opacidad de la parte correspondiente del elemento u objeto visual no
cambia. La opacidad especificada por la mscara de opacidad se combina con cualquier valor de opacidad
presente en el elemento u objeto visual. Por ejemplo, si un elemento es opaco en un 25% y se le aplica una
mscara de opacidad que realiza la transicin de totalmente opaco a totalmente transparente, el resultado es
un elemento que efecta una transicin desde el 25% de opacidad a totalmente transparente.
Nota:
Aunque en los ejemplos de esta informacin general se muestra el uso de mscaras de opacidad en
elementos de imagen, una mscara de opacidad se puede aplicar a cualquier elemento u objeto Visual,
incluso a los paneles y controles.
Las mscaras de opacidad se utilizan para crear efectos visuales interesantes, como crear imgenes o botones
que se desvanecen mediante un fundido, agregar texturas a los elementos, o combinar degradados para
generar superficies cristalinas. En la ilustracin siguiente se muestra el uso de una mscara de opacidad. Se
utiliza un fondo de cuadros para mostrar las partes transparentes de la mscara.
Ejemplo de mscara de opacidad

MCT: Luis Dueas

Pag 350 de 473

Manual de Windows Presentation Foundation

Crear una mscara de opacidad


Para crear una mscara de opacidad, se crea un Brush y se aplica a la propiedad OpacityMask de un elemento u
objeto visual. Puede utilizar cualquier tipo de Brush como mscara de opacidad.

LinearGradientBrush, RadialGradientBrush: se utiliza para que un elemento o un objeto visual se


desvanezcan mediante un fundido.
En la ilustracin siguiente se muestra LinearGradientBrush utilizado como mscara de opacidad.
Ejemplo de mscara de opacidad con LinearGradientBrush

ImageBrush: se utiliza para crear textura y efectos de bordes suaves o rasgados.


En la ilustracin siguiente se muestra ImageBrush utilizado como mscara de opacidad.
Ejemplo de mscara de opacidad con LinearGradientBrush

DrawingBrush: se utiliza para crear mscaras de opacidad complejas a partir de modelos de formas,
imgenes y degradados.
En la ilustracin siguiente se muestra DrawingBrush utilizado como mscara de opacidad.
Ejemplo de mscara de opacidad con DrawingBrush

Los pinceles de degradado (LinearGradientBrush y RadialGradientBrush) resultan particularmente apropiados


para su uso como mscaras de opacidad. Dado que SolidColorBrush rellena una rea de un color uniforme, no
es muy eficaz como mscara de opacidad; utilizar SolidColorBrush equivale a establecer la propiedad
OpacityMask del elemento u objeto visual.
Utilizar un degradado como mscara de opacidad
Para crear un relleno de degradado, se especifican dos o ms puntos de degradado. Cada punto de degradado
contiene un color y una posicin. El proceso es el mismo cuando se utiliza un degradado como mscara de
opacidad, con la excepcin de que, en lugar de mezclar los colores, el degradado de la mscara de opacidad
mezcla los valores de canal alfa. En consecuencia, el color real del contenido del degradado no es relevante;
nicamente se tiene en cuenta el canal alfa, u opacidad, de cada color. A continuacin, se muestra un ejemplo.
<!--With the opacity mask:-->
<Image Width="200" Height="150" Source="sampleImages\Waterlilies.jpg" Margin="10"
HorizontalAlignment="Left" Grid.Column="2" Grid.Row="3">
<Image.OpacityMask>
<LinearGradientBrush StartPoint="0.1,0.1" EndPoint="0.75,0.75">
<LinearGradientBrush.GradientStops>

MCT: Luis Dueas

Pag 351 de 473

Manual de Windows Presentation Foundation


<GradientStop Offset="0" Color="Black"/>
<GradientStop Offset="1" Color="Transparent"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Image.OpacityMask>
</Image>
Especificar puntos de degradado para una mscara de opacidad
En el ejemplo anterior, el color Black definido por el sistema se utiliza como color de inicio del degradado. Dado
que todos los colores de la clase Colors, excepto Transparent, son totalmente opacos, se pueden utilizar para
definir de manera sencilla un color de inicio para una mscara de opacidad de degradado.
Para controlar ms los valores alfa al definir una mscara de opacidad, puede especificar el canal alfa de los
colores mediante la notacin hexadecimal ARGB en el marcado o mediante el mtodo Color.FromScRgb.
Especificar la opacidad del color en "XAML"
En Lenguaje de marcado de aplicaciones extensible (XAML), se utiliza la notacin hexadecimal ARGB para
especificar la opacidad de los colores individuales. La notacin hexadecimal ARGB utiliza la sintaxis siguiente:
#aarrggbb
aa en la lnea anterior representa un valor hexadecimal de dos dgitos utilizado para especificar la opacidad del
color.

rr,

gg

bb

representan

valores

hexadecimales

de

dos

dgitos

utilizados

para

especificar,

respectivamente, la cantidad de rojo, verde y azul del color. Cada dgito hexadecimal puede tener un valor
comprendido entre 0 y 9 o entre A y F. 0 es el valor mnimo y F es el mximo. Un valor de alfa de 00 especifica
un color completamente transparente, mientras que un valor de alfa de FF crea un color totalmente opaco. En
el ejemplo siguiente, se utiliza la notacin hexadecimal ARGB para especificar dos colores. El primero es
totalmente opaco, mientras el segundo es completamente transparente.
<Canvas.OpacityMask>
<RadialGradientBrush>
<RadialGradientBrush.GradientStops>
<GradientStop Offset="0" Color="#FF000000"/>
<GradientStop Offset="1" Color="#00000000"/>
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</Canvas.OpacityMask>
Utilizar una imagen como mscara de opacidad
Las imgenes tambin se pueden utilizar como mscaras de opacidad. En la ilustracin siguiente se muestra un
ejemplo. Se utiliza un fondo de cuadros para mostrar las partes transparentes de la mscara.
Ejemplo de mscara de opacidad

Para utilizar una imagen como una mscara de opacidad, utilice ImageBrush para contener la imagen. Al crear
una imagen que se va a utilizar como mscara de opacidad, guarde la imagen en un formato que admita varios
niveles de transparencia, como Grficos de red porttiles (PNG). En el ejemplo siguiente se muestra el cdigo
utilizado para crear la ilustracin anterior.

MCT: Luis Dueas

Pag 352 de 473

Manual de Windows Presentation Foundation


<!-- With the Opacity Mask-->
<Image Height="150" Width="200" Source="sampleImages/Waterlilies.jpg"
HorizontalAlignment="Left" Margin="10" Grid.Column="2" Grid.Row="1">
<Image.OpacityMask>
<ImageBrush ImageSource="sampleImages/tornedges.png"/>
</Image.OpacityMask>
</Image>
Utilizar una imagen en mosaico como mscara de opacidad
En el ejemplo siguiente, la misma imagen se utiliza con otro ImageBrush, pero se utilizan las caractersticas de
mosaico del pincel para generar mosaicos de la imagen de 50 pxeles cuadrados.
<!-- With the Opacity Mask -->
<Image Height="150" Width="200" Source="sampleImages/Waterlilies.jpg"
HorizontalAlignment="Left" Margin="10" Grid.Column="2" Grid.Row="2">
<Image.OpacityMask>
<ImageBrush Viewport="0,0,50,50" ViewportUnits="Absolute" TileMode="Tile"
ImageSource="sampleImages/tornedges.png"/>
</Image.OpacityMask>
</Image>
Crear una mscara de opacidad a partir de un dibujo
Se pueden utilizar dibujos como mscaras de opacidad. Las formas contenidas dentro del dibujo se pueden
rellenar con degradados, colores slidos, imgenes o incluso otros dibujos. En la ilustracin siguiente se
muestra un ejemplo de un dibujo utilizado como mscara de opacidad. Se utiliza un fondo de cuadros para
mostrar las partes transparentes de la mscara.
Ejemplo de mscara de opacidad con DrawingBrush

Para utilizar un dibujo como mscara de opacidad, utilice DrawingBrush para contener el dibujo. En el ejemplo
siguiente se muestra el cdigo utilizado para crear la ilustracin anterior:
<!-- With the Opacity Mask-->
<Image Grid.Row="4" Grid.Column="5" Height="150" Width="200"
Source="sampleImages/Waterlilies.jpg">
<Image.OpacityMask>
<DrawingBrush>
<DrawingBrush.Drawing>
<GeometryDrawing>
<GeometryDrawing.Brush>
<RadialGradientBrush>
<RadialGradientBrush.GradientStops>
<GradientStop Offset="0" Color="Black" />
<GradientStop Offset="1" Color="Transparent" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</GeometryDrawing.Brush>
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0.05,0.05 0.9,0.9" />
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>

MCT: Luis Dueas

Pag 353 de 473

Manual de Windows Presentation Foundation


<Pen Thickness="0.1" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Image.OpacityMask>
</Image>
Utilizar un dibujo en mosaico como mscara de opacidad
Al igual que ImageBrush, se puede hacer que DrawingBrush cree el dibujo en mosaico. En el ejemplo siguiente,
se utiliza un pincel de dibujo para crear una mscara de opacidad en mosaico.
<!-- With the Opacity Mask-->
<Button Grid.Row="8" Grid.Column="5" Height="100" Width="200" FontFamily="MS Gothic"
FontSize="16">A Button
<Button.OpacityMask>
<DrawingBrush Viewport="0,0,0.25,0.25" TileMode="Tile">
<DrawingBrush.Drawing>
<GeometryDrawing>
<GeometryDrawing.Brush>
<RadialGradientBrush>
<RadialGradientBrush.GradientStops>
<GradientStop Offset="0" Color="Black" />
<GradientStop Offset="1" Color="Transparent" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</GeometryDrawing.Brush>
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0.05,0.05 0.9,0.9" />
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="0.1" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Button.OpacityMask>
</Button>

7.3.2.4. Informacin General sobre el Dibujo con Colores Slidos y Degradados


En este tema se describe cmo usar los objetos SolidColorBrush, LinearGradientBrush y RadialGradientBrush
para pintar con colores slidos, degradados lineales y degradados radiales.
Pintar un rea con un color slido
Una de las operaciones ms comunes en cualquier plataforma es pintar un rea con un Color slido. Para ello,
Windows Presentation Foundation (WPF) proporciona la clase SolidColorBrush. En las secciones siguientes, se
describen las distintas formas de pintar con SolidColorBrush.
Usar SolidColorBrush en "XAML"
Para pintar un rea con un color slido en XAML, use una de las opciones siguientes.

Seleccione un pincel de color slido predefinido por nombre. Por ejemplo, puede establecer la
propiedad Background de un botn en "Red" o "MediumBlue". Para obtener una lista del resto de los
pinceles predefinidos de color slido, vea las propiedades estticas de la clase Brushes. A continuacin,
se muestra un ejemplo.
<!-- This button's background is painted with a red SolidColorBrush,
described using a named color. -->
<Button Background="Red">A Button</Button>

Elija un color de la paleta de colores de 32 bits especificando las cantidades de rojo, verde y azul que
se van a combinar en un solo color slido. El formato para especificar un color de la paleta de 32 bits
es "#rrggbb", donde rr es un nmero hexadecimal de dos dgitos que especifica la cantidad relativa de

MCT: Luis Dueas

Pag 354 de 473

Manual de Windows Presentation Foundation


rojo, gg especifica la cantidad de verde y bb, la cantidad de azul. Adems, el color se puede especificar
como "#aarrggbb" donde aa especifica el valor alfa, o transparencia, del color. Este enfoque permite
crear colores parcialmente transparentes. En el ejemplo siguiente, la propiedad Background de Button
se establece en rojo totalmente opaco mediante la notacin hexadecimal.
<!-- This button's background is painted with a red SolidColorBrush,
described using hexadecimal notation. -->
<Button Background="#FFFF0000">A Button</Button>

Use la sintaxis de las etiquetas de las propiedades para describir SolidColorBrush. Esta sintaxis es ms
prolija pero permite especificar valores adicionales, como la opacidad del pincel. En el ejemplo
siguiente, las propiedades Background de dos elementos Button se establecen en rojo totalmente
opaco. El color del primer pincel se describe mediante un nombre de color predefinido y el del
segundo, mediante la notacin hexadecimal.
<!-- Both of these buttons' backgrounds are painted with red
SolidColorBrush objects, described using object element syntax. -->
<Button>A Button
<Button.Background>
<SolidColorBrush Color="Red" />
</Button.Background>
</Button>
<Button>A Button
<Button.Background>
<SolidColorBrush Color="#FFFF0000" />
</Button.Background>
</Button>

Pintar con SolidColorBrush en cdigo


Para pintar un rea con un color slido en cdigo, use una de las opciones siguientes.

Utilice uno de los pinceles predefinidos proporcionados por la clase Brushes. En el ejemplo siguiente,
se establece el valor de la propiedad Background de Button en Red.
Button myButton = new Button();
myButton.Content = "A Button";
myButton.Background = Brushes.Red;

Cree un SolidColorBrush y establezca el valor de su propiedad Color mediante una estructura Color.
Puede utilizar un color predefinido de la clase Colors o puede crear un objeto Color mediante el mtodo
FromArgb esttico.
En el ejemplo siguiente, se muestra cmo establecer la propiedad Color de SolidColorBrush con un
color predefinido.
Button myButton = new Button();
myButton.Content = "A Button";
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush.Color = Colors.Red;
myButton.Background = mySolidColorBrush;

El objeto FromArgb esttico permite especificar los valores alfa, rojo, verde y azul del color. El intervalo normal
de cada uno de estos valores est comprendido entre 0 y 255. Por ejemplo, un valor alpha de 0 indica que un
color es totalmente transparente, mientras que un valor de 255 indica que el color es totalmente opaco. De
igual forma, un valor rojo de 0 indica que un color no contiene rojo, mientras un valor de 255 indica que un
color contiene la cantidad mxima de rojo posible. En el ejemplo siguiente, el color de un pincel se describe
especificando los valores de alfa, rojo, verde y azul.
Pintar un rea con un degradado
Un pincel de degradado pinta un rea con varios colores que se mezclan entre s a lo largo de un eje. Puede
utilizarlos para crear impresiones de luz y sombras, dando una sensacin tridimensional a los controles.
Tambin puede utilizarlos para simular cristal, cromo, agua y otras superficies suavizadas. WPF proporciona dos
tipos de pinceles de degradado: LinearGradientBrush y RadialGradientBrush.
Degradados lineales

MCT: Luis Dueas

Pag 355 de 473

Manual de Windows Presentation Foundation


LinearGradientBrush pinta un rea con un degradado definido a lo largo de una lnea, el eje de degradado.
Especifique los colores del degradado y su ubicacin a lo largo del eje de degradado mediante objetos
GradientStop. Tambin puede modificar el eje de degradado, lo que permite crear los degradados horizontal y
vertical e invertir la direccin del degradado. El eje de degradado se describe en la seccin siguiente. De forma
predeterminada, se crea un degradado diagonal.
En el ejemplo siguiente, se muestra el cdigo que crea un degradado lineal con cuatro colores.
<!-- This rectangle is painted with a diagonal linear gradient. -->
<Rectangle Width="200" Height="100">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="Yellow" Offset="0.0" />
<GradientStop Color="Red" Offset="0.25" />
<GradientStop Color="Blue" Offset="0.75" />
<GradientStop Color="LimeGreen" Offset="1.0" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
Rectangle diagonalFillRectangle = new Rectangle();
diagonalFillRectangle.Width = 200;
diagonalFillRectangle.Height = 100;
// Create a diagonal linear gradient with four stops.
LinearGradientBrush myLinearGradientBrush = new LinearGradientBrush();
myLinearGradientBrush.StartPoint = new Point(0,0);
myLinearGradientBrush.EndPoint = new Point(1,1);
myLinearGradientBrush.GradientStops.Add(new GradientStop(Colors.Yellow, 0.0));
myLinearGradientBrush.GradientStops.Add(new GradientStop(Colors.Red, 0.25));
myLinearGradientBrush.GradientStops.Add(new GradientStop(Colors.Blue, 0.75));
myLinearGradientBrush.GradientStops.Add(new GradientStop(Colors.LimeGreen, 1.0));
// Use the brush to paint the rectangle.
diagonalFillRectangle.Fill = myLinearGradientBrush;
Este cdigo genera el siguiente degradado.

Nota: los ejemplos de degradado de este tema utilizan el sistema de coordenadas predeterminado para
establecer los puntos inicial y final. El sistema de coordenadas predeterminado es relativo a un rectngulo de
seleccin: 0 indica un 0 por ciento del rectngulo de seleccin y 1, un 100 por cien del rectngulo de seleccin.
Puede cambiar este sistema de coordenadas estableciendo la propiedad MappingMode en el valor Absolute. Un
sistema de coordenadas absoluto no es relativo respecto a ningn rectngulo de seleccin. Los valores se
interpretan directamente en el espacio local.
GradientStop es el bloque de creacin bsico de un pincel de degradado. Un punto de degradado especifica una
propiedad Color en una propiedad Offset a lo largo del eje de degradado.

La propiedad Color del punto de degradado especifica el color del punto. Puede establecer el color
mediante un color predefinido (proporcionado por la clase Colors) o especificando los valores ScRGB o
ARGB. En XAML, tambin puede utilizar la notacin hexadecimal para describir un color. Para obtener
ms informacin, vea la estructura Color.

La propiedad Offset del punto de degradado especifica la posicin del color del punto en el eje de
degradado. El desplazamiento es un objeto Double comprendido entre 0 y 1. Cuanto ms se aproxime
el valor de desplazamiento de un punto de degradado a 0, ms prximo estar el color al principio del
degradado. Cuanto ms se aproxime el valor de desplazamiento a 1, ms prximo estar el color al
final del degradado.

MCT: Luis Dueas

Pag 356 de 473

Manual de Windows Presentation Foundation


El color de cada punto entre los puntos de degradado se interpola linealmente como combinacin del color
especificado por los dos puntos limitantes de degradado. En la ilustracin siguiente se resaltan los puntos de
degradado del ejemplo anterior. Los crculos marcan la posicin de los puntos de degradado y una lnea de
guiones muestra el eje de degradado.

El primer punto de degradado especifica el color amarillo en un desplazamiento de 0.0. El segundo punto de
degradado especifica el color rojo en un desplazamiento de 0.25. Los puntos entre estos dos puntos cambian
gradualmente del amarillo al rojo a medida que se mueva de izquierda a derecha por el eje de degradado. El
tercer punto de degradado especifica el color azul en un desplazamiento de 0.75. Los puntos entre los puntos
segundo y tercero de degradado cambian gradualmente del rojo al azul. El cuarto punto de degradado
especifica el color verde oscuro en un desplazamiento de 1.0. Los puntos entre los puntos tercero y cuarto de
degradado cambian gradualmente del azul al verde oscuro.
Eje de degradado
Como se ha mencionado anteriormente, los puntos de degradado de un pincel de degradado lineal se colocan a
lo largo de una lnea, el eje de degradado. Puede cambiar la orientacin y el tamao de la lnea con las
propiedades StartPoint y EndPoint del pincel. Si manipula las propiedades StartPoint y EndPoint del pincel,
puede crear degradados horizontales y verticales, invertir la direccin del degradado, comprimir la extensin
del degradado, etc.
De forma predeterminada, las propiedades StartPoint y EndPoint del pincel de degradado lineal son relativos al
rea que se pinta. El punto (0,0) representa la esquina superior izquierda del rea que se pinta y (1,1), la
esquina inferior derecha. El valor de la propiedad StartPoint predeterminada de LinearGradientBrush es (0,0) y
el de su propiedad EndPoint predeterminada es (1,1). De esta forma, se crea un degradado diagonal que
empieza en la esquina superior izquierda y se extiende hasta la esquina inferior derecha del rea que se pinta.
En la ilustracin siguiente, se muestra el eje de degradado de un pincel de degradado lineal con las propiedades
StartPoint y EndPoint predeterminadas.

En el ejemplo siguiente, se muestra cmo crear un degradado horizontal especificando las propiedades
StartPoint y EndPoint del pincel. Observe que los puntos de degradado son iguales a los de los ejemplos
anteriores; al cambiar las propiedades StartPoint y EndPoint, el degradado se ha cambiado de diagonal a
horizontal.
<!-- This rectangle is painted with a horizontal linear gradient. -->
<Rectangle Width="200" Height="100">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
<GradientStop Color="Yellow" Offset="0.0" />
<GradientStop Color="Red" Offset="0.25" />
<GradientStop Color="Blue" Offset="0.75" />
<GradientStop Color="LimeGreen" Offset="1.0" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>

MCT: Luis Dueas

Pag 357 de 473

Manual de Windows Presentation Foundation

Rectangle horizontalFillRectangle = new Rectangle();


horizontalFillRectangle.Width = 200;
horizontalFillRectangle.Height = 100;
// Create a horizontal linear gradient with four stops.
LinearGradientBrush myHorizontalGradient = new LinearGradientBrush();
myHorizontalGradient.StartPoint = new Point(0,0.5);
myHorizontalGradient.EndPoint = new Point(1,0.5);
myHorizontalGradient.GradientStops.Add(new GradientStop(Colors.Yellow, 0.0));
myHorizontalGradient.GradientStops.Add(new GradientStop(Colors.Red, 0.25));
myHorizontalGradient.GradientStops.Add(new GradientStop(Colors.Blue, 0.75));
myHorizontalGradient.GradientStops.Add(new GradientStop(Colors.LimeGreen, 1.0));
// Use the brush to paint the rectangle.
horizontalFillRectangle.Fill = myHorizontalGradient;
En la ilustracin siguiente, se muestra el degradado que se crea. El eje de degradado se marca con una lnea de
guiones y los puntos de degradado, con crculos.

En el ejemplo siguiente, se muestra cmo crear un degradado vertical.


<!-- This rectangle is painted with a vertical gradient. -->
<Rectangle Width="200" Height="100">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="Yellow" Offset="0.0" />
<GradientStop Color="Red" Offset="0.25" />
<GradientStop Color="Blue" Offset="0.75" />
<GradientStop Color="LimeGreen" Offset="1.0" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
Rectangle verticalFillRectangle = new Rectangle();
verticalFillRectangle.Width = 200;
verticalFillRectangle.Height = 100;
// Create a vertical linear gradient with four stops.
LinearGradientBrush myVerticalGradient = new LinearGradientBrush();
myVerticalGradient.StartPoint = new Point(0.5,0);
myVerticalGradient.EndPoint = new Point(0.5,1);
myVerticalGradient.GradientStops.Add(new GradientStop(Colors.Yellow, 0.0));
myVerticalGradient.GradientStops.Add(new GradientStop(Colors.Red, 0.25));
myVerticalGradient.GradientStops.Add(new GradientStop(Colors.Blue, 0.75));
myVerticalGradient.GradientStops.Add(new GradientStop(Colors.LimeGreen, 1.0));
// Use the brush to paint the rectangle.
verticalFillRectangle.Fill = myVerticalGradient;
En la ilustracin siguiente, se muestra el degradado que se crea. El eje de degradado se marca con una lnea de
guiones y los puntos de degradado, con crculos.

Degradados radiales
Como un LinearGradientBrush, un RadialGradientBrush pinta un rea con colores que se mezclan juntos a lo
largo de un eje. Los ejemplos anteriores mostraron cmo el eje de un pincel de degradado lineal es una lnea
recta. Un crculo define el eje de un pincel de degradado radial; sus colores "irradian" desde su origen.
En el ejemplo siguiente, se usa un pincel de degradado radial para pintar el interior de un rectngulo.
<!-- This rectangle is painted with a diagonal linear gradient. -->

MCT: Luis Dueas

Pag 358 de 473

Manual de Windows Presentation Foundation


<Rectangle Width="200" Height="100">
<Rectangle.Fill>
<RadialGradientBrush
GradientOrigin="0.5,0.5" Center="0.5,0.5"
RadiusX="0.5" RadiusY="0.5">
<GradientStop Color="Yellow" Offset="0" />
<GradientStop Color="Red" Offset="0.25" />
<GradientStop Color="Blue" Offset="0.75" />
<GradientStop Color="LimeGreen" Offset="1" />
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>
RadialGradientBrush myRadialGradientBrush = new RadialGradientBrush();
myRadialGradientBrush.GradientOrigin = new Point(0.5,0.5);
myRadialGradientBrush.Center = new Point(0.5,0.5);
myRadialGradientBrush.RadiusX = 0.5;
myRadialGradientBrush.RadiusY = 0.5;
myRadialGradientBrush.GradientStops.Add(new GradientStop(Colors.Yellow, 0.0));
myRadialGradientBrush.GradientStops.Add(new GradientStop(Colors.Red, 0.25));
myRadialGradientBrush.GradientStops.Add(new GradientStop(Colors.Blue, 0.75));
myRadialGradientBrush.GradientStops.Add(new GradientStop(Colors.LimeGreen, 1.0));
Rectangle myRectangle = new Rectangle();
myRectangle.Width = 200;
myRectangle.Height = 100;
myRectangle.Fill = myRadialGradientBrush;
En la ilustracin siguiente, se muestra el degradado creado en el ejemplo anterior. Se han resaltado los puntos
de degradado del pincel. Observe que, aunque los resultados sean diferentes, los puntos de degradado de este
ejemplo son idnticos a los del degradado de los ejemplos anteriores del pincel de degradado lineal.

La propiedad GradientOrigin especifica el punto de inicio del eje de degradado de un pincel de degradado radial.
El eje de degradado irradia desde el origen del degradado hacia el crculo del mismo. Al crculo del degradado
de un pincel lo definen sus propiedades Center, RadiusX y RadiusY.
Las presentaciones de la ilustracin siguientes muestran varios degradados radiales con valores distintos de las
propiedades GradientOrigin, Center, RadiusX y RadiusY.
RadialGradientBrushes con valores distintos de GradientOrigin, Center, RadiusX y RadiusY.

Especificar puntos de degradado parcialmente transparentes o transparentes


Puesto que los puntos de degradado no proporcionan ninguna propiedad de opacidad, debe especificar el canal
alfa de los colores mediante la notacin hexadecimal de ARGB del marcado o use el mtodo Color.FromScRgb

MCT: Luis Dueas

Pag 359 de 473

Manual de Windows Presentation Foundation


para crear puntos de degradado que sean transparentes o parcialmente transparentes. En las secciones
siguientes, se explica cmo crear puntos de degradado parcialmente transparentes en XAML y en cdigo.
Especificar la opacidad del color en "XAML"
En XAML, se utiliza la notacin hexadecimal ARGB para especificar la opacidad de los colores individuales. La
notacin hexadecimal ARGB utiliza la sintaxis siguiente:
#aarrggbb
aa en la lnea anterior representa un valor hexadecimal de dos dgitos utilizado para especificar la opacidad del
color.

rr,

gg

bb

representan

valores

hexadecimales

de

dos

dgitos

utilizados

para

especificar,

respectivamente, la cantidad de rojo, verde y azul del color. Cada dgito hexadecimal puede tener un valor
comprendido entre 0 y 9 o entre A y F. 0 es el valor mnimo y F, el mximo. Un valor alfa de 00 especifica un
color completamente transparente, mientras que un valor alfa de FF crea un color totalmente opaco. En el
ejemplo siguiente, se utiliza la notacin hexadecimal ARGB para especificar dos colores. El primero es
parcialmente transparente (tiene un valor alfa de x 20), mientras que el segundo es totalmente opaco.
<Rectangle Width="100" Height="100">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0">
<!-- This gradient stop is partially transparent. -->
<GradientStop Color="#200000FF" Offset="0.0" />
<!-- This gradient stop is fully opaque. -->
<GradientStop Color="#FF0000FF" Offset="1.0" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
Especificar la opacidad del color en cdigo
Al utilizar cdigo, el mtodo FromArgb esttico permite especificar un valor alfa al crear un color. El mtodo
toma cuatro parmetros de tipo Byte. El primer parmetro especifica el canal alfa del color; los otros tres, los
valores de rojo, verde y azul del color. Cada valor debe estar comprendido entre 0 y 255, ambos inclusive. Un
valor alfa de 0 especifica un color completamente transparente, mientras que un valor alfa de 255, un color
totalmente opaco. En el siguiente ejemplo, se usa el mtodo FromArgb para crear dos colores. El primer color
es parcialmente transparente (tiene un valor alfa de 32), mientras que el segundo es totalmente opaco.
LinearGradientBrush myLinearGradientBrush = new LinearGradientBrush();
// This gradient stop is partially transparent.
myLinearGradientBrush.GradientStops.Add(new GradientStop(Color.FromArgb(32, 0, 0, 255),
0.0));
// This gradient stop is fully opaque.
myLinearGradientBrush.GradientStops.Add(new GradientStop(Color.FromArgb(255, 0, 0, 255),
1.0));
Rectangle myRectangle = new Rectangle();
myRectangle.Width = 100;
myRectangle.Height = 100;
myRectangle.Fill = myLinearGradientBrush;
O bien, puede utilizar el mtodo FromScRgb, que permite usar los valores de ScRGB para crear un color.
Pintar con imgenes, dibujos, elementos visuales y modelos
Las clases ImageBrush, DrawingBrush y VisualBrush permiten pintar un rea con imgenes, dibujos o
elementos visuales.

7.3.2.5. Pintar con Imgenes, Dibujos y Elementos Visuales


En este tema se describe cmo utilizar objetos ImageBrush, DrawingBrushy VisualBrush para pintar una rea
con una imagen o con un objeto Drawing o Visual.
Pintar un rea con una imagen

MCT: Luis Dueas

Pag 360 de 473

Manual de Windows Presentation Foundation


Un ImageBrush pinta una rea con un objeto ImageSource. El tipo ms comn de ImageSource para su uso
con ImageBrush es BitmapImage, que describe un grfico de mapa de bits. Puede utilizar un objeto
DrawingImage para pintar mediante un objeto Drawing, pero es ms fcil utilizar un objeto DrawingBrush en su
lugar.
Para pintar con ImageBrush, cree un objeto BitmapImage y utilcelo para cargar el contenido del mapa de bits.
A continuacin, utilice BitmapImage para establecer la propiedad ImageSource de ImageBrush. Por ltimo,
aplique ImageBrush al objeto que desea pintar. En Lenguaje de marcado de aplicaciones extensible (XAML),
tambin puede establecer simplemente la propiedad ImageSource de ImageBrush en la ruta de acceso de la
imagen que desea cargar.
Al igual que todos los objetos Brush, ImageBrush se puede utilizar para pintar objetos como formas, paneles,
controles y texto. En la ilustracin siguiente se muestran algunos efectos que se pueden lograr con ImageBrush
Objetos pintados con ImageBrush

De manera predeterminada, ImageBrush expande su imagen a fin de rellenar completamente el rea pintada,
con lo que es posible que se distorsione la imagen si el rea pintada tiene una relacin de aspecto diferente que
la imagen. Puede cambiar este comportamiento modificando el valor predeterminado de la propiedad Stretch,
Fill, por uno de estos valores: None, Uniform o UniformToFill. Dado que ImageBrush es un tipo de TileBrush,
puede especificar con exactitud de qu modo el pincel de imagen rellenar el rea de salida, e incluso crear
tramas.
Ejemplo: Pintar un objeto con una imagen de mapa de bits
En el ejemplo siguiente se usa un objeto ImageBrush para pintar la propiedad Background de un objeto
Canvas.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Microsoft.Samples.BrushExamples.ImageBrushExample"
WindowTitle="ImageBrush Example" Background="White">
<StackPanel>
<Canvas
Height="200" Width="300">
<Canvas.Background>
<ImageBrush ImageSource="sampleImages\Waterlilies.jpg" />
</Canvas.Background>
</Canvas>
</StackPanel>
</Page>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Microsoft.Samples.BrushExamples
{
public class ImageBrushExample : Page
{
public ImageBrushExample()
{

MCT: Luis Dueas

Pag 361 de 473

Manual de Windows Presentation Foundation


StackPanel mainPanel = new StackPanel();
canvasBackgroundExample(mainPanel);
this.Content = mainPanel;
}
private void canvasBackgroundExample(Panel mainPanel)
{
BitmapImage theImage = new BitmapImage
(new Uri("sampleImages\\Waterlilies.jpg", UriKind.Relative));
ImageBrush myImageBrush = new ImageBrush(theImage);
Canvas myCanvas = new Canvas();
myCanvas.Width = 300;
myCanvas.Height = 200;
myCanvas.Background = myImageBrush;
mainPanel.Children.Add(myCanvas);
}
}
}
Pintar un rea con un dibujo
Un objeto DrawingBrush permite pintar una rea con formas, texto, imgenes y vdeo. Las formas contenidas
en un pincel de dibujo pueden pintarse a su vez con un color slido, un degradado, una imagen o incluso un
objeto DrawingBrush. En la siguiente ilustracin se muestran algunos usos de DrawingBrush.
Objetos pintados mediante DrawingBrush

Un objeto DrawingBrush pinta un rea con un objeto Drawing. Un objeto de dibujo, o Drawing, describe el
contenido visible, como una forma, un mapa de bits, vdeo o una lnea de texto. Los diferentes tipos de dibujos
describen tipos diferentes de contenido. A continuacin, se muestra una lista de tipos diferentes de objetos de
dibujo.

GeometryDrawing: dibuja una forma.


ImageDrawing: dibuja una imagen.
GlyphRunDrawing: dibuja texto.
VideoDrawing: reproduce un archivo de audio o vdeo.
DrawingGroup: dibuja otros dibujos. Utilice un grupo de dibujo para combinar otros dibujos en un
nico dibujo compuesto.

Al igual que un objeto ImageBrush, un objeto DrawingBrush expande su propiedad Drawing para rellenar su
rea de salida. Puede invalidar este comportamiento cambiando el valor predeterminado (Fill) de la propiedad
Stretch.
Ejemplo: Pintar un objeto con un dibujo
En el ejemplo siguiente se muestra cmo pintar un objeto con un dibujo de tres elipses. GeometryDrawing se
utiliza para describir las elipses.
<Button Content="A Button">
<Button.Background>
<DrawingBrush>
<DrawingBrush.Drawing>
<GeometryDrawing Brush="LightBlue">
<GeometryDrawing.Geometry>
<GeometryGroup>

MCT: Luis Dueas

Pag 362 de 473

Manual de Windows Presentation Foundation


<EllipseGeometry RadiusX="12.5" RadiusY="25" Center="25,50" />
<EllipseGeometry RadiusX="12.5" RadiusY="25" Center="50,50" />
<EllipseGeometry RadiusX="12.5" RadiusY="25" Center="75,50" />
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="1" Brush="Gray" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Button.Background>
</Button>
// Create a DrawingBrush.
DrawingBrush myDrawingBrush = new DrawingBrush();
// Create a drawing.
GeometryDrawing myGeometryDrawing = new GeometryDrawing();
myGeometryDrawing.Brush = Brushes.LightBlue;
myGeometryDrawing.Pen = new Pen(Brushes.Gray, 1);
GeometryGroup ellipses = new GeometryGroup();
ellipses.Children.Add(new EllipseGeometry(new Point(25,50), 12.5, 25));
ellipses.Children.Add(new EllipseGeometry(new Point(50,50), 12.5, 25));
ellipses.Children.Add(new EllipseGeometry(new Point(75,50), 12.5, 25));
myGeometryDrawing.Geometry = ellipses;
myDrawingBrush.Drawing = myGeometryDrawing;
Button myButton = new Button();
myButton.Content = "A Button";
// Use the DrawingBrush to paint the button's background.
myButton.Background = myDrawingBrush;
Pintar una rea con un objeto visual
El ms verstil y eficaz de todos los pinceles, VisualBrush, pinta una rea con un objeto Visual. Un objeto Visual
es un tipo de grfico de bajo nivel que acta como antecesor de muchos componentes grficos tiles. Por
ejemplo, las clases Window, FrameworkElement y Control son tipos de objetos Visual. Con VisualBrush, puede
pintar reas con casi cualquier objeto grfico de Windows Presentation Foundation (WPF).
Nota:
Aunque VisualBrush es un tipo del objeto Freezable, no se puede inmovilizar (establecer como de slo
lectura) cuando su propiedad Visual est establecida en un valor distinto de null.
Hay dos maneras de especificar el contenido de la propiedad Visual de un objeto VisualBrush.

Cree un nuevo objeto Visual y utilcelo para establecer la propiedad Visual de VisualBrush.
Utilice un objeto Visual existente, que crea una imagen duplicada del objeto Visual de destino. A
continuacin, puede utilizar VisualBrush para crear efectos interesantes, tales como reflexiones y
ampliaciones.

Cuando se define un nuevo objeto Visual para VisualBrush y ese objeto Visual es un elemento UIElement (por
ejemplo, un panel o un control), el sistema del diseo se ejecuta en el UIElement y sus elementos secundarios
cuando la propiedad AutoLayoutContent est establecida en true. Sin embargo, el UIElement raz queda aislado
esencialmente del resto del sistema: los estilos y el diseo externo no puede penetrar este lmite. Por
consiguiente, debe especificar explcitamente el tamao del elemento UIElement raz, porque su nico elemento
primario es VisualBrush y, por consiguiente, su tamao no se puede ajustar automticamente al rea pintada.
Al igual que ImageBrush y DrawingBrush, un objeto VisualBrush expande su contenido a fin de rellenar su rea
de salida. Puede invalidar este comportamiento cambiando el valor predeterminado (Fill) de la propiedad
Stretch.
Ejemplo: Pintar un objeto con un objeto visual

MCT: Luis Dueas

Pag 363 de 473

Manual de Windows Presentation Foundation


En el ejemplo siguiente, se utilizan varios controles y un panel para pintar un rectngulo.
<Rectangle Width="150" Height="150" Stroke="Black" Margin="5,0,5,0">
<Rectangle.Fill>
<VisualBrush>
<VisualBrush.Visual>
<StackPanel Background="White">
<Rectangle Width="25" Height="25" Fill="Red" Margin="2" />
<TextBlock FontSize="10pt" Margin="2">Hello, World!</TextBlock>
<Button Margin="2">A Button</Button>
</StackPanel>
</VisualBrush.Visual>
</VisualBrush>
</Rectangle.Fill>
</Rectangle>
VisualBrush myVisualBrush = new VisualBrush();
// Create the visual brush's contents.
StackPanel myStackPanel = new StackPanel();
myStackPanel.Background = Brushes.White;
Rectangle redRectangle = new Rectangle();
redRectangle.Width = 25;
redRectangle.Height =25;
redRectangle.Fill = Brushes.Red;
redRectangle.Margin = new Thickness(2);
myStackPanel.Children.Add(redRectangle);
TextBlock someText = new TextBlock();
FontSizeConverter myFontSizeConverter = new FontSizeConverter();
someText.FontSize = (double)myFontSizeConverter.ConvertFrom("10pt");
someText.Text = "Hello, World!";
someText.Margin = new Thickness(2);
myStackPanel.Children.Add(someText);
Button aButton = new Button();
aButton.Content = "A Button";
aButton.Margin = new Thickness(2);
myStackPanel.Children.Add(aButton);
// Use myStackPanel as myVisualBrush's content.
myVisualBrush.Visual = myStackPanel;
// Create a rectangle to paint.
Rectangle myRectangle = new Rectangle();
myRectangle.Width = 150;
myRectangle.Height = 150;
myRectangle.Stroke = Brushes.Black;
myRectangle.Margin = new Thickness(5,0,5,0);
// Use myVisualBrush to paint myRectangle.
myRectangle.Fill = myVisualBrush;
Ejemplo: Crear una reflexin
En el ejemplo anterior se ha mostrado cmo crear un nuevo objeto Visual para su uso como fondo. Tambin
puede utilizar un objeto VisualBrush para mostrar un objeto visual existente; esta funcin permite producir
efectos visuales interesantes, tales como reflexiones y ampliaciones. En el ejemplo siguiente se usa VisualBrush
para crear una reflexin de un Border que contiene varios elementos. En la ilustracin siguiente se muestra el
resultado de aplicar este ejemplo.
Objeto visual reflejado

MCT: Luis Dueas

Pag 364 de 473

Manual de Windows Presentation Foundation


using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;
using System.IO;
using System.Collections.ObjectModel;
using System.Windows.Shapes;
namespace SDKSample
{
public partial class ReflectionExample : Page
{
public ReflectionExample()
{
// Create a name scope for the page.
NameScope.SetNameScope(this, new NameScope());
this.Background = Brushes.Black;
StackPanel myStackPanel = new StackPanel();
myStackPanel.Margin = new Thickness(50);
Border myReflectedBorder = new Border();
this.RegisterName("ReflectedVisual", myReflectedBorder);
// Create a gradient background for the border.
GradientStop firstStop = new GradientStop();
firstStop.Offset = 0.0;
Color firstStopColor = new Color();
firstStopColor.R = 204;
firstStopColor.G = 204;
firstStopColor.B = 255;
firstStopColor.A = 255;
firstStop.Color = firstStopColor;
GradientStop secondStop = new GradientStop();
secondStop.Offset = 1.0;
secondStop.Color = Colors.White;
GradientStopCollection myGradientStopCollection = new GradientStopCollection();
myGradientStopCollection.Add(firstStop);
myGradientStopCollection.Add(secondStop);
LinearGradientBrush myLinearGradientBrush = new LinearGradientBrush();
myLinearGradientBrush.StartPoint = new Point(0, 0.5);
myLinearGradientBrush.EndPoint = new Point(1, 0.5);
myLinearGradientBrush.GradientStops = myGradientStopCollection;
myReflectedBorder.Background = myLinearGradientBrush;
// Add contents to the border.
StackPanel borderStackPanel = new StackPanel();
borderStackPanel.Orientation = Orientation.Horizontal;
borderStackPanel.Margin = new Thickness(10);
TextBlock myTextBlock = new TextBlock();
myTextBlock.TextWrapping = TextWrapping.Wrap;
myTextBlock.Width = 200;
myTextBlock.Text = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit." +
" Suspendisse vel ante. Donec luctus tortor sit amet est." +
" Nullam pulvinar odio et wisi." +
" Pellentesque quis magna. Sed pellentesque." +
" Nulla euismod." +
"Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas.";
borderStackPanel.Children.Add(myTextBlock);
StackPanel ellipseStackPanel = new StackPanel();
Ellipse ellipse1 = new Ellipse();
ellipse1.Margin = new Thickness(10);
ellipse1.Height = 50;

MCT: Luis Dueas

Pag 365 de 473

Manual de Windows Presentation Foundation


ellipse1.Width = 50;
ellipse1.Fill = Brushes.Black;
ellipseStackPanel.Children.Add(ellipse1);
Ellipse ellipse2 = new Ellipse();
ellipse2.Margin = new Thickness(10);
ellipse2.Height = 50;
ellipse2.Width = 50;
ellipse2.Fill = Brushes.Black;
ellipseStackPanel.Children.Add(ellipse2);
Ellipse ellipse3 = new Ellipse();
ellipse3.Margin = new Thickness(10);
ellipse3.Height = 50;
ellipse3.Width = 50;
ellipse3.Fill = Brushes.Black;
ellipseStackPanel.Children.Add(ellipse3);
borderStackPanel.Children.Add(ellipseStackPanel);
myReflectedBorder.Child = borderStackPanel;
// Create divider rectangle
Rectangle dividerRectangle = new Rectangle();
dividerRectangle.Height = 1;
dividerRectangle.Fill = Brushes.Gray;
dividerRectangle.HorizontalAlignment = HorizontalAlignment.Stretch;
// Create the object to contain the reflection.
Rectangle reflectionRectangle = new Rectangle();
// Bind the height of the rectangle to the border height.
Binding heightBinding = new Binding();
heightBinding.ElementName = "ReflectedVisual";
heightBinding.Path = new PropertyPath(Rectangle.HeightProperty);
BindingOperations.SetBinding(reflectionRectangle, Rectangle.HeightProperty,
heightBinding);
// Bind the width of the rectangle to the border width.
Binding widthBinding = new Binding();
widthBinding.ElementName = "ReflectedVisual";
widthBinding.Path = new PropertyPath(Rectangle.WidthProperty);
BindingOperations.SetBinding(reflectionRectangle, Rectangle.WidthProperty,
widthBinding);
// Creates the reflection.
VisualBrush myVisualBrush = new VisualBrush();
myVisualBrush.Opacity = 0.75;
myVisualBrush.Stretch = Stretch.None;
Binding reflectionBinding = new Binding();
reflectionBinding.ElementName = "ReflectedVisual";
BindingOperations.SetBinding(myVisualBrush, VisualBrush.VisualProperty,
reflectionBinding);
ScaleTransform myScaleTransform = new ScaleTransform();
myScaleTransform.ScaleX = 1;
myScaleTransform.ScaleY = -1;
TranslateTransform myTranslateTransform = new TranslateTransform();
myTranslateTransform.Y = 1;
TransformGroup myTransformGroup = new TransformGroup();
myTransformGroup.Children.Add(myScaleTransform);
myTransformGroup.Children.Add(myTranslateTransform);
myVisualBrush.RelativeTransform = myTransformGroup;
reflectionRectangle.Fill = myVisualBrush;
// Create a gradient background for the border.
GradientStop firstStop2 = new GradientStop();
firstStop2.Offset = 0.0;
Color c1 = new Color();
c1.R = 0;
c1.G = 0;
c1.B = 0;
c1.A = 255;

MCT: Luis Dueas

Pag 366 de 473

Manual de Windows Presentation Foundation


firstStop2.Color = c1;
GradientStop secondStop2 = new GradientStop();
secondStop2.Offset = 0.5;
Color c2 = new Color();
c2.R = 0;
c2.G = 0;
c2.B = 0;
c2.A = 51;
firstStop2.Color = c2;
GradientStop thirdStop = new GradientStop();
thirdStop.Offset = 0.75;
Color c3 = new Color();
c3.R = 0;
c3.G = 0;
c3.B = 0;
c3.A = 0;
thirdStop.Color = c3;
GradientStopCollection myGradientStopCollection2=new GradientStopCollection();
myGradientStopCollection2.Add(firstStop2);
myGradientStopCollection2.Add(secondStop2);
myGradientStopCollection2.Add(thirdStop);
LinearGradientBrush myLinearGradientBrush2 = new LinearGradientBrush();
myLinearGradientBrush2.StartPoint = new Point(0.5, 0);
myLinearGradientBrush2.EndPoint = new Point(0.5, 1);
myLinearGradientBrush2.GradientStops = myGradientStopCollection2;
reflectionRectangle.OpacityMask = myLinearGradientBrush2;
BlurBitmapEffect myBlurBitmapEffect = new BlurBitmapEffect();
myBlurBitmapEffect.Radius = 1.5;
reflectionRectangle.BitmapEffect = myBlurBitmapEffect;
myStackPanel.Children.Add(myReflectedBorder);
myStackPanel.Children.Add(dividerRectangle);
myStackPanel.Children.Add(reflectionRectangle);
this.Content = myStackPanel;
}
/*
<Rectangle
Height="{Binding Path=ActualHeight, ElementName=ReflectedVisual}"
Width="{Binding Path=ActualWidth, ElementName=ReflectedVisual}">
<Rectangle.OpacityMask>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#FF000000" Offset="0.0" />
<GradientStop Color="#33000000" Offset="0.5" />
<GradientStop Color="#00000000" Offset="0.75" />
</LinearGradientBrush>
</Rectangle.OpacityMask>
<Rectangle.BitmapEffect>
<BlurBitmapEffect Radius="1.5" />
</Rectangle.BitmapEffect>
</Rectangle>
</StackPanel>
</Page>
*/
}
}
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="Black">
<StackPanel Margin="50">
<!-- The object to reflect. -->
<Border Name="ReflectedVisual" Width="400">

MCT: Luis Dueas

Pag 367 de 473

Manual de Windows Presentation Foundation


<Border.Background>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
<GradientStop Offset="0.0" Color="#CCCCFF" />
<GradientStop Offset="1.0" Color="White" />
</LinearGradientBrush>
</Border.Background>
<StackPanel Orientation="Horizontal" Margin="10">
<TextBlock TextWrapping="Wrap" Width="200" Margin="10">
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Suspendisse vel ante. Donec luctus tortor sit amet est.
Nullam pulvinar odio et wisi.
Pellentesque quis magna. Sed pellentesque.
Nulla euismod.
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac
turpis egestas.
</TextBlock>
<StackPanel>
<Ellipse Margin="10" Height="50" Width="50" Fill="Black" />
<Ellipse Margin="10" Height="50" Width="50" Fill="Black" />
<Ellipse Margin="10" Height="50" Width="50" Fill="Black" />
</StackPanel>
</StackPanel>
</Border>
<Rectangle Height="1" Fill="Gray" HorizontalAlignment="Stretch" />
<!-- The object to contain the reflection.-->
<Rectangle Height="{Binding Path=ActualHeight, ElementName=ReflectedVisual}"
Width="{Binding Path=ActualWidth, ElementName=ReflectedVisual}">
<Rectangle.Fill>
<!-- Creates the reflection. -->
<VisualBrush Opacity="0.75" Stretch="None"
Visual="{Binding ElementName=ReflectedVisual}">
<VisualBrush.RelativeTransform>
<!-- Flip the reflection. -->
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="-1" />
<TranslateTransform Y="1" />
</TransformGroup>
</VisualBrush.RelativeTransform>
</VisualBrush>
</Rectangle.Fill>
<Rectangle.OpacityMask>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#FF000000" Offset="0.0" />
<GradientStop Color="#33000000" Offset="0.5" />
<GradientStop Color="#00000000" Offset="0.75" />
</LinearGradientBrush>
</Rectangle.OpacityMask>
<Rectangle.BitmapEffect>
<BlurBitmapEffect Radius="1.5" />
</Rectangle.BitmapEffect>
</Rectangle>
</StackPanel>
</Page>
Caractersticas de TileBrush
ImageBrush, DrawingBrush y VisualBrush son tipos de objetos TileBrush. Los objetos TileBrush proporcionan un
gran control sobre cmo se pinta un rea con una imagen, un dibujo o un objeto visual. Por ejemplo, en lugar
de limitarse a pintar una rea con una sola imagen expandida, puede hacerlo con una serie de mosaicos de la
imagen que crean una trama.
TileBrush tiene tres componentes primarios: el contenido, los mosaicos y el rea de salida.

MCT: Luis Dueas

Pag 368 de 473

Manual de Windows Presentation Foundation


Componentes de TileBrush con un solo mosaico

Componentes de TileBrush con varios mosaicos

7.3.2.6. Informacin General sobre Objetos TileBrush


Los objetos TileBrush proporcionan un gran control sobre cmo se pinta un rea con una imagen, o con un
objeto Drawing o Visual. En este tema se describe cmo utilizar las caractersticas de TileBrush para obtener un
mayor control sobre cmo se pinta un rea con ImageBrush, DrawingBrush o VisualBrush.
Pintar un rea con mosaicos
ImageBrush, DrawingBrush y VisualBrush son tipos de objetos TileBrush. Los pinceles de mosaico le
proporcionan un gran control sobre cmo se pinta un rea con una imagen, un dibujo o un objeto visual. Por
ejemplo, en lugar de limitarse a pintar una rea con una sola imagen expandida, puede hacerlo con una serie
de mosaicos de la imagen que crean una trama.
Al pintar un rea con un pincel de mosaico se utilizan tres componentes: el contenido, el mosaico base y el rea
de salida.
Componentes de TileBrush con un solo mosaico

Componentes de TileBrush con Tile como TileMode

El rea de salida es el rea que se pinta, como la propiedad Fill de un objeto Ellipse o la propiedad Background
de un objeto Button. En las secciones siguientes se describen los otros dos componentes de TileBrush.

MCT: Luis Dueas

Pag 369 de 473

Manual de Windows Presentation Foundation


Contenido del pincel
Hay tres tipos diferentes de TileBrush y cada uno de ellos pinta con un tipo diferente de contenido.

Si el pincel es ImageBrush, este contenido es una imagen. La propiedad ImageSource especifica el


contenido de ImageBrush.

Si el pincel es DrawingBrush, este contenido es un dibujo. La propiedad Drawing especifica el


contenido de DrawingBrush.

Si el pincel es VisualBrush, este contenido es un objeto visual. La propiedad Visual especifica el


contenido de VisualBrush.

Puede especificar la posicin y las dimensiones del contenido de TileBrush mediante la propiedad Viewbox,
aunque es frecuente dejar la propiedad Viewbox establecida en su valor predeterminado. De manera
predeterminada, Viewbox se configura para contener completamente el contenido del pincel.
Mosaico base
Un objeto TileBrush proyecta su contenido en un mosaico base. La propiedad Stretch controla cmo se expande
el contenido de TileBrush para rellenar el mosaico base. La propiedad Stretch acepta los valores siguientes,
definidos por la enumeracin Stretch:

None: el contenido del pincel no se expande para rellenar el mosaico.


Fill: se ajusta la escala del contenido del pincel para ajustarla al mosaico. Dado que la escala del alto y
del ancho del contenido se ajusta de manera independiente, puede que no se conserve la relacin de
aspecto original del contenido. Es decir, puede que el contenido del pincel quede distorsionado al
rellenar completamente el mosaico de salida.

Uniform: se ajusta la escala del contenido del pincel para que se ajuste completamente dentro del
mosaico. Se conserva la relacin de aspecto del contenido.

UniformToFill: se ajusta la escala del contenido del pincel para que rellene completamente el rea de
salida conservando la relacin de aspecto original del contenido.

En la siguiente imagen se ilustran los diferentes valores de Stretch.

En el ejemplo siguiente, se establece el contenido de un objeto ImageBrush de modo que no se expanda para
rellenar el rea de salida.
<Rectangle Width="125" Height="175" Stroke="Black" StrokeThickness="1" Margin="0,0,5,0">
<Rectangle.Fill>
<ImageBrush Stretch="None" ImageSource="sampleImages\testImage.gif"/>
</Rectangle.Fill>
</Rectangle>
// Create a rectangle.
Rectangle myRectangle = new Rectangle();
myRectangle.Width = 125;
myRectangle.Height = 175;
myRectangle.Stroke = Brushes.Black;
myRectangle.StrokeThickness = 1;
myRectangle.Margin = new Thickness(0,5,0,0);
// Load the image.
BitmapImage theImage = new BitmapImage(new Uri("sampleImages\\testImage.gif",
UriKind.Relative));
ImageBrush myImageBrush = new ImageBrush(theImage);
// Configure the brush so that it doesn't stretch its image to fill the rectangle.
myImageBrush.Stretch = Stretch.None;

MCT: Luis Dueas

Pag 370 de 473

Manual de Windows Presentation Foundation


// Use the ImageBrush to paint the rectangle's background.
myRectangle.Fill = myImageBrush;
De manera predeterminada, TileBrush genera un mosaico nico (el mosaico base) y expande ese mosaico hasta
rellenar completamente el rea de salida. Puede cambiar el tamao y la posicin del mosaico base
estableciendo las propiedades Viewport y ViewportUnits.
Tamao del mosaico base
La propiedad Viewport determina el tamao y la posicin del mosaico base; la propiedad ViewportUnits
determina si Viewport se especifica mediante coordenadas absolutas o relativas. Si las coordenadas son
relativas, son relativas al tamao del rea de salida. El punto (0,0) representa la esquina superior izquierda del
rea de salida y (1,1) representa la esquina inferior derecha del rea de salida. Para especificar que la
propiedad Viewport utiliza coordenadas absolutas, establezca la propiedad ViewportUnits en Absolute.
En la ilustracin siguiente se muestra la diferencia entre una propiedad ViewportUnits absoluta y relativa para
TileBrush. Observe que ambas ilustraciones muestran una trama de mosaico; en la seccin siguiente se
describe cmo especificar una trama de mosaico.

En el ejemplo siguiente, se utiliza una imagen para crear un mosaico con un ancho y un alto del 50%. El
mosaico base se sita en (0,0) en el rea de salida.
<Rectangle Width="50" Height="100">
<Rectangle.Fill>
<!-- Paints an area with 4 tiles. -->
<ImageBrush ImageSource="sampleImages\cherries_larger.jpg" Viewport="0,0,0.5,0.5"
ViewportUnits="RelativeToBoundingBox" TileMode="Tile" />
</Rectangle.Fill>
</Rectangle>
// Create a rectangle.
Rectangle myRectangle = new Rectangle();
myRectangle.Width = 50;
myRectangle.Height = 100;
// Load the image.
BitmapImage theImage = new BitmapImage(new Uri("sampleImages\\cherries_larger.jpg",
UriKind.Relative));
ImageBrush myImageBrush = new ImageBrush(theImage);
// Create tiles that are 1/4 the size of the output area.
myImageBrush.Viewport = new Rect(0,0,0.25,0.25);
myImageBrush.ViewportUnits = BrushMappingMode.RelativeToBoundingBox;
// Set the tile mode to Tile.
myImageBrush.TileMode = TileMode.Tile;
// Use the ImageBrush to paint the rectangle's background.
myRectangle.Fill = myImageBrush;

MCT: Luis Dueas

Pag 371 de 473

Manual de Windows Presentation Foundation


En el ejemplo siguiente se establecen los mosaicos de ImageBrush en un tamao de 25 por 25 pxeles
independientes del dispositivo. Dado que el valor de ViewportUnits es absoluto, los mosaicos de ImageBrush
siempre tienen 25 por 25 pxeles, independientemente del rea pintada.
<Rectangle Width="50" Height="100">
<Rectangle.Fill>
<!-- Paints an area with 25 x 25 tiles. -->
<ImageBrush ImageSource="sampleImages\cherries_larger.jpg" Viewport="0,0,25,25"
ViewportUnits="Absolute" TileMode="Tile" />
</Rectangle.Fill>
</Rectangle>
// Create a rectangle.
Rectangle myRectangle = new Rectangle();
myRectangle.Width = 50;
myRectangle.Height = 100;
// Load the image.
BitmapImage theImage = new BitmapImage(new Uri("sampleImages\\cherries_larger.jpg",
UriKind.Relative));
ImageBrush myImageBrush = new ImageBrush(theImage);
// Create tiles that are 25 x 25, regardless of the size of the output area.
myImageBrush.Viewport = new Rect(0, 0, 25, 25);
myImageBrush.ViewportUnits = BrushMappingMode.Absolute;
// Set the tile mode to Tile.
myImageBrush.TileMode = TileMode.Tile;
// Use the ImageBrush to paint the rectangle's background.
myRectangle.Fill = myImageBrush;
Comportamiento de mosaico
TileBrush genera una trama de mosaico cuando su mosaico base no rellena completamente el rea de salida y
se especifica un modo de mosaico distinto de None. Cuando el mosaico de un pincel de mosaico no rellena
completamente el rea de salida, su propiedad TileMode especifica si el mosaico base se debe duplicar para
rellenar el rea de salida y, en caso afirmativo, cmo se debe duplicar. La propiedad TileMode acepta los
valores siguientes, definidos por la enumeracin TileMode:

None: slo se dibuja el mosaico base.


Tile: se dibuja el mosaico base y el rea restante se rellena repitiendo el mosaico base de tal modo
que el borde derecho de un mosaico sea adyacente al borde izquierdo del siguiente, y lo mismo en
sentido vertical.

FlipX: igual que Tile, pero volteando en sentido horizontal columnas alternas de mosaicos.
FlipY: igual que Tile, pero volteando en sentido vertical filas alternas de mosaicos.
FlipXY: combinacin de FlipX y FlipY.

En la siguiente imagen se ilustran los diferentes modos de mosaico.

En el ejemplo siguiente, se utiliza una imagen para pintar un rectngulo de 100 pxeles de ancho por 100
pxeles de alto. La propiedad Viewport del pincel se ha establecido en 0,0,0.25,0.25; el mosaico base del pincel
ocupa 1/4 del rea de salida. La propiedad TileMode del pincel se establece en FlipXY, para que rellene el
rectngulo con filas de mosaicos.
<Rectangle Width="100" Height="100" >
<Rectangle.Fill>
<ImageBrush ImageSource="sampleImages\triangle.jpg" Viewport="0,0,0.25,0.25"
TileMode="FlipXY" />
</Rectangle.Fill>
</Rectangle>
// Create a rectangle.
Rectangle myRectangle = new Rectangle();

MCT: Luis Dueas

Pag 372 de 473

Manual de Windows Presentation Foundation


myRectangle.Width = 100;
myRectangle.Height = 100;
// Load the image.
BitmapImage theImage = new BitmapImage(new Uri("sampleImages\\triangle.jpg",
UriKind.Relative));
ImageBrush myImageBrush = new ImageBrush(theImage);
// Create tiles that are 1/4 the size of the output area.
myImageBrush.Viewport = new Rect(0,0,0.25,0.25);
// Set the tile mode to FlipXY.
myImageBrush.TileMode = TileMode.FlipXY;
// Use the ImageBrush to paint the rectangle's background.
myRectangle.Fill = myImageBrush;

7.3.2.7. Temas Cmo de Pinceles


En los temas siguientes se muestra cmo utilizar los pinceles de Windows Presentation Foundation (WPF) para
pintar contenido en la pantalla.

7.3.2.7.1. Cmo: Animar el Color o la Opacidad de un Objeto SolidColorBrush


En este ejemplo se muestra cmo animar las propiedades Color y Opacity de un objeto SolidColorBrush.
Ejemplo
En el ejemplo siguiente se utilizan tres animaciones para animar las propiedades Color y Opacity de un objeto
SolidColorBrush.

La primera animacin, ColorAnimation, cambia el color del pincel a Gray cuando el mouse entra en el
rectngulo.

La animacin siguiente, ColorAnimation, cambia el color del pincel a Orange cuando el mouse sale del
rectngulo.

La ltima animacin, DoubleAnimation, cambia la opacidad del pincel a 0.0 cuando se presiona el

botn primario del mouse.


using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Input;
namespace Microsoft.Samples.Animation
{
/// <summary>
/// This example shows how to animate the Opacity and Color
/// properties of a SolidColorBrush.
/// </summary>
public class SolidColorBrushExample : Page
{
public SolidColorBrushExample()
{
Title = "SolidColorBrush Animation Example";
Background = Brushes.White;
// Create a NameScope for the page so that Storyboards can be used.
NameScope.SetNameScope(this, new NameScope());
// Create a Rectangle.
Rectangle aRectangle = new Rectangle();
aRectangle.Width = 100;
aRectangle.Height = 100;
// Create a SolidColorBrush to paint the rectangle's fill. The Opacity
// and Color properties of the brush will be animated.
SolidColorBrush myAnimatedBrush = new SolidColorBrush();
myAnimatedBrush.Color = Colors.Orange;

MCT: Luis Dueas

Pag 373 de 473

Manual de Windows Presentation Foundation


aRectangle.Fill = myAnimatedBrush;
// Register the brush's name with the page
// so that it can be targeted by storyboards.
this.RegisterName("MyAnimatedBrush", myAnimatedBrush);
// Animate the brush's color to gray when the mouse enters the rectangle.
ColorAnimation mouseEnterColorAnimation = new ColorAnimation();
mouseEnterColorAnimation.To = Colors.Gray;
mouseEnterColorAnimation.Duration = TimeSpan.FromSeconds(1);
Storyboard.SetTargetName(mouseEnterColorAnimation, "MyAnimatedBrush");
Storyboard.SetTargetProperty(mouseEnterColorAnimation,
new PropertyPath(SolidColorBrush.ColorProperty));
Storyboard mouseEnterStoryboard = new Storyboard();
mouseEnterStoryboard.Children.Add(mouseEnterColorAnimation);
aRectangle.MouseEnter += delegate(object sender, MouseEventArgs e)
{
mouseEnterStoryboard.Begin(this);
};
// Animate the brush's color to orange when the mouse leaves the rectangle.
ColorAnimation mouseLeaveColorAnimation = new ColorAnimation();
mouseLeaveColorAnimation.To = Colors.Orange;
mouseLeaveColorAnimation.Duration = TimeSpan.FromSeconds(1);
Storyboard.SetTargetName(mouseLeaveColorAnimation, "MyAnimatedBrush");
Storyboard.SetTargetProperty(mouseLeaveColorAnimation,
new PropertyPath(SolidColorBrush.ColorProperty));
Storyboard mouseLeaveStoryboard = new Storyboard();
mouseLeaveStoryboard.Children.Add(mouseLeaveColorAnimation);
aRectangle.MouseLeave += delegate(object sender, MouseEventArgs e)
{
mouseLeaveStoryboard.Begin(this);
};
// Animate the brush's opacity to 0 and back when
// the left mouse button is pressed over the rectangle.
DoubleAnimation opacityAnimation = new DoubleAnimation();
opacityAnimation.To = 0.0;
opacityAnimation.Duration = TimeSpan.FromSeconds(0.5);
opacityAnimation.AutoReverse = true;
Storyboard.SetTargetName(opacityAnimation, "MyAnimatedBrush");
Storyboard.SetTargetProperty(opacityAnimation,
new PropertyPath(SolidColorBrush.OpacityProperty));
Storyboard mouseLeftButtonDownStoryboard = new Storyboard();
mouseLeftButtonDownStoryboard.Children.Add(opacityAnimation);
aRectangle.MouseLeftButtonDown += delegate(object sender, MouseButtonEventArgs e)
{
mouseLeftButtonDownStoryboard.Begin(this);
};
StackPanel mainPanel = new StackPanel();
mainPanel.Margin = new Thickness(20);
mainPanel.Children.Add(aRectangle);
Content = mainPanel;
}
}
}
<!-- SolidColorBrushExample.xaml
This example shows how to animate the Opacity and Color
properties of a SolidColorBrush.-->
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SolidColorBrush Animation Example" Background="White">
<StackPanel Margin="20">
<!-- The Opacity and Color of the SolidColorBrush

MCT: Luis Dueas

Pag 374 de 473

Manual de Windows Presentation Foundation


used to fill this rectangle is animated. -->
<Rectangle Width="100" Height="100">
<Rectangle.Fill>
<SolidColorBrush x:Name="MyAnimatedBrush" Color="Orange" />
</Rectangle.Fill>
<Rectangle.Triggers>
<!-- Animates the brush's color to gray When the mouse enters the rectangle. -->
<EventTrigger RoutedEvent="Rectangle.MouseEnter">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="MyAnimatedBrush"
Storyboard.TargetProperty="Color" To="Gray" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<!-- Animates the brush's color to orange when the mouse leaves the rectangle. -->
<EventTrigger RoutedEvent="Rectangle.MouseLeave">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="MyAnimatedBrush"
Storyboard.TargetProperty="Color" To="Orange" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<!-- Animates the brush's opacity when the
left mouse button is pressed over the rectangle. -->
<EventTrigger RoutedEvent="Rectangle.MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="MyAnimatedBrush"
Storyboard.TargetProperty="Opacity" To="0.0" Duration="0:0:0.5"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</StackPanel>
</Page>
Para ofrecer coherencia con otros ejemplos de animacin, en las versiones de cdigo de este ejemplo se utiliza
un objeto Storyboard para aplicar sus animaciones. Sin embargo, al aplicar una animacin nica en cdigo, es
ms fcil para utilizar el mtodo BeginAnimation en lugar de utilizar un objeto Storyboard.

7.3.2.7.2. Cmo: Animar la Posicin o Color de un Punto de Degradado


En este ejemplo se muestra cmo animar las propiedades Color y Offset de los objetos GradientStop.
Ejemplo
En el ejemplo siguiente se animan tres puntos de degradado dentro de LinearGradientBrush. En el ejemplo se
utilizan tres animaciones, cada una de las cuales anima un punto de degradado diferente:

La primera animacin, DoubleAnimation, anima la propiedad Offset del primer punto de degradado
desde 0.0 hasta 1.0 y de nuevo hasta 0.0. Como resultado, el primer color del degradado cambia del
lado izquierdo al derecho del rectngulo y vuelve al izquierdo.

La segunda animacin, ColorAnimation, anima la propiedad Color del segundo punto de degradado
desde Purple hasta Yellow y de nuevo hasta Purple. Como resultado, el color central del degradado
cambia del prpura al amarillo y vuelve al prpura.

La tercera animacin, ColorAnimation, anima la opacidad de la propiedad Color del tercer punto de
degradado en -1 y vuelve al punto inicial. Como resultado, el tercer color del degradado se desvanece
y, a continuacin, se vuelve de nuevo opaco.

MCT: Luis Dueas

Pag 375 de 473

Manual de Windows Presentation Foundation


using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace BrushesIntroduction
{
public class GradientStopAnimationExample : Page
{
public GradientStopAnimationExample()
{
Title = "GradientStop Animation Example";
Background = Brushes.White;
// Create a NameScope for the page so that Storyboards can be used.
NameScope.SetNameScope(this, new NameScope());
// Create a rectangle. This rectangle Hill be painted with a gradient.
Rectangle aRectangle = new Rectangle();
aRectangle.Width = 200;
aRectangle.Height = 100;
aRectangle.Stroke = Brushes.Black;
aRectangle.StrokeThickness = 1;
// Create a LinearGradientBrush to paint the rectangle's fill.
LinearGradientBrush gradientBrush = new LinearGradientBrush();
// Create gradient stops for the brush.
GradientStop stop1 = new GradientStop(Colors.MediumBlue, 0.0);
GradientStop stop2 = new GradientStop(Colors.Purple, 0.5);
GradientStop stop3 = new GradientStop(Colors.Red, 1.0);
// Register a name for each gradient stop with the
// page so that they can be animated by a storyboard.
this.RegisterName("GradientStop1", stop1);
this.RegisterName("GradientStop2", stop2);
this.RegisterName("GradientStop3", stop3);
// Add the stops to the brush.
gradientBrush.GradientStops.Add(stop1);
gradientBrush.GradientStops.Add(stop2);
gradientBrush.GradientStops.Add(stop3);
// Apply the brush to the rectangle.
aRectangle.Fill = gradientBrush;
// Animate the first gradient stop's offset from
// 0.0 to 1.0 and then back to 0.0.
DoubleAnimation offsetAnimation = new DoubleAnimation();
offsetAnimation.From = 0.0;
offsetAnimation.To = 1.0;
offsetAnimation.Duration = TimeSpan.FromSeconds(1.5);
offsetAnimation.AutoReverse = true;
Storyboard.SetTargetName(offsetAnimation, "GradientStop1");
Storyboard.SetTargetProperty(offsetAnimation,
new PropertyPath(GradientStop.OffsetProperty));
// Animate the second gradient stop's color from
// Purple to Yellow and then back to Purple.
ColorAnimation gradientStopColorAnimation = new ColorAnimation();
gradientStopColorAnimation.From = Colors.Purple;
gradientStopColorAnimation.To = Colors.Yellow;
gradientStopColorAnimation.Duration = TimeSpan.FromSeconds(1.5);
gradientStopColorAnimation.AutoReverse = true;
Storyboard.SetTargetName(gradientStopColorAnimation, "GradientStop2");
Storyboard.SetTargetProperty(gradientStopColorAnimation,
new PropertyPath(GradientStop.ColorProperty));
// Set the animation to begin after the first animation ends.
gradientStopColorAnimation.BeginTime = TimeSpan.FromSeconds(3);

MCT: Luis Dueas

Pag 376 de 473

Manual de Windows Presentation Foundation


// Animate the third gradient stop's color so that it becomes transparent.
ColorAnimation opacityAnimation = new ColorAnimation();
// Reduces the target color's alpha value by 1, making the color transparent.
opacityAnimation.By = Color.FromScRgb(-1.0F, 0F, 0F, 0F);
opacityAnimation.Duration = TimeSpan.FromSeconds(1.5);
opacityAnimation.AutoReverse = true;
Storyboard.SetTargetName(opacityAnimation, "GradientStop3");
Storyboard.SetTargetProperty(opacityAnimation,
new PropertyPath(GradientStop.ColorProperty));
// Set the animation to begin after the first two animations have ended.
opacityAnimation.BeginTime = TimeSpan.FromSeconds(6);
// Create a Storyboard to apply the animations.
Storyboard gradientStopAnimationStoryboard = new Storyboard();
gradientStopAnimationStoryboard.Children.Add(offsetAnimation);
gradientStopAnimationStoryboard.Children.Add(gradientStopColorAnimation);
gradientStopAnimationStoryboard.Children.Add(opacityAnimation);
// Begin the storyboard when the left mouse button is
// pressed over the rectangle.
aRectangle.MouseLeftButtonDown += delegate(object sender, MouseButtonEventArgs e)
{
gradientStopAnimationStoryboard.Begin(this);
};
StackPanel mainPanel = new StackPanel();
mainPanel.Margin = new Thickness(10);
mainPanel.Children.Add(aRectangle);
Content = mainPanel;
}
}
}
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="GradientStop Animation Example" Background="White">
<StackPanel Margin="10">
<Rectangle Width="200" Height="100" Stroke="Black" StrokeThickness="1">
<Rectangle.Fill>
<LinearGradientBrush>
<GradientStop x:Name="GradientStop1" Color="MediumBlue" Offset="0.0" />
<GradientStop x:Name="GradientStop2" Color="Purple" Offset="0.5" />
<GradientStop x:Name="GradientStop3" Color="Red" Offset="1.0" />
</LinearGradientBrush>
</Rectangle.Fill>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="GradientStop1"
Storyboard.TargetProperty="Offset" From="0.0" To="1.0" Duration="0:0:1.5"
AutoReverse="True" />
<ColorAnimation Storyboard.TargetName="GradientStop2"
Storyboard.TargetProperty="Color" From="Purple" To="Yellow"
Duration="0:0:1.5" AutoReverse="True" BeginTime="0:0:3" />
<ColorAnimation Storyboard.TargetName="GradientStop3"
Storyboard.TargetProperty="Color" Duration="0:0:1.5"
AutoReverse="True" BeginTime="0:0:6">
<ColorAnimation.By>
<Color ScA="-1" ScR="0" ScB="0" ScG="0" />
</ColorAnimation.By>
</ColorAnimation>
</Storyboard>
</BeginStoryboard>

MCT: Luis Dueas

Pag 377 de 473

Manual de Windows Presentation Foundation


</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</StackPanel>
</Page>
Aunque en este ejemplo se utiliza un objeto LinearGradientBrush, el proceso es el mismo para animar objetos
GradientStop dentro de RadialGradientBrush.

7.3.2.7.3. Cmo: Crear una Reflexin


En este ejemplo se muestra cmo usar un objeto VisualBrush para crear una reflexin. Dado que VisualBrush
puede mostrar un objeto visual existente, puede usar esta funcionalidad para generar efectos visuales
interesantes, como reflexiones y ampliacin.
Ejemplo
En el ejemplo siguiente se usa VisualBrush para crear una reflexin de un Border que contiene varios
elementos. En la ilustracin siguiente se muestra el resultado de aplicar este ejemplo.
Objeto visual reflejado

using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;
using System.IO;
using System.Collections.ObjectModel;
using System.Windows.Shapes;
namespace SDKSample
{
public partial class ReflectionExample : Page
{
public ReflectionExample()
{
// Create a name scope for the page.
NameScope.SetNameScope(this, new NameScope());
this.Background = Brushes.Black;
StackPanel myStackPanel = new StackPanel();
myStackPanel.Margin = new Thickness(50);
Border myReflectedBorder = new Border();
this.RegisterName("ReflectedVisual", myReflectedBorder);
// Create a gradient background for the border.
GradientStop firstStop = new GradientStop();
firstStop.Offset = 0.0;
Color firstStopColor = new Color();
firstStopColor.R = 204;
firstStopColor.G = 204;
firstStopColor.B = 255;
firstStopColor.A = 255;
firstStop.Color = firstStopColor;
GradientStop secondStop = new GradientStop();
secondStop.Offset = 1.0;

MCT: Luis Dueas

Pag 378 de 473

Manual de Windows Presentation Foundation


secondStop.Color = Colors.White;
GradientStopCollection myGradientStopCollection = new GradientStopCollection();
myGradientStopCollection.Add(firstStop);
myGradientStopCollection.Add(secondStop);
LinearGradientBrush myLinearGradientBrush = new LinearGradientBrush();
myLinearGradientBrush.StartPoint = new Point(0, 0.5);
myLinearGradientBrush.EndPoint = new Point(1, 0.5);
myLinearGradientBrush.GradientStops = myGradientStopCollection;
myReflectedBorder.Background = myLinearGradientBrush;
// Add contents to the border.
StackPanel borderStackPanel = new StackPanel();
borderStackPanel.Orientation = Orientation.Horizontal;
borderStackPanel.Margin = new Thickness(10);
TextBlock myTextBlock = new TextBlock();
myTextBlock.TextWrapping = TextWrapping.Wrap;
myTextBlock.Width = 200;
myTextBlock.Text = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit." +
" Suspendisse vel ante. Donec luctus tortor sit amet est." +
" Nullam pulvinar odio et wisi." +
" Pellentesque quis magna. Sed pellentesque." +
" Nulla euismod." +
"Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas.";
borderStackPanel.Children.Add(myTextBlock);
StackPanel ellipseStackPanel = new StackPanel();
Ellipse ellipse1 = new Ellipse();
ellipse1.Margin = new Thickness(10);
ellipse1.Height = 50;
ellipse1.Width = 50;
ellipse1.Fill = Brushes.Black;
ellipseStackPanel.Children.Add(ellipse1);
Ellipse ellipse2 = new Ellipse();
ellipse2.Margin = new Thickness(10);
ellipse2.Height = 50;
ellipse2.Width = 50;
ellipse2.Fill = Brushes.Black;
ellipseStackPanel.Children.Add(ellipse2);
Ellipse ellipse3 = new Ellipse();
ellipse3.Margin = new Thickness(10);
ellipse3.Height = 50;
ellipse3.Width = 50;
ellipse3.Fill = Brushes.Black;
ellipseStackPanel.Children.Add(ellipse3);
borderStackPanel.Children.Add(ellipseStackPanel);
myReflectedBorder.Child = borderStackPanel;
// Create divider rectangle
Rectangle dividerRectangle = new Rectangle();
dividerRectangle.Height = 1;
dividerRectangle.Fill = Brushes.Gray;
dividerRectangle.HorizontalAlignment = HorizontalAlignment.Stretch;
// Create the object to contain the reflection.
Rectangle reflectionRectangle = new Rectangle();
// Bind the height of the rectangle to the border height.
Binding heightBinding = new Binding();
heightBinding.ElementName = "ReflectedVisual";
heightBinding.Path = new PropertyPath(Rectangle.HeightProperty);
BindingOperations.SetBinding(reflectionRectangle, Rectangle.HeightProperty,
heightBinding);
// Bind the width of the rectangle to the border width.
Binding widthBinding = new Binding();
widthBinding.ElementName = "ReflectedVisual";
widthBinding.Path = new PropertyPath(Rectangle.WidthProperty);

MCT: Luis Dueas

Pag 379 de 473

Manual de Windows Presentation Foundation


BindingOperations.SetBinding(reflectionRectangle, Rectangle.WidthProperty,
widthBinding);
// Creates the reflection.
VisualBrush myVisualBrush = new VisualBrush();
myVisualBrush.Opacity = 0.75;
myVisualBrush.Stretch = Stretch.None;
Binding reflectionBinding = new Binding();
reflectionBinding.ElementName = "ReflectedVisual";
BindingOperations.SetBinding(myVisualBrush, VisualBrush.VisualProperty,
reflectionBinding);
ScaleTransform myScaleTransform = new ScaleTransform();
myScaleTransform.ScaleX = 1;
myScaleTransform.ScaleY = -1;
TranslateTransform myTranslateTransform = new TranslateTransform();
myTranslateTransform.Y = 1;
TransformGroup myTransformGroup = new TransformGroup();
myTransformGroup.Children.Add(myScaleTransform);
myTransformGroup.Children.Add(myTranslateTransform);
myVisualBrush.RelativeTransform = myTransformGroup;
reflectionRectangle.Fill = myVisualBrush;
// Create a gradient background for the border.
GradientStop firstStop2 = new GradientStop();
firstStop2.Offset = 0.0;
Color c1 = new Color();
c1.R = 0;
c1.G = 0;
c1.B = 0;
c1.A = 255;
firstStop2.Color = c1;
GradientStop secondStop2 = new GradientStop();
secondStop2.Offset = 0.5;
Color c2 = new Color();
c2.R = 0;
c2.G = 0;
c2.B = 0;
c2.A = 51;
firstStop2.Color = c2;
GradientStop thirdStop = new GradientStop();
thirdStop.Offset = 0.75;
Color c3 = new Color();
c3.R = 0;
c3.G = 0;
c3.B = 0;
c3.A = 0;
thirdStop.Color = c3;
GradientStopCollection myGradientStopCollection2 = new GradientStopCollection();
myGradientStopCollection2.Add(firstStop2);
myGradientStopCollection2.Add(secondStop2);
myGradientStopCollection2.Add(thirdStop);
LinearGradientBrush myLinearGradientBrush2 = new LinearGradientBrush();
myLinearGradientBrush2.StartPoint = new Point(0.5, 0);
myLinearGradientBrush2.EndPoint = new Point(0.5, 1);
myLinearGradientBrush2.GradientStops = myGradientStopCollection2;
reflectionRectangle.OpacityMask = myLinearGradientBrush2;
BlurBitmapEffect myBlurBitmapEffect = new BlurBitmapEffect();
myBlurBitmapEffect.Radius = 1.5;
reflectionRectangle.BitmapEffect = myBlurBitmapEffect;
myStackPanel.Children.Add(myReflectedBorder);
myStackPanel.Children.Add(dividerRectangle);
myStackPanel.Children.Add(reflectionRectangle);
this.Content = myStackPanel;
}

MCT: Luis Dueas

Pag 380 de 473

Manual de Windows Presentation Foundation


/*
<Rectangle
Height="{Binding Path=ActualHeight, ElementName=ReflectedVisual}"
Width="{Binding Path=ActualWidth, ElementName=ReflectedVisual}">
<Rectangle.OpacityMask>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#FF000000" Offset="0.0" />
<GradientStop Color="#33000000" Offset="0.5" />
<GradientStop Color="#00000000" Offset="0.75" />
</LinearGradientBrush>
</Rectangle.OpacityMask>
<Rectangle.BitmapEffect>
<BlurBitmapEffect Radius="1.5" />
</Rectangle.BitmapEffect>
</Rectangle>
</StackPanel>
</Page>
*/
}
}
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Background="Black">
<StackPanel Margin="50">
<!-- The object to reflect. -->
<Border Name="ReflectedVisual" Width="400">
<Border.Background>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
<GradientStop Offset="0.0" Color="#CCCCFF" />
<GradientStop Offset="1.0" Color="White" />
</LinearGradientBrush>
</Border.Background>
<StackPanel Orientation="Horizontal" Margin="10">
<TextBlock TextWrapping="Wrap" Width="200" Margin="10">
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Suspendisse vel ante. Donec luctus tortor sit amet est.
Nullam pulvinar odio et wisi.
Pellentesque quis magna. Sed pellentesque.
Nulla euismod.
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac
turpis egestas.
</TextBlock>
<StackPanel>
<Ellipse Margin="10" Height="50" Width="50" Fill="Black" />
<Ellipse Margin="10" Height="50" Width="50" Fill="Black" />
<Ellipse Margin="10" Height="50" Width="50" Fill="Black" />
</StackPanel>
</StackPanel>
</Border>
<Rectangle Height="1" Fill="Gray" HorizontalAlignment="Stretch" />
<!-- The object to contain the reflection.-->
<Rectangle Height="{Binding Path=ActualHeight, ElementName=ReflectedVisual}"
Width="{Binding Path=ActualWidth, ElementName=ReflectedVisual}">
<Rectangle.Fill>
<!-- Creates the reflection. -->
<VisualBrush
Opacity="0.75" Stretch="None" Visual="{Binding ElementName=ReflectedVisual}">
<VisualBrush.RelativeTransform>
<!-- Flip the reflection. -->
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="-1" />

MCT: Luis Dueas

Pag 381 de 473

Manual de Windows Presentation Foundation


<TranslateTransform Y="1" />
</TransformGroup>
</VisualBrush.RelativeTransform>
</VisualBrush>
</Rectangle.Fill>
<Rectangle.OpacityMask>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#FF000000" Offset="0.0" />
<GradientStop Color="#33000000" Offset="0.5" />
<GradientStop Color="#00000000" Offset="0.75" />
</LinearGradientBrush>
</Rectangle.OpacityMask>
<Rectangle.BitmapEffect>
<BlurBitmapEffect Radius="1.5" />
</Rectangle.BitmapEffect>
</Rectangle>
</StackPanel>
</Page>

7.3.2.7.4. Cmo: Crear Patrones de Mosaico diferentes con un Objeto TileBrush


En este ejemplo se muestra cmo usar la propiedad TileMode de TileBrush para crear un patrn.
La propiedad TileMode permite especificar cmo se repite el contenido de un objeto TileBrush, es decir, en
mosaico para rellenar una rea de salida. Para crear un patrn, TileMode se establece en Tile, FlipX, FlipY o
FlipXY. Tambin se debe establecer la propiedad Viewport del objeto TileBrush para que sea menor que el rea
que est pintando; de lo contrario, slo se genera un mosaico, independientemente del valor de TileMode que
se use.
Ejemplo
En el ejemplo siguiente se crean cinco objetos DrawingBrush, se proporciona a cada uno un valor de TileMode
diferente y se usan para pintar cinco rectngulos. Aunque en este ejemplo se usa la clase DrawingBrush para
mostrar el comportamiento de TileMode, la propiedad TileMode funciona de manera idntica para todos los
objetos TileBrush, es decir, para ImageBrush, VisualBrush y DrawingBrush.
En la ilustracin siguiente se muestra el resultado de aplicar este ejemplo.
Patrones de mosaico creados con la propiedad TileMode

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace BrushesIntroduction
{
public class TileModeExample : Page
{
public TileModeExample()
{
Background = Brushes.White;
Margin = new Thickness(20);
StackPanel mainPanel = new StackPanel();
mainPanel.HorizontalAlignment = HorizontalAlignment.Left;
// Create a Drawing. This will be the DrawingBrushes' content.
PolyLineSegment triangleLinesSegment = new PolyLineSegment();
triangleLinesSegment.Points.Add(new Point(50, 0));

MCT: Luis Dueas

Pag 382 de 473

Manual de Windows Presentation Foundation


triangleLinesSegment.Points.Add(new Point(0, 50));
PathFigure triangleFigure = new PathFigure();
triangleFigure.IsClosed = true;
triangleFigure.StartPoint = new Point(0, 0);
triangleFigure.Segments.Add(triangleLinesSegment);
PathGeometry triangleGeometry = new PathGeometry();
triangleGeometry.Figures.Add(triangleFigure);
GeometryDrawing triangleDrawing = new GeometryDrawing();
triangleDrawing.Geometry = triangleGeometry;
triangleDrawing.Brush = new SolidColorBrush(Color.FromArgb(255, 204, 204, 255));
Pen trianglePen = new Pen(Brushes.Black, 2);
triangleDrawing.Pen = trianglePen;
trianglePen.MiterLimit = 0;
triangleDrawing.Freeze();
// Create the first TileBrush. Its content is not tiled.
DrawingBrush tileBrushWithoutTiling = new DrawingBrush();
tileBrushWithoutTiling.Drawing = triangleDrawing;
tileBrushWithoutTiling.TileMode = TileMode.None;
// Create a rectangle and paint it with the DrawingBrush.
Rectangle noTileExampleRectangle = createExampleRectangle();
noTileExampleRectangle.Fill = tileBrushWithoutTiling;
// Add a header and the rectangle to the main panel.
mainPanel.Children.Add(createExampleHeader("None"));
mainPanel.Children.Add(noTileExampleRectangle);
// Create another TileBrush, this time with tiling.
DrawingBrush tileBrushWithTiling = new DrawingBrush();
tileBrushWithTiling.Drawing = triangleDrawing;
tileBrushWithTiling.TileMode = TileMode.Tile;
// Specify the brush's Viewport. Otherwise, a single tile will be produced
// that fills the entire output area and its TileMode will have no effect.
// This setting uses realtive values to creates four tiles.
tileBrushWithTiling.Viewport = new Rect(0, 0, 0.5, 0.5);
// Create a rectangle and paint it with the DrawingBrush.
Rectangle tilingExampleRectangle = createExampleRectangle();
tilingExampleRectangle.Fill = tileBrushWithTiling;
mainPanel.Children.Add(createExampleHeader("Tile"));
mainPanel.Children.Add(tilingExampleRectangle);
// Create a TileBrush with FlipX tiling.
// The brush's content is flipped horizontally as it is tiled in this example
DrawingBrush tileBrushWithFlipXTiling = new DrawingBrush();
tileBrushWithFlipXTiling.Drawing = triangleDrawing;
tileBrushWithFlipXTiling.TileMode = TileMode.FlipX;
// Specify the brush's Viewport.
tileBrushWithFlipXTiling.Viewport = new Rect(0, 0, 0.5, 0.5);
// Create a rectangle and paint it with the DrawingBrush.
Rectangle flipXTilingExampleRectangle = createExampleRectangle();
flipXTilingExampleRectangle.Fill = tileBrushWithFlipXTiling;
mainPanel.Children.Add(createExampleHeader("FlipX"));
mainPanel.Children.Add(flipXTilingExampleRectangle);
// Create a TileBrush with FlipY tiling.
// The brush's content is flipped vertically as it is tiled in this example
DrawingBrush tileBrushWithFlipYTiling = new DrawingBrush();
tileBrushWithFlipYTiling.Drawing = triangleDrawing;
tileBrushWithFlipYTiling.TileMode = TileMode.FlipX;
// Specify the brush's Viewport.
tileBrushWithFlipYTiling.Viewport = new Rect(0, 0, 0.5, 0.5);
// Create a rectangle and paint it with the DrawingBrush.
Rectangle flipYTilingExampleRectangle = createExampleRectangle();
flipYTilingExampleRectangle.Fill = tileBrushWithFlipYTiling;
mainPanel.Children.Add(createExampleHeader("FlipY"));
mainPanel.Children.Add(flipYTilingExampleRectangle);
// Create a TileBrush with FlipXY tiling.

MCT: Luis Dueas

Pag 383 de 473

Manual de Windows Presentation Foundation


// The brush's content is flipped vertically as it is tiled in this example
DrawingBrush tileBrushWithFlipXYTiling = new DrawingBrush();
tileBrushWithFlipXYTiling.Drawing = triangleDrawing;
tileBrushWithFlipXYTiling.TileMode = TileMode.FlipXY;
// Specify the brush's Viewport.
tileBrushWithFlipXYTiling.Viewport = new Rect(0, 0, 0.5, 0.5);
// Create a rectangle and paint it with the DrawingBrush.
Rectangle flipXYTilingExampleRectangle = createExampleRectangle();
flipXYTilingExampleRectangle.Fill = tileBrushWithFlipXYTiling;
mainPanel.Children.Add(createExampleHeader("FlipXY"));
mainPanel.Children.Add(flipXYTilingExampleRectangle);
Content = mainPanel;
}
// Helper method that creates rectangle elements.
private static Rectangle createExampleRectangle()
{
Rectangle exampleRectangle = new Rectangle();
exampleRectangle.Width = 50;
exampleRectangle.Height = 50;
exampleRectangle.Stroke = Brushes.Black;
exampleRectangle.StrokeThickness = 1;
return exampleRectangle;
}
// Helper method that creates headers for the examples.
private static TextBlock createExampleHeader(String text)
{
TextBlock header = new TextBlock();
header.Margin = new Thickness(0, 10, 0, 0);
header.Text = text;
return header;
}
}
}
<!-- Demonstrates TileMode values. -->
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PresentationOption="http://schemas.microsoft.com/winfx/2006/xaml/presentation/option"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions" Background="White" Margin="20">
<Page.Resources>
<!-- Define a Drawing as a resource that it can be easily used
as content for all the DrawingBrush objects in this example. -->
<GeometryDrawing x:Key="TriangleDrawing" Geometry="M0,0 L50,0 0,50Z" Brush="#CCCCFF"
PresentationOptions:Freeze="True" >
<GeometryDrawing.Pen>
<Pen Thickness="2" Brush="Black" MiterLimit="0" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</Page.Resources>
<StackPanel HorizontalAlignment="Left">
<TextBlock Margin="0,10,0,0">None</TextBlock>
<Rectangle Width="50" Height="50" Stroke="Black" StrokeThickness="1">
<Rectangle.Fill>
<!-- The DrawingBrush's content is not tiled in this example. -->
<DrawingBrush TileMode="None" Drawing="{StaticResource TriangleDrawing}" />
</Rectangle.Fill>
</Rectangle>
<TextBlock Margin="0,10,0,0">Tile</TextBlock>
<Rectangle Width="50" Height="50" Stroke="Black" StrokeThickness="1">
<Rectangle.Fill>

MCT: Luis Dueas

Pag 384 de 473

Manual de Windows Presentation Foundation


<!-- The DrawingBrush's content is tiled in this example.
The Viewport property is set to create four tiles. -->
<DrawingBrush TileMode="Tile" Viewport="0,0,0.5,0.5"
Drawing="{StaticResource TriangleDrawing}"/>
</Rectangle.Fill>
</Rectangle>
<TextBlock Margin="0,10,0,0">FlipX</TextBlock>
<Rectangle Width="50" Height="50" Stroke="Black" StrokeThickness="1">
<Rectangle.Fill>
<!-- The DrawingBrush's content is flipped horizontally as it is
tiled in this example. -->
<DrawingBrush TileMode="FlipX" Viewport="0,0,0.5,0.5"
Drawing="{StaticResource TriangleDrawing}" />
</Rectangle.Fill>
</Rectangle>
<TextBlock Margin="0,10,0,0">FlipY</TextBlock>
<Rectangle Width="50" Height="50" Stroke="Black" StrokeThickness="1">
<Rectangle.Fill>
<!-- The DrawingBrush's content is flipped vertically as it is
tiled in this example. -->
<DrawingBrush TileMode="FlipY" Viewport="0,0,0.5,0.5"
Drawing="{StaticResource TriangleDrawing}" />
</Rectangle.Fill>
</Rectangle>
<TextBlock Margin="0,10,0,0">FlipXY</TextBlock>
<Rectangle Width="50" Height="50" Stroke="Black" StrokeThickness="1">
<Rectangle.Fill>
<!-- The DrawingBrush's content is flipped horizontally
and vertically as it is tiled in this example. -->
<DrawingBrush TileMode="FlipXY" Viewport="0,0,0.5,0.5"
Drawing="{StaticResource TriangleDrawing}" />
</Rectangle.Fill>
</Rectangle>
</StackPanel>
</Page>

7.3.2.7.5. Cmo: Definir un Lpiz


En este ejemplo se muestra cmo utilizar un objeto Pen para describir una forma. Para crear un Pen sencillo
nicamente es necesario especificar sus propiedades Thickness y Brush. Puede crear lpices ms complejos
especificando las propiedades DashStyle, DashCap, LineJoin, StartLineCap y EndLineCap.
Ejemplo
En el ejemplo siguiente se utiliza Pen para describir una forma definida por un objeto GeometryDrawing.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace SDKSample
{
public partial class PenExample : Page
{
public PenExample()
{
// Create several geometries.
RectangleGeometry myRectangleGeometry = new RectangleGeometry();
myRectangleGeometry.Rect = new Rect(0, 0, 50, 50);
EllipseGeometry myEllipseGeometry = new EllipseGeometry();
myEllipseGeometry.Center = new Point(75, 75);
myEllipseGeometry.RadiusX = 50;
myEllipseGeometry.RadiusY = 50;

MCT: Luis Dueas

Pag 385 de 473

Manual de Windows Presentation Foundation


LineGeometry myLineGeometry = new LineGeometry();
myLineGeometry.StartPoint = new Point(75, 75);
myLineGeometry.EndPoint = new Point(75, 0);
// Create a GeometryGroup and add the geometries to it.
GeometryGroup myGeometryGroup = new GeometryGroup();
myGeometryGroup.Children.Add(myRectangleGeometry);
myGeometryGroup.Children.Add(myEllipseGeometry);
myGeometryGroup.Children.Add(myLineGeometry);
// Create a GeometryDrawing and use the GeometryGroup to specify its geometry.
GeometryDrawing myGeometryDrawing = new GeometryDrawing();
myGeometryDrawing.Geometry = myGeometryGroup;
// Add the GeometryDrawing to a DrawingGroup.
DrawingGroup myDrawingGroup = new DrawingGroup();
myDrawingGroup.Children.Add(myGeometryDrawing);
// Create a Pen to add to the GeometryDrawing created above.
Pen myPen = new Pen();
myPen.Thickness = 10;
myPen.LineJoin = PenLineJoin.Round;
myPen.EndLineCap = PenLineCap.Round;
// Create a gradient to use as a value for the Pen's Brush property.
GradientStop firstStop = new GradientStop();
firstStop.Offset = 0.0;
Color c1 = new Color();
c1.A = 255;
c1.R = 204;
c1.G = 204;
c1.B = 255;
firstStop.Color = c1;
GradientStop secondStop = new GradientStop();
secondStop.Offset = 1.0;
secondStop.Color = Colors.Purple;
LinearGradientBrush myLinearGradientBrush = new LinearGradientBrush();
myLinearGradientBrush.GradientStops.Add(firstStop);
myLinearGradientBrush.GradientStops.Add(secondStop);
myPen.Brush = myLinearGradientBrush;
myGeometryDrawing.Pen = myPen;
// Create an Image and set its DrawingImage to the Geometry created above.
Image myImage = new Image();
myImage.Stretch = Stretch.None;
myImage.Margin = new Thickness(10);
DrawingImage myDrawingImage = new DrawingImage();
myDrawingImage.Drawing = myDrawingGroup;
myImage.Source = myDrawingImage;
this.Content = myImage;
}
}
}
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Background="White">
<Image Stretch="None" Margin="20">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup>
<GeometryDrawing>
<GeometryDrawing.Geometry>
<GeometryGroup>
<RectangleGeometry Rect="0,0,50,50" />
<EllipseGeometry Center="75,75" RadiusX="50" RadiusY="50" />
<LineGeometry StartPoint="75,75" EndPoint="75,0" />

MCT: Luis Dueas

Pag 386 de 473

Manual de Windows Presentation Foundation


</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="10" LineJoin="Round" EndLineCap="Triangle"
StartLineCap="Round">
<Pen.Brush>
<LinearGradientBrush>
<GradientStop Offset="0.0" Color="#CCCCFF" />
<GradientStop Offset="1.0" Color="Purple" />
</LinearGradientBrush>
</Pen.Brush>
</Pen>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Page>
Objeto GeometryDrawing

7.3.2.7.6. Cmo: Pintar un Area con un Dibujo


En este ejemplo se muestra cmo pintar un rea con un dibujo. Para pintar una rea con un dibujo, se utiliza
un objeto DrawingBrush y uno o ms objetos Drawing. En el ejemplo siguiente se utiliza un objeto
DrawingBrush para pintar un objeto con un dibujo de dos elipses.
Ejemplo
<!-- Demonstrates the use of DrawingBrush. -->
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="White">
<StackPanel Margin="20">
<Rectangle Width="150" Height="150" Stroke="Black" StrokeThickness="1">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<GeometryDrawing Brush="MediumBlue">
<GeometryDrawing.Geometry>
<GeometryGroup>
<EllipseGeometry RadiusX="20" RadiusY="45" Center="50,50" />
<EllipseGeometry RadiusX="45" RadiusY="20" Center="50,50" />
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="10">
<Pen.Brush>
<LinearGradientBrush>
<GradientStop Offset="0.0" Color="Black" />
<GradientStop Offset="1.0" Color="Gray" />
</LinearGradientBrush>
</Pen.Brush>
</Pen>
</GeometryDrawing.Pen>

MCT: Luis Dueas

Pag 387 de 473

Manual de Windows Presentation Foundation


</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
</StackPanel>
</Page>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Microsoft.Samples.DrawingBrushExamples
{
/// <summary>
/// Paints a Rectangle element with a DrawingBrush.
/// </summary>
public class DrawingBrushExample : Page
{
public DrawingBrushExample()
{
Background = Brushes.White;
StackPanel mainPanel = new StackPanel();
// Create a drawing of two ellipses.
GeometryDrawing aDrawing = new GeometryDrawing();
// Use geometries to describe two overlapping ellipses.
EllipseGeometry ellipse1 = new EllipseGeometry();
ellipse1.RadiusX = 20;
ellipse1.RadiusY = 45;
ellipse1.Center = new Point(50, 50);
EllipseGeometry ellipse2 = new EllipseGeometry();
ellipse2.RadiusX = 45;
ellipse2.RadiusY = 20;
ellipse2.Center = new Point(50, 50);
GeometryGroup ellipses = new GeometryGroup();
ellipses.Children.Add(ellipse1);
ellipses.Children.Add(ellipse2);
// Add the geometry to the drawing.
aDrawing.Geometry = ellipses;
// Specify the drawing's fill.
aDrawing.Brush = Brushes.Blue;
// Specify the drawing's stroke.
Pen stroke = new Pen();
stroke.Thickness = 10.0;
stroke.Brush = new LinearGradientBrush(
Colors.Black, Colors.Gray, new Point(0, 0), new Point(1, 1));
aDrawing.Pen = stroke;
// Create a DrawingBrush
DrawingBrush myDrawingBrush = new DrawingBrush();
myDrawingBrush.Drawing = aDrawing;
// Create a Rectangle element.
Rectangle aRectangle = new Rectangle();
aRectangle.Width = 150;
aRectangle.Height = 150;
aRectangle.Stroke = Brushes.Black;
aRectangle.StrokeThickness = 1.0;
// Use the DrawingBrush to paint the rectangle's background.
aRectangle.Fill = myDrawingBrush;
mainPanel.Children.Add(aRectangle);

MCT: Luis Dueas

Pag 388 de 473

Manual de Windows Presentation Foundation


this.Content = mainPanel;
}
}
}
En la ilustracin siguiente se muestran los resultados del ejemplo.

Estableciendo las propiedades Viewport y TileMode de un objeto DrawingBrush, puede crear una trama
repetitiva. En el ejemplo siguiente se pinta un objeto con una trama creada a partir de un dibujo de dos elipses.
<!-- Demonstrates the use of DrawingBrush. -->
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Background="White">
<StackPanel Margin="20">
<Rectangle Width="150" Height="150" Stroke="Black" StrokeThickness="1">
<Rectangle.Fill>
<DrawingBrush Viewport="0,0,0.25,0.25" TileMode="Tile">
<DrawingBrush.Drawing>
<GeometryDrawing Brush="MediumBlue">
<GeometryDrawing.Geometry>
<GeometryGroup>
<EllipseGeometry RadiusX="20" RadiusY="45" Center="50,50" />
<EllipseGeometry RadiusX="45" RadiusY="20" Center="50,50" />
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="10">
<Pen.Brush>
<LinearGradientBrush>
<GradientStop Offset="0.0" Color="Black" />
<GradientStop Offset="1.0" Color="Gray" />
</LinearGradientBrush>
</Pen.Brush>
</Pen>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
</StackPanel>
</Page>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Microsoft.Samples.DrawingBrushExamples
{
/// <summary>
/// Paints a Rectangle element with a tiled DrawingBrush.
/// </summary>
public class TiledDrawingBrushExample : Page
{
public TiledDrawingBrushExample()

MCT: Luis Dueas

Pag 389 de 473

Manual de Windows Presentation Foundation


{
Background = Brushes.White;
StackPanel mainPanel = new StackPanel();
// Create a drawing of two ellipses.
GeometryDrawing aDrawing = new GeometryDrawing();
// Use geometries to describe two overlapping ellipses.
EllipseGeometry ellipse1 = new EllipseGeometry();
ellipse1.RadiusX = 20;
ellipse1.RadiusY = 45;
ellipse1.Center = new Point(50, 50);
EllipseGeometry ellipse2 = new EllipseGeometry();
ellipse2.RadiusX = 45;
ellipse2.RadiusY = 20;
ellipse2.Center = new Point(50, 50);
GeometryGroup ellipses = new GeometryGroup();
ellipses.Children.Add(ellipse1);
ellipses.Children.Add(ellipse2);
// Add the geometry to the drawing.
aDrawing.Geometry = ellipses;
// Specify the drawing's fill.
aDrawing.Brush = Brushes.Blue;
// Specify the drawing's stroke.
Pen stroke = new Pen();
stroke.Thickness = 10.0;
stroke.Brush = new LinearGradientBrush(
Colors.Black, Colors.Gray, new Point(0, 0), new Point(1, 1));
aDrawing.Pen = stroke;
// Create a DrawingBrush
DrawingBrush myDrawingBrush = new DrawingBrush();
myDrawingBrush.Drawing = aDrawing;
// Set the DrawingBrush's Viewport and TileMode
// properties so that it generates a pattern.
myDrawingBrush.Viewport = new Rect(0, 0, 0.25, 0.25);
myDrawingBrush.TileMode = TileMode.Tile;
// Create a Rectangle element.
Rectangle aRectangle = new Rectangle();
aRectangle.Width = 150;
aRectangle.Height = 150;
aRectangle.Stroke = Brushes.Black;
aRectangle.StrokeThickness = 1.0;
// Use the DrawingBrush to paint the rectangle's background.
aRectangle.Fill = myDrawingBrush;
mainPanel.Children.Add(aRectangle);
this.Content = mainPanel;
}
}
}
En la ilustracin siguiente se muestran los resultados de DrawingBrush en mosaico.

7.3.2.7.7. Cmo: Pintar un Area con una Imagen


En este ejemplo se muestra cmo utilizar la clase ImageBrush para pintar una rea con una imagen. Un
ImageBrush muestra una imagen nica, que se especifica mediante su propiedad ImageSource.
Ejemplo
En el ejemplo siguiente se pinta el fondo (Background) de un botn mediante ImageBrush.

MCT: Luis Dueas

Pag 390 de 473

Manual de Windows Presentation Foundation


using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Media;
namespace Microsoft.Samples.Graphics.UsingImageBrush
{
public class PaintingWithImagesExample : Page
{
public PaintingWithImagesExample()
{
Background = Brushes.White;
StackPanel mainPanel = new StackPanel();
mainPanel.Margin = new Thickness(20.0);
// Create a button.
Button berriesButton = new Button();
berriesButton.Foreground = Brushes.White;
berriesButton.FontWeight = FontWeights.Bold;
FontSizeConverter sizeConverter = new FontSizeConverter();
berriesButton.FontSize = (Double)sizeConverter.ConvertFromString("16pt");
berriesButton.FontFamily = new FontFamily("Verdana");
berriesButton.Content = "Berries";
berriesButton.Padding = new Thickness(20.0);
berriesButton.HorizontalAlignment = HorizontalAlignment.Left;
// Create an ImageBrush.
ImageBrush berriesBrush = new ImageBrush();
berriesBrush.ImageSource = new BitmapImage(new Uri(@"sampleImages\berries.jpg",
UriKind.Relative));
// Use the brush to paint the button's background.
berriesButton.Background = berriesBrush;
mainPanel.Children.Add(berriesButton);
this.Content = mainPanel;
}
}
}
<!-- This example shows how to use an ImageBrush to paint shapes and controls. -->
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Background="White">
<StackPanel Margin="20">
<!-- Sets the button's Background property with an
ImageBrush. The resulting button has an image as its background. -->
<Button Foreground="White" FontWeight="Bold" FontSize="16pt" FontFamily="Verdana"
Content="Berries" Padding="20" HorizontalAlignment="Left">
<Button.Background>
<ImageBrush ImageSource="sampleImages\berries.jpg" />
</Button.Background>
</Button>
</StackPanel>
</Page>
De manera predeterminada, ImageBrush expande su imagen hasta rellenar completamente el rea que se est
pintando. En el ejemplo anterior, la imagen se expande hasta rellenar el botn, lo que puede distorsionar la
imagen. Puede controlar este comportamiento estableciendo la propiedad Stretch de TileBrush en Uniform o
UniformToFill, para que el pincel conserve la relacin de aspecto de la imagen.
Si establece las propiedades Viewport y TileMode de ImageBrush, puede crear un patrn que se repite. En el
ejemplo siguiente se pinta un botn mediante un patrn creado a partir de una imagen.
using System;
using System.Windows;
using System.Windows.Controls;

MCT: Luis Dueas

Pag 391 de 473

Manual de Windows Presentation Foundation


using System.Windows.Media.Imaging;
using System.Windows.Media;
namespace Microsoft.Samples.Graphics.UsingImageBrush
{
public class TiledImageBrushExample : Page
{
public TiledImageBrushExample()
{
Background = Brushes.White;
StackPanel mainPanel = new StackPanel();
mainPanel.Margin = new Thickness(20.0);
// Create a button.
Button berriesButton = new Button();
berriesButton.Foreground = Brushes.White;
berriesButton.FontWeight = FontWeights.Bold;
FontSizeConverter sizeConverter = new FontSizeConverter();
berriesButton.FontSize = (Double)sizeConverter.ConvertFromString("16pt");
berriesButton.FontFamily = new FontFamily("Verdana");
berriesButton.Content = "Berries";
berriesButton.Padding = new Thickness(20.0);
berriesButton.HorizontalAlignment = HorizontalAlignment.Left;
// Create an ImageBrush.
ImageBrush berriesBrush = new ImageBrush();
berriesBrush.ImageSource = new BitmapImage(new Uri(@"sampleImages\berries.jpg",
UriKind.Relative));
// Set the ImageBrush's Viewport and TileMode
// so that it produces a pattern from the image.
berriesBrush.Viewport = new Rect(0,0,0.5,0.5);
berriesBrush.TileMode = TileMode.FlipXY;
// Use the brush to paint the button's background.
berriesButton.Background = berriesBrush;
mainPanel.Children.Add(berriesButton);
this.Content = mainPanel;
}
}
}
<!-- This example shows how to use an ImageBrush to paint shapes and controls. -->
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Background="White">
<StackPanel Margin="20">
<!-- Sets the button's Background property with an
ImageBrush. The resulting button has an image as its background. -->
<Button Foreground="White" FontWeight="Bold" FontSize="16pt" FontFamily="Verdana"
Content="Berries" Padding="20" HorizontalAlignment="Left">
<Button.Background>
<!-- The ImageBrush's Viewport and TileMode
are set to produce a pattern from the image. -->
<ImageBrush Viewport="0,0,0.5,0.5" TileMode="FlipXY"
ImageSource="sampleImages\berries.jpg" />
</Button.Background>
</Button>
</StackPanel>
</Page>

7.3.2.7.8. Cmo: Pintar un Area con un Degradado Lineal


En este ejemplo se muestra cmo utilizar la clase LinearGradientBrush para pintar una rea con un degradado
lineal. En el ejemplo siguiente, la propiedad Fill de Rectangle se pinta con un degradado lineal diagonal que
efecta una transicin del amarillo al rojo, al azul y al verde esmeralda.

MCT: Luis Dueas

Pag 392 de 473

Manual de Windows Presentation Foundation


Ejemplo
<!-- This rectangle is painted with a diagonal linear gradient. -->
<Rectangle Width="200" Height="100">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="Yellow" Offset="0.0" />
<GradientStop Color="Red" Offset="0.25" />
<GradientStop Color="Blue" Offset="0.75" />
<GradientStop Color="LimeGreen" Offset="1.0" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
Rectangle diagonalFillRectangle = new Rectangle();
diagonalFillRectangle.Width = 200;
diagonalFillRectangle.Height = 100;
// Create a diagonal linear gradient with four stops.
LinearGradientBrush myLinearGradientBrush = new LinearGradientBrush();
myLinearGradientBrush.StartPoint = new Point(0,0);
myLinearGradientBrush.EndPoint = new Point(1,1);
myLinearGradientBrush.GradientStops.Add(new GradientStop(Colors.Yellow, 0.0));
myLinearGradientBrush.GradientStops.Add(new GradientStop(Colors.Red, 0.25));
myLinearGradientBrush.GradientStops.Add(new GradientStop(Colors.Blue, 0.75));
myLinearGradientBrush.GradientStops.Add(new GradientStop(Colors.LimeGreen, 1.0));
// Use the brush to paint the rectangle.
diagonalFillRectangle.Fill = myLinearGradientBrush;
En la ilustracin siguiente se muestra el degradado creado en el ejemplo anterior.

Para crear un degradado lineal horizontal, cambie las propiedades StartPoint y EndPoint de LinearGradientBrush
a (0,0.5) y (1,0.5). En el ejemplo siguiente, se pinta un objeto Rectangle con un degradado lineal horizontal.
<!-- This rectangle is painted with a horizontal linear gradient. -->
<Rectangle Width="200" Height="100">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
<GradientStop Color="Yellow" Offset="0.0" />
<GradientStop Color="Red" Offset="0.25" />
<GradientStop Color="Blue" Offset="0.75" />
<GradientStop Color="LimeGreen" Offset="1.0" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
Rectangle horizontalFillRectangle = new Rectangle();
horizontalFillRectangle.Width = 200;
horizontalFillRectangle.Height = 100;
// Create a horizontal linear gradient with four stops.
LinearGradientBrush myHorizontalGradient = new LinearGradientBrush();
myHorizontalGradient.StartPoint = new Point(0,0.5);
myHorizontalGradient.EndPoint = new Point(1,0.5);
myHorizontalGradient.GradientStops.Add(new GradientStop(Colors.Yellow, 0.0));
myHorizontalGradient.GradientStops.Add(new GradientStop(Colors.Red, 0.25));
myHorizontalGradient.GradientStops.Add(new GradientStop(Colors.Blue, 0.75));
myHorizontalGradient.GradientStops.Add(new GradientStop(Colors.LimeGreen, 1.0));
// Use the brush to paint the rectangle.
horizontalFillRectangle.Fill = myHorizontalGradient;
En la ilustracin siguiente se muestra el degradado creado en el ejemplo anterior.

MCT: Luis Dueas

Pag 393 de 473

Manual de Windows Presentation Foundation

Para crear un degradado lineal vertical, cambie las propiedades StartPoint y EndPoint de LinearGradientBrush a
(0.5,0) y (0.5,1). En el ejemplo siguiente, se pinta un objeto Rectangle con un degradado lineal vertical.
<!-- This rectangle is painted with a vertical gradient. -->
<Rectangle Width="200" Height="100">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="Yellow" Offset="0.0" />
<GradientStop Color="Red" Offset="0.25" />
<GradientStop Color="Blue" Offset="0.75" />
<GradientStop Color="LimeGreen" Offset="1.0" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
Rectangle verticalFillRectangle = new Rectangle();
verticalFillRectangle.Width = 200;
verticalFillRectangle.Height = 100;
// Create a vertical linear gradient with four stops.
LinearGradientBrush myVerticalGradient = new LinearGradientBrush();
myVerticalGradient.StartPoint = new Point(0.5,0);
myVerticalGradient.EndPoint = new Point(0.5,1);
myVerticalGradient.GradientStops.Add(new GradientStop(Colors.Yellow, 0.0));
myVerticalGradient.GradientStops.Add(new GradientStop(Colors.Red, 0.25));
myVerticalGradient.GradientStops.Add(new GradientStop(Colors.Blue, 0.75));
myVerticalGradient.GradientStops.Add(new GradientStop(Colors.LimeGreen, 1.0));
// Use the brush to paint the rectangle.
verticalFillRectangle.Fill = myVerticalGradient;
En la ilustracin siguiente se muestra el degradado creado en el ejemplo anterior.

Nota:
Los ejemplos de este tema utilizan el sistema de coordenadas predeterminado para establecer los puntos
inicial y final. El sistema de coordenadas predeterminado es relativo a un rectngulo de seleccin: 0 indica
un 0 por ciento del rectngulo de seleccin y 1, un 100 por cien del mismo. Puede cambiar este sistema de
coordenadas estableciendo la propiedad MappingMode en el valor BrushMappingMode.Absolute. Un sistema
de coordenadas absoluto no es relativo respecto a ningn rectngulo de seleccin. Los valores se
interpretan directamente en el espacio local.

7.3.2.7.9. Cmo: Pintar un Area con un Degradado Radial


En este ejemplo se muestra cmo utilizar la clase RadialGradientBrush para pintar una rea con un degradado
radial.
Ejemplo
En el ejemplo siguiente se utiliza RadialGradientBrush para pintar un rectngulo con un degradado radial que
efecta una transicin del amarillo al rojo, al azul y al verde esmeralda.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

MCT: Luis Dueas

Pag 394 de 473

Manual de Windows Presentation Foundation


namespace BrushesIntroduction
{
public class RadialGradientBrushSnippet : Page
{
public RadialGradientBrushSnippet()
{
Title = "RadialGradientBrush Example";
Background = Brushes.White;
Margin = new Thickness(20);
// Create a RadialGradientBrush with four gradient stops.
RadialGradientBrush radialGradient = new RadialGradientBrush();
// Set the GradientOrigin to the center of the area being painted.
radialGradient.GradientOrigin = new Point(0.5, 0.5);
// Set the gradient center to the center of the area being painted.
radialGradient.Center = new Point(0.5, 0.5);
// Set the radius of the gradient circle so that it extends to
// the edges of the area being painted.
radialGradient.RadiusX = 0.5;
radialGradient.RadiusY = 0.5;
// Create four gradient stops.
radialGradient.GradientStops.Add(new GradientStop(Colors.Yellow, 0.0));
radialGradient.GradientStops.Add(new GradientStop(Colors.Red, 0.25));
radialGradient.GradientStops.Add(new GradientStop(Colors.Blue, 0.75));
radialGradient.GradientStops.Add(new GradientStop(Colors.LimeGreen, 1.0));
// Freeze the brush (make it unmodifiable) for performance benefits.
radialGradient.Freeze();
// Create a rectangle and paint it with the RadialGradientBrush.
Rectangle aRectangle = new Rectangle();
aRectangle.Width = 200;
aRectangle.Height = 100;
aRectangle.Fill = radialGradient;
StackPanel mainPanel = new StackPanel();
mainPanel.Children.Add(aRectangle);
Content = mainPanel;
}
}
}
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="RadialGradientBrush Example" Background="White" Margin="20">
<StackPanel>
<!-- This rectangle is painted with a radial gradient. -->
<Rectangle Width="200" Height="100">
<Rectangle.Fill>
<RadialGradientBrush GradientOrigin="0.5,0.5" Center="0.5,0.5"
RadiusX="0.5" RadiusY="0.5">
<RadialGradientBrush.GradientStops>
<GradientStop Color="Yellow" Offset="0" />
<GradientStop Color="Red" Offset="0.25" />
<GradientStop Color="Blue" Offset="0.75" />
<GradientStop Color="LimeGreen" Offset="1" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>
</StackPanel>
</Page>
En la ilustracin siguiente, se ve el degradado del ejemplo anterior. Se han resaltado los puntos de degradado.

MCT: Luis Dueas

Pag 395 de 473

Manual de Windows Presentation Foundation

Nota:
En los ejemplos de este tema se utiliza el sistema de coordenadas predeterminado para establecer los
puntos de control. El sistema de coordenadas predeterminado es relativo a un rectngulo de seleccin: 0
indica un 0 por ciento del rectngulo de seleccin y 1, un 100 por cien del mismo. Puede cambiar este
sistema de coordenadas estableciendo la propiedad MappingMode en el valor Absolute. Un sistema de
coordenadas absoluto no es relativo respecto a ningn rectngulo de seleccin. Los valores se interpretan
directamente en el espacio local.

7.3.2.7.10. Cmo: Pintar un Area con un Color Slido


Para pintar una rea con un color slido, puede utilizar un pincel predefinido del sistema, como Red o Blue o
puede crear un nuevo SolidColorBrush y describir su propiedad Color mediante sus valores de alfa, rojo, verde
y azul. En XAML, puede pintar tambin una rea con un color slido utilizando la notacin hexadecimal.
En los ejemplos siguientes se utilizan cada una de estas tcnicas para pintar un objeto Rectangle de azul.
Ejemplo
Utilizar un pincel predefinido
En el ejemplo siguiente se utiliza la propiedad Blue del pincel predefinido para pintar un rectngulo de azul.
<Rectangle Width="50" Height="50" Fill="Blue" />
// Create a rectangle and paint it with a predefined brush.
Rectangle myPredefinedBrushRectangle = new Rectangle();
myPredefinedBrushRectangle.Width = 50;
myPredefinedBrushRectangle.Height = 50;
myPredefinedBrushRectangle.Fill = Brushes.Blue;
Utilizar la notacin hexadecimal
En el ejemplo siguiente se utiliza la notacin hexadecimal de 8 dgitos para pintar un rectngulo de azul.
<!-- Note that the first two characters "FF" of the 8-digit
value is the alpha which controls the transparency of
the color. Therefore, to make a completely transparent
color (invisible), use "00" for those digits (e.g. #000000FF). -->
<Rectangle Width="50" Height="50" Fill="#FF0000FF" />
Utilizar los valores ARGB
En el ejemplo siguiente se crea un objeto SolidColorBrush y se describe su propiedad Color utilizando los
valores ARGB para el color azul.
<Rectangle Width="50" Height="50">
<Rectangle.Fill>
<SolidColorBrush>
<SolidColorBrush.Color>
<!-- Describes the brush's color using
RGB values. Each value has a range of 0-255.
R is for red, G is for green, and B is for blue.
A is for alpha which controls transparency of the
color. Therefore, to make a completely transparent
color (invisible), use a value of 0 for Alpha. -->
<Color A="255" R="0" G="0" B="255" />

MCT: Luis Dueas

Pag 396 de 473

Manual de Windows Presentation Foundation


</SolidColorBrush.Color>
</SolidColorBrush>
</Rectangle.Fill>
</Rectangle>
Rectangle myRgbRectangle = new Rectangle();
myRgbRectangle.Width = 50;
myRgbRectangle.Height = 50;
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
// Describes the brush's color using RGB values. Each value has a range of 0-255.
mySolidColorBrush.Color = Color.FromArgb(255, 0, 0, 255);
myRgbRectangle.Fill = mySolidColorBrush;

7.3.2.7.11. Cmo: Pintar un Area con un Pincel del Sistema


La clase SystemColors proporciona acceso a los pinceles y colores del sistema, tales como ControlBrush,
ControlBrushKey y DesktopBrush. Un pincel del sistema es un objeto SolidColorBrush que pinta una rea con el
color del sistema especificado. Un pincel del sistema siempre genera un relleno slido; no se puede utilizar para
crear un degradado.
Puede utilizar los pinceles del sistema como recursos estticos o dinmicos. Utilice un recurso dinmico si desea
que el pincel se actualice automticamente en caso de que el usuario cambie el pincel del sistema mientras se
ejecuta la aplicacin; de lo contrario, utilice un recurso esttico. La clase SystemColors contiene varias
propiedades estticas que cumplen una convencin de nomenclatura estricta:

<SystemColor>Brush
Obtiene una referencia esttica a un objeto SolidColorBrush del color del sistema especificado.

<SystemColor>BrushKey
Obtiene una referencia dinmica a un objeto SolidColorBrush del color del sistema especificado.

<SystemColor>Color
Obtiene una referencia esttica a una estructura Color del color del sistema especificado.

<SystemColor>ColorKey
Obtiene una referencia dinmica a la estructura Color del color del sistema especificado.

Un color del sistema es una estructura Color que se puede utilizar para configurar un pincel. Por ejemplo, puede
crear un degradado con los colores del sistema estableciendo las propiedades Color de los puntos de degradado
de un objeto LinearGradientBrush degradado en colores del sistema.
Ejemplo
En el ejemplo siguiente se utiliza una referencia de pincel del sistema dinmica para establecer el fondo de un
botn.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowTitle="SystemColors Example" Background="White">
<StackPanel Margin="20">
<!-- Uses a dynamic resource to set the background of a button.
If the desktop brush changes while this application
is running, this button will be updated. -->
<Button Background="{DynamicResource {x:Static SystemColors.DesktopBrushKey}}"
Content="Hello, World!" />
</StackPanel>
</Page>
En el ejemplo siguiente se utiliza una referencia de pincel del sistema esttica para establecer el fondo de un
botn.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

MCT: Luis Dueas

Pag 397 de 473

Manual de Windows Presentation Foundation


WindowTitle="SystemColors Example" Background="White">
<StackPanel Margin="20">
<!-- Uses a static brush to set the background of a button.
If the desktop brush changes while this application
is running, this button will not be updated until the page is loaded again. -->
<Button Background="{x:Static SystemColors.DesktopBrush}" Content="Hello, World!" />
</StackPanel>
</Page>

7.3.2.7.12. Cmo: Pintar un Area con un Vdeo


En este ejemplo se muestra cmo pintar un rea con multimedia. Una manera de pintar un rea con multimedia
es utilizar un control MediaElement junto con un objeto VisualBrush. Utilice el control MediaElement para cargar
y reproducir multimedia y, a continuacin, utilcelo para establecer la propiedad Visual de VisualBrush. Luego,
puede utilizar VisualBrush para pintar un rea con el contenido multimedia cargado.
Ejemplo
En el ejemplo siguiente se utilizan MediaElement y VisualBrush para pintar el Foreground de un control
TextBlock con vdeo. En este ejemplo se establece la propiedad IsMuted de MediaElement en true para que no
genere ningn sonido.
MediaElement myMediaElement = new MediaElement();
myMediaElement.Source = new Uri("sampleMedia\\xbox.wmv", UriKind.Relative);
myMediaElement.IsMuted = true;
VisualBrush myVisualBrush = new VisualBrush();
myVisualBrush.Visual = myMediaElement;
TextBlock myTextBlock = new TextBlock();
myTextBlock.FontSize = 150;
myTextBlock.Text = "Some Text";
myTextBlock.FontWeight = FontWeights.Bold;
myTextBlock.Foreground = myVisualBrush;
<TextBlock FontSize="100pt" Text="Some Text" FontWeight="Bold">
<TextBlock.Foreground>
<VisualBrush>
<VisualBrush.Visual>
<MediaElement Source="sampleMedia\xbox.wmv" IsMuted="True" />
</VisualBrush.Visual>
</VisualBrush>
</TextBlock.Foreground>
</TextBlock>
Dado que VisualBrush hereda de la clase TileBrush, proporciona varios modos de mosaico. Si establece la
propiedad TileMode de VisualBrush en Tile y establece su propiedad Viewport en un valor menor que el rea
que se est pintando, puede crear un modelo en mosaico.
El ejemplo siguiente es idntico al ejemplo anterior, salvo que VisualBrush genera un modelo a partir del vdeo.
MediaElement myMediaElement = new MediaElement();
myMediaElement.Source = new Uri("sampleMedia\\xbox.wmv", UriKind.Relative);
myMediaElement.IsMuted = true;
VisualBrush myVisualBrush = new VisualBrush();
myVisualBrush.Viewport = new Rect(0, 0, 0.5, 0.5);
myVisualBrush.TileMode = TileMode.Tile;
myVisualBrush.Visual = myMediaElement;
TextBlock myTextBlock = new TextBlock();
myTextBlock.FontSize = 150;
myTextBlock.Text = "Some Text";
myTextBlock.FontWeight = FontWeights.Bold;
myTextBlock.Foreground = myVisualBrush;
<TextBlock FontSize="100pt" Text="Some Text" FontWeight="Bold">
<TextBlock.Foreground>

MCT: Luis Dueas

Pag 398 de 473

Manual de Windows Presentation Foundation


<VisualBrush Viewport="0,0,0.5,0.5" TileMode="Tile">
<VisualBrush.Visual>
<MediaElement Source="sampleMedia\xbox.wmv" IsMuted="True" />
</VisualBrush.Visual>
</VisualBrush>
</TextBlock.Foreground>
</TextBlock>

7.3.2.7.13. Cmo: Pintar un Area con un Objeto Visual


En este ejemplo se muestra cmo utilizar la clase VisualBrush para pintar una rea con un objeto Visual.
En el ejemplo siguiente, se utilizan varios controles y un panel como fondo de un rectngulo.
Ejemplo
<Rectangle Width="150" Height="150" Stroke="Black" Margin="5,0,5,0">
<Rectangle.Fill>
<VisualBrush>
<VisualBrush.Visual>
<StackPanel Background="White">
<Rectangle Width="25" Height="25" Fill="Red" Margin="2" />
<TextBlock FontSize="10pt" Margin="2">Hello, World!</TextBlock>
<Button Margin="2">A Button</Button>
</StackPanel>
</VisualBrush.Visual>
</VisualBrush>
</Rectangle.Fill>
</Rectangle>
VisualBrush myVisualBrush = new VisualBrush();
// Create the visual brush's contents.
StackPanel myStackPanel = new StackPanel();
myStackPanel.Background = Brushes.White;
Rectangle redRectangle = new Rectangle();
redRectangle.Width = 25;
redRectangle.Height =25;
redRectangle.Fill = Brushes.Red;
redRectangle.Margin = new Thickness(2);
myStackPanel.Children.Add(redRectangle);
TextBlock someText = new TextBlock();
FontSizeConverter myFontSizeConverter = new FontSizeConverter();
someText.FontSize = (double)myFontSizeConverter.ConvertFrom("10pt");
someText.Text = "Hello, World!";
someText.Margin = new Thickness(2);
myStackPanel.Children.Add(someText);
Button aButton = new Button();
aButton.Content = "A Button";
aButton.Margin = new Thickness(2);
myStackPanel.Children.Add(aButton);
// Use myStackPanel as myVisualBrush's content.
myVisualBrush.Visual = myStackPanel;
// Create a rectangle to paint.
Rectangle myRectangle = new Rectangle();
myRectangle.Width = 150;
myRectangle.Height = 150;
myRectangle.Stroke = Brushes.Black;
myRectangle.Margin = new Thickness(5,0,5,0);
// Use myVisualBrush to paint myRectangle.
myRectangle.Fill = myVisualBrush;

MCT: Luis Dueas

Pag 399 de 473

Manual de Windows Presentation Foundation

7.3.2.7.14. Cmo: Conservar la Relacin de Aspecto de una Imagen utilizada


como Fondo
En este ejemplo se muestra cmo utilizar la propiedad Stretch de ImageBrush para conservar la relacin de
aspecto de la imagen.
De manera predeterminada, cuando se utiliza un ImageBrush para pintar una rea, su contenido se expande
hasta rellenar completamente el rea de salida. Cuando el rea de salida y la imagen tienen relaciones de
aspecto diferentes, la imagen queda distorsionada al expandirse.
Para que ImageBrush conserve la relacin de aspecto de su imagen, establezca la propiedad Stretch en Uniform
o UniformToFill.
Ejemplo
En el ejemplo siguiente se utilizan dos objetos ImageBrush para pintar dos rectngulos. Cada rectngulo es de
300 por 150 pxeles y cada uno de ellos contiene una imagen de 300 por 300 pxeles. La propiedad Stretch del
primer pincel se establece en Uniform, y la propiedad Stretch del segundo pincel se establece en UniformToFill.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Windows.Shapes;
namespace Microsoft.Samples.Graphics.UsingImageBrush
{
/// <summary>
/// Demonstrates different ImageBrush Stretch settings.
/// </summary>
public class StretchModes : Page
{
public StretchModes()
{
// Create an ImageBrush with its Stretch property set to Uniform. The image it
// contains will be expanded as much as posible to fill the output area while
// still preserving its aspect ratio.
ImageBrush uniformBrush = new ImageBrush();
uniformBrush.ImageSource =
new BitmapImage(new Uri("sampleImages\\square.jpg", UriKind.Relative));
uniformBrush.Stretch = Stretch.Uniform;
// Freeze the brush (make it unmodifiable) for performance benefits.
uniformBrush.Freeze();
// Create a rectangle and paint it with the ImageBrush.
Rectangle rectangle1 = new Rectangle();
rectangle1.Width = 300;
rectangle1.Height = 150;
rectangle1.Stroke = Brushes.MediumBlue;
rectangle1.StrokeThickness = 1.0;
rectangle1.Fill = uniformBrush;
// Create an ImageBrush with its Stretch property set to UniformToFill.
// The image it contains will be expanded to completely fill
// the rectangle, but its aspect ratio is preserved.
ImageBrush uniformToFillBrush = new ImageBrush();
uniformToFillBrush.ImageSource =
new BitmapImage(new Uri("sampleImages\\square.jpg", UriKind.Relative));
uniformToFillBrush.Stretch = Stretch.UniformToFill;
// Freeze the brush (make it unmodifiable) for performance benefits.
uniformToFillBrush.Freeze();
// Create a rectangle and paint it with the ImageBrush.
Rectangle rectangle2 = new Rectangle();
rectangle2.Width = 300;

MCT: Luis Dueas

Pag 400 de 473

Manual de Windows Presentation Foundation


rectangle2.Height = 150;
rectangle2.Stroke = Brushes.MediumBlue;
rectangle2.StrokeThickness = 1.0;
rectangle2.Margin = new Thickness(0, 10, 0, 0);
rectangle2.Fill = uniformToFillBrush;
StackPanel mainPanel = new StackPanel();
mainPanel.Children.Add(rectangle1);
mainPanel.Children.Add(rectangle2);
Content = mainPanel;
Background = Brushes.White;
Margin = new Thickness(20);
Title = "ImageBrush Stretch Modes";
}
}
}
<!-- Demonstrates different ImageBrush Stretch settings.-->
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PresentationOption="http://schemas.microsoft.com/winfx/2006/xaml/presentation/option"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions" Background="White" Margin="20"
Title="ImageBrush Stretch Modes">
<StackPanel>
<!-- The ImageBrush in this example has its Stretch property set to Uniform. As a
result, the image it contains is expanded as much as possible
to fill the rectangle while still preserving its aspect ratio.-->
<Rectangle Width="300" Height="150" Stroke="MediumBlue" StrokeThickness="1">
<Rectangle.Fill>
<ImageBrush Stretch="Uniform" ImageSource="sampleImages\square.jpg"
PresentationOptions:Freeze="True" />
</Rectangle.Fill>
</Rectangle>
<!-- The ImageBrush in this example has its Stretch property set to UniformToFill.
As a result, the image is expanded to completely fill
the rectangle, but its aspect ratio is preserved.-->
<Rectangle Width="300" Height="150" Stroke="MediumBlue" StrokeThickness="1"
Margin="0,10,0,0">
<Rectangle.Fill>
<ImageBrush Stretch="UniformToFill" ImageSource="sampleImages\square.jpg"
PresentationOptions:Freeze="True" />
</Rectangle.Fill>
</Rectangle>
</StackPanel>
</Page>
En la ilustracin siguiente se muestra la salida del primer pincel, cuya propiedad Stretch est establecida en el
valor Uniform.

En la ilustracin siguiente se muestra la salida del segundo pincel, cuya propiedad Stretch est establecida en el
valor UniformToFill.

MCT: Luis Dueas

Pag 401 de 473

Manual de Windows Presentation Foundation

Observe que la propiedad Stretch se comporta exactamente igual para los dems objetos TileBrush, es decir,
para DrawingBrush y VisualBrush.
Asimismo, tenga en cuenta que, aunque parezca que la propiedad Stretch especifica cmo se expandir el
contenido de TileBrush hasta ajustarse a su rea de salida, en realidad especifica cmo se expande el contenido
de TileBrush hasta rellenar su mosaico base.

7.3.2.7.15. Cmo: Establecer la Alineacin Horizontal y Vertical de TileBrush


En este ejemplo se muestra cmo controlar la alineacin horizontal y vertical del contenido de un mosaico. Para
controlar la alineacin horizontal y vertical de TileBrush, use sus propiedades AlignmentX y AlignmentY.
Las propiedades AlignmentX y AlignmentY de TileBrush se usan cuando se cumple cualquiera de estas
condiciones:

El valor de la propiedad Stretch es Uniform o UniformToFill, y Viewbox y Viewport tienen relaciones de


aspecto diferentes.

El valor de la propiedad Stretch es None y Viewbox y Viewport tienen tamaos diferentes.

Ejemplo
En el ejemplo siguiente se alinea el contenido de DrawingBrush, que es un tipo de TileBrush, con la esquina
superior izquierda de su mosaico. Para alinear el contenido, el ejemplo establece la propiedad AlignmentX de
DrawingBrush en Left y la propiedad AlignmentY en Top. Este ejemplo produce el siguiente resultado.
TileBrush con el contenido alineado con la esquina superior izquierda

// Create a TileBrush and align its content to the top-left of its tile.
DrawingBrush topLeftAlignedTileBrush = new DrawingBrush();
topLeftAlignedTileBrush.AlignmentX = AlignmentX.Left;
topLeftAlignedTileBrush.AlignmentY = AlignmentY.Top;
// Set Stretch to None so that the brush's content doesn't automatically expand to
// fill the entire tile.
topLeftAlignedTileBrush.Stretch = Stretch.None;
// Define the brush's content.
GeometryGroup ellipses = new GeometryGroup();
ellipses.Children.Add(new EllipseGeometry(new Point(50, 50), 20, 45));
ellipses.Children.Add(new EllipseGeometry(new Point(50, 50), 45, 20));
Pen drawingPen = new Pen(Brushes.Gray, 10);
GeometryDrawing ellipseDrawing = new GeometryDrawing(Brushes.Blue, drawingPen, ellipses);
topLeftAlignedTileBrush.Drawing = ellipseDrawing;
// Use the brush to paint a rectangle.
Rectangle rectangle1 = new Rectangle();
rectangle1.Width = 150;
rectangle1.Height = 150;
rectangle1.Stroke = Brushes.Red;
rectangle1.StrokeThickness = 2;
rectangle1.Margin = new Thickness(20);
rectangle1.Fill = topLeftAlignedTileBrush;
<Rectangle Width="150" Height="150" Stroke="Red" StrokeThickness="2" Margin="20">
<Rectangle.Fill>
<!-- This brush's content is aligned to the top-left of its tile. -->
<DrawingBrush Stretch="None" AlignmentX="Left" AlignmentY="Top">
<DrawingBrush.Drawing>

MCT: Luis Dueas

Pag 402 de 473

Manual de Windows Presentation Foundation


<GeometryDrawing Brush="MediumBlue">
<GeometryDrawing.Geometry>
<GeometryGroup>
<EllipseGeometry RadiusX="20" RadiusY="45" Center="50,50" />
<EllipseGeometry RadiusX="45" RadiusY="20" Center="50,50" />
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="Gray" Thickness="10" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
En el ejemplo siguiente se alinea el contenido de DrawingBrush con la esquina inferior derecha de su mosaico al
establecer la propiedad AlignmentX en Right y la propiedad AlignmentY en Bottom. El ejemplo produce el
siguiente resultado:
TileBrush con el contenido alineado con la esquina inferior derecha

// Create a TileBrush and align its content to the bottom-right of its tile.
DrawingBrush bottomRightAlignedTileBrush = new DrawingBrush();
bottomRightAlignedTileBrush.AlignmentX = AlignmentX.Right;
bottomRightAlignedTileBrush.AlignmentY = AlignmentY.Bottom;
bottomRightAlignedTileBrush.Stretch = Stretch.None;
// Define the brush's content.
bottomRightAlignedTileBrush.Drawing = ellipseDrawing;
// Use the brush to paint a rectangle.
Rectangle rectangle2 = new Rectangle();
rectangle2.Width = 150;
rectangle2.Height = 150;
rectangle2.Stroke = Brushes.Red;
rectangle2.StrokeThickness = 2;
rectangle2.Margin = new Thickness(20);
rectangle2.Fill = bottomRightAlignedTileBrush;
<Rectangle Width="150" Height="150" Stroke="Red" StrokeThickness="2" Margin="20">
<Rectangle.Fill>
<!-- This brush's content is aligned to the bottom right of its tile. -->
<DrawingBrush Stretch="None" AlignmentX="Right" AlignmentY="Bottom">
<DrawingBrush.Drawing>
<GeometryDrawing Brush="MediumBlue">
<GeometryDrawing.Geometry>
<GeometryGroup>
<EllipseGeometry RadiusX="20" RadiusY="45" Center="50,50" />
<EllipseGeometry RadiusX="45" RadiusY="20" Center="50,50" />
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="Gray" Thickness="10" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>

MCT: Luis Dueas

Pag 403 de 473

Manual de Windows Presentation Foundation


En el ejemplo siguiente se alinea el contenido de DrawingBrush con la esquina superior izquierda de su mosaico
al establecer la propiedad AlignmentX en Left y la propiedad AlignmentY en Top. Tambin se establecen las
propiedades Viewport y TileMode de DrawingBrush para generar un patrn de mosaico. El ejemplo produce el
siguiente resultado:
Patrn de mosaico con el contenido alineado con la esquina superior izquierda en el mosaico base

En la ilustracin se resalta el mosaico base para que pueda ver cmo se alinea su contenido. Observe que el
valor AlignmentX no tiene ningn efecto porque el contenido de DrawingBrush rellena horizontalmente todo el
mosaico base.
// Create a TileBrush that generates a tiled pattern and align its
// content to the top-left of its tile.
DrawingBrush tiledTopLeftAlignedTileBrush = new DrawingBrush();
tiledTopLeftAlignedTileBrush.AlignmentX = AlignmentX.Left;
tiledTopLeftAlignedTileBrush.AlignmentY = AlignmentY.Top;
tiledTopLeftAlignedTileBrush.Stretch = Stretch.Uniform;
// Set the brush's Viewport and TileMode to produce a tiled pattern.
tiledTopLeftAlignedTileBrush.Viewport = new Rect(0, 0, 0.25, 0.5);
tiledTopLeftAlignedTileBrush.TileMode = TileMode.Tile;
// Define the brush's content.
tiledTopLeftAlignedTileBrush.Drawing = ellipseDrawing;
// Use the brush to paint a rectangle.
Rectangle rectangle3 = new Rectangle();
rectangle3.Width = 150;
rectangle3.Height = 150;
rectangle3.Stroke = Brushes.Black;
rectangle3.StrokeThickness = 2;
rectangle3.Margin = new Thickness(20);
rectangle3.Fill = tiledTopLeftAlignedTileBrush;
<Rectangle Width="150" Height="150" Stroke="Black" StrokeThickness="2" Margin="20">
<Rectangle.Fill>
<!-- This brush's content is aligned to the top left of its tile. -->
<DrawingBrush Stretch="Uniform" Viewport="0,0,0.25,0.5" TileMode="Tile"
AlignmentX="Left" AlignmentY="Top">
<DrawingBrush.Drawing>
<GeometryDrawing Brush="MediumBlue">
<GeometryDrawing.Geometry>
<GeometryGroup>
<EllipseGeometry RadiusX="20" RadiusY="45" Center="50,50" />
<EllipseGeometry RadiusX="45" RadiusY="20" Center="50,50" />
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="Gray" Thickness="10" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>

MCT: Luis Dueas

Pag 404 de 473

Manual de Windows Presentation Foundation


En el ltimo ejemplo se alinea el contenido de un elemento DrawingBrush en mosaico con la esquina inferior
derecha de su mosaico base al establecer la propiedad AlignmentX en Right y la propiedad AlignmentY en
Bottom. El ejemplo produce el siguiente resultado:
Patrn de mosaico con el contenido alineado con la esquina inferior derecha en el mosaico base

De nuevo, observe que el valor AlignmentX no tiene ningn efecto porque el contenido de DrawingBrush rellena
horizontalmente todo el mosaico base.
// Create a TileBrush and align its content to the bottom-right of its tile.
DrawingBrush bottomRightAlignedTileBrush = new DrawingBrush();
bottomRightAlignedTileBrush.AlignmentX = AlignmentX.Right;
bottomRightAlignedTileBrush.AlignmentY = AlignmentY.Bottom;
bottomRightAlignedTileBrush.Stretch = Stretch.None;
// Define the brush's content.
bottomRightAlignedTileBrush.Drawing = ellipseDrawing;
// Use the brush to paint a rectangle.
Rectangle rectangle2 = new Rectangle();
rectangle2.Width = 150;
rectangle2.Height = 150;
rectangle2.Stroke = Brushes.Red;
rectangle2.StrokeThickness = 2;
rectangle2.Margin = new Thickness(20);
rectangle2.Fill = bottomRightAlignedTileBrush;
<Rectangle Width="150" Height="150" Stroke="Red" StrokeThickness="2" Margin="20">
<Rectangle.Fill>
<!-- This brush's content is aligned to the bottom right of its tile. -->
<DrawingBrush Stretch="None" AlignmentX="Right" AlignmentY="Bottom">
<DrawingBrush.Drawing>
<GeometryDrawing Brush="MediumBlue">
<GeometryDrawing.Geometry>
<GeometryGroup>
<EllipseGeometry RadiusX="20" RadiusY="45" Center="50,50" />
<EllipseGeometry RadiusX="45" RadiusY="20" Center="50,50" />
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="Gray" Thickness="10" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
En el ejemplo se emplean objetos DrawingBrush para demostrar el uso de las propiedades AlignmentX y
AlignmentY. Estas propiedades se comportan de igual forma para todos los pinceles de mosaico: DrawingBrush,
ImageBrush y VisualBrush.

7.3.2.7.16. Cmo: Establecer el Tamao del Mosaico de un TileBrush


En este ejemplo se muestra cmo establecer el tamao del mosaico para TileBrush. De manera
predeterminada, TileBrush genera un mosaico nico que rellena completamente el rea que se est pintando.
Puede invalidar este comportamiento estableciendo las propiedades Viewport y ViewportUnits.

MCT: Luis Dueas

Pag 405 de 473

Manual de Windows Presentation Foundation


La propiedad Viewport especifica el tamao del mosaico para TileBrush. De manera predeterminada, el valor de
la propiedad Viewport es relativo al tamao del rea que se est pintando. Para que la propiedad Viewport
especifique un tamao del mosaico absoluto, establezca la propiedad ViewportUnits en Absolute.
Ejemplo
En el ejemplo siguiente se utiliza ImageBrush, un tipo de TileBrush, para pintar un rectngulo con mosaicos. En
el ejemplo se establece el rea de salida de cada mosaico en el 50% del alto y del ancho (el rectngulo). Como
resultado, el rectngulo se pintado con cuatro proyecciones de la imagen.
En la ilustracin siguiente se muestra el resultado de aplicar el ejemplo.

// Create an ImageBrush and set the size of each


// tile to 50% by 50% of the area being painted.
ImageBrush relativeTileSizeImageBrush = new ImageBrush();
relativeTileSizeImageBrush.ImageSource =
new BitmapImage(new Uri(@"sampleImages\cherries_larger.jpg", UriKind.Relative));
relativeTileSizeImageBrush.TileMode = TileMode.Tile;
// Specify the size of the base tile. By default, the size of the Viewport is relative
// to the area being painted, so a value of 0.5 indicates 50% of the output area.
relativeTileSizeImageBrush.Viewport = new Rect(0, 0, 0.5, 0.5);
// Create a rectangle and paint it with the ImageBrush.
Rectangle relativeTileSizeExampleRectangle = new Rectangle();
relativeTileSizeExampleRectangle.Width = 200;
relativeTileSizeExampleRectangle.Height = 150;
relativeTileSizeExampleRectangle.Stroke = Brushes.LimeGreen;
relativeTileSizeExampleRectangle.StrokeThickness = 1;
relativeTileSizeExampleRectangle.Fill = relativeTileSizeImageBrush;
<!-- The ImageBrush's tiles are set to 50% by 50% of the output area. -->
<Rectangle Width="200" Height="150" Stroke="LimeGreen" StrokeThickness="1">
<Rectangle.Fill>
<ImageBrush Viewport="0,0,0.5,0.5" TileMode="Tile"
ImageSource="sampleImages\cherries_larger.jpg" />
</Rectangle.Fill>
</Rectangle>
En el ejemplo siguiente se crea ImageBrush, se establece Viewport en 0,0,25,25 y ViewportUnits en Absolute, y
se lo utiliza para pintar otro rectngulo. Como resultado, el pincel genera mosaicos que tienen un ancho de 25
pxeles y un alto de 25 pxeles.
En la ilustracin siguiente se muestra el resultado de aplicar el ejemplo.

// Create an ImageBrush and set the size of each tile to 25 by 25 pixels.


ImageBrush absoluteTileSizeImageBrush = new ImageBrush();
absoluteTileSizeImageBrush.ImageSource =
new BitmapImage(new Uri(@"sampleImages\cherries_larger.jpg", UriKind.Relative));
absoluteTileSizeImageBrush.TileMode = TileMode.Tile;
// Specify that the Viewport is to be interpreted as an absolute value.
absoluteTileSizeImageBrush.ViewportUnits = BrushMappingMode.Absolute;
// Set the size of the base tile. Had we left ViewportUnits set to RelativeToBoundingBox

MCT: Luis Dueas

Pag 406 de 473

Manual de Windows Presentation Foundation


// (the default value), each tile would be 25 times the size of the area being painted.
// Because ViewportUnits is set to Absolute, the following line creates tiles that are 25
// by 25 pixels.
absoluteTileSizeImageBrush.Viewport = new Rect(0, 0, 25, 25);
// Create a rectangle and paint it with the ImageBrush.
Rectangle absoluteTileSizeExampleRectangle = new Rectangle();
absoluteTileSizeExampleRectangle.Width = 200;
absoluteTileSizeExampleRectangle.Height = 150;
absoluteTileSizeExampleRectangle.Stroke = Brushes.LimeGreen;
absoluteTileSizeExampleRectangle.StrokeThickness = 1;
absoluteTileSizeExampleRectangle.Fill = absoluteTileSizeImageBrush;
<!-- The ImageBrush's tiles are set to 25 by 25 pixels. -->
<Rectangle Width="200" Height="150" Stroke="LimeGreen" StrokeThickness="1">
<Rectangle.Fill>
<ImageBrush Viewport="0,0,25,25" ViewportUnits="Absolute" TileMode="Tile"
ImageSource="sampleImages\cherries_larger.jpg" />
</Rectangle.Fill>
</Rectangle>
Aunque en este ejemplo se utiliza la clase ImageBrush, las propiedades Viewport y ViewportUnits se comportan
exactamente igual para los dems objetos TileBrush, es decir, para DrawingBrush y VisualBrush.

7.3.2.7.17. Cmo: Transformar un Pincel


En este ejemplo se muestra cmo transformar objetos Brush utilizando sus dos propiedades de transformacin:
RelativeTransform y Transform.
En los ejemplos siguientes se utiliza un objeto RotateTransform para girar el contenido de un objeto
ImageBrush 45 grados.
En la ilustracin siguiente se muestra el objeto ImageBrush sin RotateTransform, con el objeto RotateTransform
aplicado a la propiedad RelativeTransform y con el objeto RotateTransform aplicado a la propiedad Transform.

Ejemplo
En el primer ejemplo se aplica un objeto RotateTransform a la propiedad RelativeTransform de un objeto
ImageBrush. Las propiedades CenterX y CenterY de un objeto RotateTransform se establecen ambas en 0,5,
que es la coordenada relativa del punto central de este contenido. Como resultado, el contenido del objeto
ImageBrush gira sobre su centro.
// Create an ImageBrush with a relative transform and use it to paint a rectangle.
ImageBrush relativeTransformImageBrush = new ImageBrush();
relativeTransformImageBrush.ImageSource =
new BitmapImage(new Uri(@"sampleImages\pinkcherries.jpg", UriKind.Relative));
// Create a 45 rotate transform about the brush's center
// and apply it to the brush's RelativeTransform property.
RotateTransform aRotateTransform = new RotateTransform();
aRotateTransform.CenterX = 0.5;
aRotateTransform.CenterY = 0.5;
aRotateTransform.Angle = 45;

MCT: Luis Dueas

Pag 407 de 473

Manual de Windows Presentation Foundation


relativeTransformImageBrush.RelativeTransform = aRotateTransform;
// Use the brush to paint a rectangle.
Rectangle relativeTransformImageBrushRectangle = new Rectangle();
relativeTransformImageBrushRectangle.Width = 175;
relativeTransformImageBrushRectangle.Height = 90;
relativeTransformImageBrushRectangle.Stroke = Brushes.Black;
relativeTransformImageBrushRectangle.Fill = relativeTransformImageBrush;
<Rectangle Width="175" Height="90" Stroke="Black">
<Rectangle.Fill>
<ImageBrush ImageSource="sampleImages\pinkcherries.jpg">
<ImageBrush.RelativeTransform>
<RotateTransform CenterX="0.5" CenterY="0.5" Angle="45" />
</ImageBrush.RelativeTransform>
</ImageBrush>
</Rectangle.Fill>
</Rectangle>
En el segundo ejemplo tambin se aplica un objeto RotateTransform a ImageBrush; sin embargo, en este
ejemplo se utiliza la propiedad Transform en lugar de la propiedad RelativeTransform.
Para girar el pincel sobre su centro, el ejemplo establece las propiedades CenterX y CenterY del objeto
RotateTransform en coordenadas absolutas. Dado que el pincel pinta un rectngulo que es de 175 por 90
pxeles, el punto central del rectngulo es (87,5, 45).
// Create an ImageBrush with a transform and use it to paint a rectangle.
ImageBrush transformImageBrush = new ImageBrush();
transformImageBrush.ImageSource =
new BitmapImage(new Uri(@"sampleImages\pinkcherries.jpg", UriKind.Relative));
// Create a 45 rotate transform about the brush's center
// and apply it to the brush's Transform property.
RotateTransform anotherRotateTransform = new RotateTransform();
anotherRotateTransform.CenterX = 87.5;
anotherRotateTransform.CenterY = 45;
anotherRotateTransform.Angle = 45;
transformImageBrush.Transform = anotherRotateTransform;
// Use the brush to paint a rectangle.
Rectangle transformImageBrushRectangle = new Rectangle();
transformImageBrushRectangle.Width = 175;
transformImageBrushRectangle.Height = 90;
transformImageBrushRectangle.Stroke = Brushes.Black;
transformImageBrushRectangle.Fill = transformImageBrush;
<Rectangle Width="175" Height="90" Stroke="Black">
<Rectangle.Fill>
<ImageBrush ImageSource="sampleImages\pinkcherries.jpg">
<ImageBrush.Transform>
<RotateTransform CenterX="87.5" CenterY="45" Angle="45" />
</ImageBrush.Transform>
</ImageBrush>
</Rectangle.Fill>
</Rectangle>

7.3.2.7.18. Cmo: Usar Colores del Sistema en un Degradado


Para utilizar un color del sistema en un degradado, se utilizan las propiedades estticas <SystemColor>Color y
<SystemColor>ColorKey de la clase SystemColors para obtener una referencia al color, donde <SystemColor>
es el nombre del color del sistema deseado. Utilice las propiedades <SystemColor>ColorKey cuando desee
crear una referencia dinmica que se actualice automticamente al cambiar el tema del sistema. De lo
contrario, utilice las propiedades <SystemColor>Color.
Ejemplo

MCT: Luis Dueas

Pag 408 de 473

Manual de Windows Presentation Foundation


En el ejemplo siguiente se utilizan recursos de colores del sistema dinmicos para crear un degradado.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowTitle="Dynamic System Colors Example" Background="White">
<StackPanel Margin="20">
<!-- Uses dynamic references to system colors to set the colors of gradient stops.
If these system colors change while this application
is running, the gradient will be updated automatically. -->
<Button Content="Hello, World!">
<Button.Background>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.0"
Color="{DynamicResource {x:Static SystemColors.DesktopColorKey}}" />
<GradientStop Offset="1.0"
Color="{DynamicResource {x:Static SystemColors.ControlLightLightColorKey}}"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Button.Background>
</Button>
</StackPanel>
</Page>
En el ejemplo siguiente se utilizan recursos de colores del sistema estticos para crear un degradado.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowTitle="Static System Colors Example" Background="White">
<StackPanel Margin="20">
<!-- Uses static references to system colors to set
the colors of gradient stops. If these system colors change while this application
is running, this button will not be updated until the page is loaded again. -->
<Button Content="Hello, World!">
<Button.Background>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.0" Color="{x:Static SystemColors.DesktopColor}" />
<GradientStop Offset="1.0" Color="{x:Static SystemColors.ControlLightLightColor}"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Button.Background>
</Button>
</StackPanel>
</Page>

7.3.3. Dibujos
Los objetos Drawing se utilizan para dibujar eficazmente formas, imgenes o texto. Los dibujos se utilizan al
pintar con un DrawingBrush o programar con objetos Visual.

7.3.3.1. Informacin General sobre Objetos Drawing


En este tema se presentan los objetos Drawing y se describe cmo utilizarlos para dibujar con eficacia formas,
mapas de bits, texto y multimedia. Utilice objetos Drawing cuando cree imgenes prediseadas, pinte con
DrawingBrush o use objetos Visual.
Qu es un objeto Drawing?
Un objeto de dibujo, o Drawing, describe el contenido visible, como una forma, un mapa de bits, vdeo o una
lnea de texto. Los diferentes tipos de dibujos describen tipos diferentes de contenido. A continuacin, se
muestra una lista de tipos diferentes de objetos de dibujo.

GeometryDrawing: dibuja una forma.


ImageDrawing: dibuja una imagen.

MCT: Luis Dueas

Pag 409 de 473

Manual de Windows Presentation Foundation

GlyphRunDrawing: dibuja texto.


VideoDrawing: reproduce un archivo de audio o vdeo.
DrawingGroup: dibuja otros dibujos. Utilice un grupo de dibujo para combinar otros dibujos en un
nico dibujo compuesto.

Los objetos Drawing son verstiles; existen numerosas maneras de utilizar un objeto Drawing.

Puede mostrarlo como una imagen utilizando un objeto DrawingImage y un control Image.
Puede utilizarlo con un objeto DrawingBrush para pintar un objeto, como la propiedad Background de
Page.

Puede utilizarlo para describir el aspecto de un objeto DrawingVisual.


Puede utilizarlo para enumerar el contenido de un objeto Visual.

WPF proporciona otros tipos de objetos que son capaces de dibujar formas, mapas de bits, texto y multimedia.
Por ejemplo, tambin puede utilizar objetos Shape para dibujar formas, mientras que el control MediaElement
proporciona otra manera de agregar vdeo a la aplicacin. As pues, cundo se deben utilizar los objetos
Drawing? Cuando se pueden sacrificar las caractersticas de nivel de marco de trabajo para obtener ventajas de
rendimiento, o cuando se necesitan las caractersticas de Freezable. Debido a que los objetos Drawing carecen
de compatibilidad con Sistema de diseo, la informacin de entrada y el foco, proporcionan ventajas de
rendimiento que los hace idneos para describir fondos o imgenes prediseadas, as como para dibujos de
bajo nivel con objetos Visual.
Al ser un objeto de tipo Freezable, los objetos Drawing obtienen varias caractersticas especiales, entre las que
se incluyen las siguientes: pueden declararse como recursos, compartirse entre varios objetos, convertirse en
objetos de slo lectura para mejorar el rendimiento, clonarse y convertirse en seguros para subprocesos.
Dibujar una forma
Para dibujar una forma, se utiliza un objeto GeometryDrawing. La propiedad Geometry de un dibujo de
geometra describe la forma que se va a dibujar, su propiedad Brush describe cmo se debe pintar el interior de
la forma, y su propiedad Pen describe cmo se debe dibujar su contorno.
En el ejemplo siguiente se utiliza GeometryDrawing para dibujar una forma. La forma se describe mediante un
objeto GeometryGroup y dos objetos EllipseGeometry. El interior de la forma se pinta con LinearGradientBrush
y su contorno se dibuja con un objeto Pen con la propiedad Black.
En este ejemplo se crea el objeto GeometryDrawing siguiente.
Objeto GeometryDrawing

// Create the Geometry to draw.


GeometryGroup ellipses = new GeometryGroup();
ellipses.Children.Add(new EllipseGeometry(new Point(50,50), 45, 20));
ellipses.Children.Add(new EllipseGeometry(new Point(50, 50), 20, 45));
// Create a GeometryDrawing.
GeometryDrawing aGeometryDrawing = new GeometryDrawing();
aGeometryDrawing.Geometry = ellipses;
// Paint the drawing with a gradient.
aGeometryDrawing.Brush = new LinearGradientBrush(Colors.Blue, Color.FromRgb(204,204,255),
new Point(0,0), new Point(1,1));
// Outline the drawing with a solid color.
aGeometryDrawing.Pen = new Pen(Brushes.Black, 10);
<GeometryDrawing>
<GeometryDrawing.Geometry>
<!-- Create a composite shape. -->
<GeometryGroup>

MCT: Luis Dueas

Pag 410 de 473

Manual de Windows Presentation Foundation


<EllipseGeometry Center="50,50" RadiusX="45" RadiusY="20" />
<EllipseGeometry Center="50,50" RadiusX="20" RadiusY="45" />
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Brush>
<!-- Paint the drawing with a gradient. -->
<LinearGradientBrush>
<GradientStop Offset="0.0" Color="Blue" />
<GradientStop Offset="1.0" Color="#CCCCFF" />
</LinearGradientBrush>
</GeometryDrawing.Brush>
<GeometryDrawing.Pen>
<!-- Outline the drawing with a solid color. -->
<Pen Thickness="10" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
Otras clases Geometry, como PathGeometry, permiten crear formas ms complejas con curvas y arcos.
Dibujar una imagen
Para dibujar una imagen, se utiliza ImageDrawing. La propiedad ImageSource de un objeto ImageDrawing
describe la imagen que se va a dibujar, mientras que su propiedad Rect define el rea donde se dibuja la
imagen.
En el ejemplo siguiente se dibuja una imagen en un rectngulo situado en (75,75), es decir, de 100 por 100
pxeles. En la ilustracin siguiente se muestra el objeto ImageDrawing creado por el ejemplo. Se agreg un
borde gris para mostrar los lmites de ImageDrawing.
Objeto ImageDrawing de 100 por 100 pxeles

// Create a 100 by 100 image with an upper-left point of (75,75).


ImageDrawing bigKiwi = new ImageDrawing();
bigKiwi.Rect = new Rect(75, 75, 100, 100);
bigKiwi.ImageSource = new BitmapImage(new Uri(@"sampleImages\kiwi.png", UriKind.Relative));
<!-- The Rect property specifies that the image only fill a 100 by 100
rectangular area. -->
<ImageDrawing Rect="75,75,100,100" ImageSource="sampleImages\kiwi.png"/>
Reproducir multimedia (slo mediante cdigo)
Nota:
Aunque puede declarar un objeto VideoDrawing en Lenguaje de marcado de aplicaciones extensible (XAML),
nicamente se pueden cargar y reproducir sus elementos multimedia mediante cdigo. Para reproducir
vdeo en Lenguaje de marcado de aplicaciones extensible (XAML), utilice un elemento MediaElement en su
lugar.
Para reproducir un archivo de audio o vdeo, se utiliza un objeto VideoDrawing y un objeto MediaPlayer. Hay
dos maneras de cargar y reproducir elementos multimedia. La primera es utilizar MediaPlayer y VideoDrawing
por s solos; la segunda consiste en crear su propio objeto MediaTimeline para utilizarlo con MediaPlayer y
VideoDrawing.
Nota:
Al distribuir los objetos multimedia con la aplicacin, no se puede usar ningn archivo multimedia como

MCT: Luis Dueas

Pag 411 de 473

Manual de Windows Presentation Foundation

recurso del proyecto, como se hara con una imagen. En lugar de ello, en el archivo del proyecto debe
establecer en Content el tipo de medios y establecer CopyToOutputDirectory en PreserveNewest o Always.
Para reproducir los elementos multimedia sin crear su propio objeto MediaTimeline, siga estos pasos.
1.

Crear un objeto MediaPlayer.


MediaPlayer player = new MediaPlayer();

2.

Utilice el mtodo Open para cargar el archivo multimedia.


player.Open(new Uri(@"sampleMedia\xbox.wmv", UriKind.Relative));

3.

Cree un control VideoDrawing.


VideoDrawing aVideoDrawing = new VideoDrawing();

4.

Especifique el tamao y la ubicacin donde dibujar el elemento multimedia estableciendo la propiedad


Rect de VideoDrawing.
aVideoDrawing.Rect = new Rect(0, 0, 100, 100);

5.

Establezca la propiedad Player de VideoDrawing con el objeto MediaPlayer que ha creado.


aVideoDrawing.Player = player;

6.

Utilice el mtodo Play de MediaPlayer para iniciar la reproduccin del elemento multimedia.
// Play the video once.
player.Play();

En el ejemplo siguiente se utilizan los objetos VideoDrawing y MediaPlayer para reproducir una vez un archivo
de vdeo.
// Create a VideoDrawing.
MediaPlayer player = new MediaPlayer();
player.Open(new Uri(@"sampleMedia\xbox.wmv", UriKind.Relative));
VideoDrawing aVideoDrawing = new VideoDrawing();
aVideoDrawing.Rect = new Rect(0, 0, 100, 100);
aVideoDrawing.Player = player;
// Play the video once.
player.Play();
Para controlar mejor el tiempo en los elementos multimedia, utilice un objeto MediaTimeline con los objetos
MediaPlayer y VideoDrawing. MediaTimeline permite especificar si el vdeo se debe repetir. Para utilizar
MediaTimeline con VideoDrawing, siga estos pasos:
1.

Declare MediaTimeline y establezca sus comportamientos del control de tiempo.


// Create a MediaTimeline.
MediaTimeline mTimeline =
new MediaTimeline(new Uri(@"sampleMedia\xbox.wmv", UriKind.Relative));
// Set the timeline to repeat.
mTimeline.RepeatBehavior = RepeatBehavior.Forever;

2.

Cree un MediaClock a partir de MediaTimeline.


// Create a clock from the MediaTimeline.
MediaClock mClock = mTimeline.CreateClock();

3.

Cree un MediaPlayer y utilice MediaClock para establecer su propiedad Clock.


MediaPlayer repeatingVideoDrawingPlayer = new MediaPlayer();
repeatingVideoDrawingPlayer.Clock = mClock;

4.

Cree un VideoDrawing y asigne MediaPlayer a la propiedad Player de VideoDrawing.


VideoDrawing repeatingVideoDrawing = new VideoDrawing();
repeatingVideoDrawing.Rect = new Rect(150, 0, 100, 100);
repeatingVideoDrawing.Player = repeatingVideoDrawingPlayer;

En el ejemplo siguiente se utiliza MediaTimeline con MediaPlayer y con VideoDrawing para reproducir
repetidamente un vdeo.
// Create a VideoDrawing that repeats. Create a MediaTimeline.
MediaTimeline mTimeline =
new MediaTimeline(new Uri(@"sampleMedia\xbox.wmv", UriKind.Relative));
// Set the timeline to repeat.
mTimeline.RepeatBehavior = RepeatBehavior.Forever;
// Create a clock from the MediaTimeline.
MediaClock mClock = mTimeline.CreateClock();
MediaPlayer repeatingVideoDrawingPlayer = new MediaPlayer();
repeatingVideoDrawingPlayer.Clock = mClock;

MCT: Luis Dueas

Pag 412 de 473

Manual de Windows Presentation Foundation


VideoDrawing repeatingVideoDrawing = new VideoDrawing();
repeatingVideoDrawing.Rect = new Rect(150, 0, 100, 100);
repeatingVideoDrawing.Player = repeatingVideoDrawingPlayer;
Tenga en cuenta que, al utilizar MediaTimeline, se utiliza el objeto ClockController interactivo devuelto de la
propiedad Controller de MediaClock para controlar la reproduccin multimedia, en lugar de los mtodos
interactivos de MediaPlayer.
Dibujar texto
Para dibujar texto, se utiliza un objeto GlyphRunDrawing y un objeto GlyphRun. En el ejemplo siguiente, se
utiliza GlyphRunDrawing para dibujar el texto "Hello World".
GlyphRun theGlyphRun = new GlyphRun(new GlyphTypeface(
new Uri(@"C:\WINDOWS\Fonts\TIMES.TTF")), 0, false, 13.333333333333334,
new ushort[]{43, 72, 79, 79, 82, 3, 58, 82, 85, 79, 71}, new Point(0, 12.29),
new double[]{9.62666666666667, 7.41333333333333, 2.96, 2.96, 7.41333333333333,
3.70666666666667, 12.5866666666667, 7.41333333333333, 4.44, 2.96, 7.41333333333333},
null, null, null, null, null, null);
GlyphRunDrawing gDrawing = new GlyphRunDrawing(Brushes.Black, theGlyphRun);
<GlyphRunDrawing ForegroundBrush="Black">
<GlyphRunDrawing.GlyphRun>
<GlyphRun CaretStops="{x:Null}" ClusterMap="{x:Null}" IsSideways="False"
GlyphOffsets="{x:Null}" GlyphIndices="43 72 79 79 82 3 58 82 85 79 71"
BaselineOrigin="0,12.29" FontRenderingEmSize="13.333333333333334"
DeviceFontName="{x:Null}" AdvanceWidths="9.62666666666667 7.41333333333333 2.96 2.96
7.41333333333333 3.70666666666667 12.5866666666667 7.41333333333333 4.44 2.96
7.41333333333333" BidiLevel="0">
<GlyphRun.GlyphTypeface>
<GlyphTypeface FontUri="C:\WINDOWS\Fonts\TIMES.TTF" />
</GlyphRun.GlyphTypeface>
</GlyphRun>
</GlyphRunDrawing.GlyphRun>
</GlyphRunDrawing>
GlyphRun es un objeto de bajo nivel cuya finalidad es utilizarlo en escenarios de presentacin de documentos
de formato fijo y de impresin. Una manera ms fcil de mostrar texto en la pantalla es utilizar Label o
TextBlock.
Dibujos compuestos
Un objeto DrawingGroup permite combinar varios dibujos en un nico dibujo compuesto. Mediante un objeto
DrawingGroup, puede combinar formas, imgenes y texto en un mismo objeto Drawing.
En el ejemplo siguiente se utiliza DrawingGroup para combinar dos objetos GeometryDrawing y un objeto
ImageDrawing. Este ejemplo produce el siguiente resultado.
Dibujo compuesto

// Create three drawings.


GeometryDrawing ellipseDrawing = new GeometryDrawing(
new SolidColorBrush(Color.FromArgb(102, 181, 243, 20)),
new Pen(Brushes.Black, 4), new EllipseGeometry(new Point(50,50), 50, 50));
ImageDrawing kiwiPictureDrawing = new ImageDrawing(
new BitmapImage(new Uri(@"sampleImages\kiwi.png", UriKind.Relative)),
new Rect(50,50,100,100));
GeometryDrawing ellipseDrawing2 = new GeometryDrawing(

MCT: Luis Dueas

Pag 413 de 473

Manual de Windows Presentation Foundation


new SolidColorBrush(Color.FromArgb(102,181,243,20)),
new Pen(Brushes.Black, 4), new EllipseGeometry(new Point(150, 150), 50, 50));
// Create a DrawingGroup to contain the drawings.
DrawingGroup aDrawingGroup = new DrawingGroup();
aDrawingGroup.Children.Add(ellipseDrawing);
aDrawingGroup.Children.Add(kiwiPictureDrawing);
aDrawingGroup.Children.Add(ellipseDrawing2);
<DrawingGroup>
<GeometryDrawing Brush="#66B5F314">
<GeometryDrawing.Geometry>
<EllipseGeometry Center="50,50" RadiusX="50" RadiusY="50"/>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness="4" />
</GeometryDrawing.Pen>
</GeometryDrawing>
<ImageDrawing ImageSource="sampleImages\kiwi.png" Rect="50,50,100,100"/>
<GeometryDrawing Brush="#66B5F314">
<GeometryDrawing.Geometry>
<EllipseGeometry Center="150,150" RadiusX="50" RadiusY="50"/>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness="4" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup>
DrawingGroup tambin permite aplicar mscaras de opacidad, transformaciones, efectos de mapa de bits y
otras operaciones a su contenido. Las operaciones de DrawingGroup se aplican en el orden siguiente:
OpacityMask, Opacity, BitmapEffect, ClipGeometry, GuidelineSet y, por ltimo, Transform.
En la ilustracin siguiente se muestra el orden en el que se aplican las operaciones de DrawingGroup.
Orden de las operaciones de DrawingGroup

En la tabla siguiente se describen las propiedades que puede utilizar para manipular el contenido de un objeto
DrawingGroup.
Propiedad

Descripcin

OpacityMask

Modifica la opacidad de partes seleccionadas del


contenido de DrawingGroup.

MCT: Luis Dueas

Ilustracin

Pag 414 de 473

Manual de Windows Presentation Foundation

Opacity

Cambia de manera uniforme la opacidad del


contenido de DrawingGroup. Utilice esta propiedad
para hacer un Drawing transparente total o
parcialmente.

BitmapEffect

Aplica BitmapEffect al contenido de DrawingGroup.

ClipGeometry

Recorta el contenido de DrawingGroup a un rea


descrita mediante Geometry.

GuidelineSet

Ajusta los pxeles independientes del dispositivo a


los pxeles del dispositivo a lo largo de las lneas de
gua especificadas. Esta propiedad es til para
asegurarse de que los grficos finamente detallados
se representen con nitidez en las pantallas con un
valor bajo de PPP.

Transform

Transforma el contenido de DrawingGroup.

Mostrar un dibujo como una imagen


Para mostrar Drawing con un control Image, utilice DrawingImage como propiedad Source del control Image y
establezca la propiedad DrawingImage.Drawing del objeto DrawingImage en el dibujo que desea mostrar.
En el ejemplo siguiente se utiliza un objeto DrawingImage y un control Image para mostrar GeometryDrawing.
Este ejemplo produce el siguiente resultado.
Objeto DrawingImage

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace SDKSample
{
public class DrawingImageExample : Page
{
public DrawingImageExample()
{
// Create the Geometry to draw.
GeometryGroup ellipses = new GeometryGroup();
ellipses.Children.Add(new EllipseGeometry(new Point(50,50), 45, 20));
ellipses.Children.Add(new EllipseGeometry(new Point(50, 50), 20, 45));
// Create a GeometryDrawing.
GeometryDrawing aGeometryDrawing = new GeometryDrawing();

MCT: Luis Dueas

Pag 415 de 473

Manual de Windows Presentation Foundation


aGeometryDrawing.Geometry = ellipses;
// Paint the drawing with a gradient.
aGeometryDrawing.Brush = new LinearGradientBrush(Colors.Blue,
Color.FromRgb(204,204,255), new Point(0,0), new Point(1,1));
// Outline the drawing with a solid color.
aGeometryDrawing.Pen = new Pen(Brushes.Black, 10);
// Use a DrawingImage and an Image control to display the drawing.
DrawingImage geometryImage = new DrawingImage(aGeometryDrawing);
// Freeze the DrawingImage for performance benefits.
geometryImage.Freeze();
Image anImage = new Image();
anImage.Source = geometryImage;
anImage.HorizontalAlignment = HorizontalAlignment.Left;
// Place the image inside a border and add it to the page.
Border exampleBorder = new Border();
exampleBorder.Child = anImage;
exampleBorder.BorderBrush = Brushes.Gray;
exampleBorder.BorderThickness = new Thickness(1);
exampleBorder.HorizontalAlignment = HorizontalAlignment.Left;
exampleBorder.VerticalAlignment = VerticalAlignment.Top;
exampleBorder.Margin = new Thickness(10);
this.Margin = new Thickness(20);
this.Background = Brushes.White;
this.Content = exampleBorder;
}
}
}
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PresentationOption="http://schemas.microsoft.com/winfx/2006/xaml/presentation/option"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions"
Background="White" Margin="20">
<Border BorderBrush="Gray" BorderThickness="1" HorizontalAlignment="Left"
VerticalAlignment="Top" Margin="10">
<!-- This image uses a Drawing object for its source. -->
<Image>
<Image.Source>
<DrawingImage PresentationOptions:Freeze="True">
<DrawingImage.Drawing>
<GeometryDrawing>
<GeometryDrawing.Geometry>
<GeometryGroup>
<EllipseGeometry Center="50,50" RadiusX="45" RadiusY="20" />
<EllipseGeometry Center="50,50" RadiusX="20" RadiusY="45" />
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Brush>
<LinearGradientBrush>
<GradientStop Offset="0.0" Color="Blue" />
<GradientStop Offset="1.0" Color="#CCCCFF" />
</LinearGradientBrush>
</GeometryDrawing.Brush>
<GeometryDrawing.Pen>
<Pen Thickness="10" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>

MCT: Luis Dueas

Pag 416 de 473

Manual de Windows Presentation Foundation


</Image>
</Border>
</Page>
Pintar un objeto con un dibujo
Un objeto DrawingBrush es un tipo de pincel que pinta una rea con un objeto de dibujo. Puede utilizarlo para
pintar casi cualquier objeto grfico con un dibujo. La propiedad Drawing de un objeto DrawingBrush describe su
propiedad Drawing. Para representar un Drawing con DrawingBrush, agrguelo al pincel utilizando la propiedad
Drawing del pincel y utilice el pincel para pintar un objeto grfico, como un control o panel.
En los ejemplos siguientes se utiliza un DrawingBrush para pintar la propiedad Fill de un objeto Rectangle con
una trama creada a partir de un objeto GeometryDrawing. Este ejemplo produce el siguiente resultado.
Objeto GeometryDrawing utilizado con un objeto DrawingBrush

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace SDKSample
{
public class DrawingBrushExample : Page
{
public DrawingBrushExample()
{
// Create the Geometry to draw.
GeometryGroup ellipses = new GeometryGroup();
ellipses.Children.Add(new EllipseGeometry(new Point(50,50), 45, 20));
ellipses.Children.Add(new EllipseGeometry(new Point(50, 50), 20, 45));
// Create a GeometryDrawing.
GeometryDrawing aGeometryDrawing = new GeometryDrawing();
aGeometryDrawing.Geometry = ellipses;
// Paint the drawing with a gradient.
aGeometryDrawing.Brush = new LinearGradientBrush(Colors.Blue,
Color.FromRgb(204,204,255), new Point(0,0), new Point(1,1));
// Outline the drawing with a solid color.
aGeometryDrawing.Pen = new Pen(Brushes.Black, 10);
DrawingBrush patternBrush = new DrawingBrush(aGeometryDrawing);
patternBrush.Viewport = new Rect(0, 0, 0.25, 0.25);
patternBrush.TileMode = TileMode.Tile;
patternBrush.Freeze();
// Create an object to paint.
Rectangle paintedRectangle = new Rectangle();
paintedRectangle.Width = 100;
paintedRectangle.Height = 100;
paintedRectangle.Fill = patternBrush;
// Place the image inside a border and add it to the page.
Border exampleBorder = new Border();
exampleBorder.Child = paintedRectangle;
exampleBorder.BorderBrush = Brushes.Gray;
exampleBorder.BorderThickness = new Thickness(1);
exampleBorder.HorizontalAlignment = HorizontalAlignment.Left;
exampleBorder.VerticalAlignment = VerticalAlignment.Top;
exampleBorder.Margin = new Thickness(10);
this.Margin = new Thickness(20);
this.Background = Brushes.White;
this.Content = exampleBorder;

MCT: Luis Dueas

Pag 417 de 473

Manual de Windows Presentation Foundation


}
}
}
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PresentationOption="http://schemas.microsoft.com/winfx/2006/xaml/presentation/option"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions"
Margin="20" Background="White">
<Border BorderBrush="Gray" BorderThickness="1" HorizontalAlignment="Left"
VerticalAlignment="Top" Margin="10">
<Rectangle Width="100" Height="100">
<Rectangle.Fill>
<DrawingBrush PresentationOptions:Freeze="True" Viewport="0,0,0.25,0.25"
TileMode="Tile">
<DrawingBrush.Drawing>
<GeometryDrawing>
<GeometryDrawing.Geometry>
<GeometryGroup>
<EllipseGeometry Center="50,50" RadiusX="45" RadiusY="20" />
<EllipseGeometry Center="50,50" RadiusX="20" RadiusY="45" />
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Brush>
<LinearGradientBrush>
<GradientStop Offset="0.0" Color="Blue" />
<GradientStop Offset="1.0" Color="#CCCCFF" />
</LinearGradientBrush>
</GeometryDrawing.Brush>
<GeometryDrawing.Pen>
<Pen Thickness="10" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
</Border>
</Page>
La clase DrawingBrush proporciona gran variedad de opciones para expandir y colocar en mosaico su contenido.
Representar un dibujo con un objeto visual
Un objeto DrawingVisual es un tipo de objeto visual diseado para representar un dibujo. El trabajo directo con
la capa visual es una opcin para aquellos programadores que desean generar un entorno grfico muy
personalizado y no se describe en esta informacin general.
Objetos DrawingContext
La clase DrawingContext permite rellenar un objeto Visual o Drawing con contenido visual. Muchos de estos
objetos grficos de bajo nivel utilizan DrawingContext porque describe con gran eficacia el contenido grfico.
Aunque los mtodos de dibujo de DrawingContext parecen similares a los mtodos de dibujo de tipo
System.Drawing.Graphics, en realidad son muy distintos. DrawingContext se utiliza con un sistema de grficos
de modo retenido, mientras que System.Drawing.Graphics se utiliza con un sistema de grficos de modo
inmediato. Al utilizar los comandos de dibujo de un objeto DrawingContext, en realidad est almacenando un
conjunto de instrucciones de representacin (si bien el mecanismo de almacenamiento exacto depende del tipo
de objeto que proporcione DrawingContext) que el sistema de grficos utilizar ms adelante; no dibuja en la
pantalla en tiempo real.

MCT: Luis Dueas

Pag 418 de 473

Manual de Windows Presentation Foundation


Nunca se crea directamente una instancia de DrawingContext; sin embargo, se puede adquirir un contexto de
dibujo a partir de determinados mtodos, como DrawingGroup.Open y DrawingVisual.RenderOpen.
Enumerar el contenido de un objeto visual
Adems de sus otros usos, los objetos Drawing tambin proporcionan un modelo de objetos para enumerar el
contenido de Visual.
En el ejemplo siguiente se utiliza el mtodo GetDrawing para recuperar el valor de DrawingGroup de un objeto
Visual y enumerarlo.
public void RetrieveDrawing(Visual v)
{
DrawingGroup dGroup = VisualTreeHelper.GetDrawing(v);
EnumDrawingGroup(dGroup);
}
// Enumerate the drawings in the DrawingGroup.
public void EnumDrawingGroup(DrawingGroup drawingGroup)
{
DrawingCollection dc = drawingGroup.Children;
// Enumerate the drawings in the DrawingCollection.
foreach (Drawing drawing in dc)
{
// If the drawing is a DrawingGroup, call the function recursively.
if (drawing.GetType() == typeof(DrawingGroup))
{
EnumDrawingGroup((DrawingGroup)drawing);
}
else if (drawing.GetType() == typeof(GeometryDrawing))
{
// Perform action based on drawing type.
}
else if (drawing.GetType() == typeof(ImageDrawing))
{
// Perform action based on drawing type.
}
else if (drawing.GetType() == typeof(GlyphRunDrawing))
{
// Perform action based on drawing type.
}
else if (drawing.GetType() == typeof(VideoDrawing))
{
// Perform action based on drawing type.
}
}
}

7.3.3.2. Temas Cmo de Dibujo


En los temas de esta seccin se describe cmo utilizar los objetos Drawing para dibujar formas, imgenes o
texto.

7.3.3.2.1. Cmo: Aplicar un Objeto BitmapEffect a un Dibujo


En este ejemplo se muestra cmo aplicar un objeto BitmapEffect a un dibujo. Utilice BitmapEffect para
desenfocar o grabar en relieve, o para aplicar otros efectos visuales al contenido representado.
Slo los objetos DrawingGroup admiten efectos de mapa de bits. Para aplicar un efecto de mapa de bits a otro
tipo de objeto Drawing, agrguelo a un objeto DrawingGroup y establezca la propiedad BitmapEffect del objeto
DrawingGroup.

MCT: Luis Dueas

Pag 419 de 473

Manual de Windows Presentation Foundation

Nota:
Los efectos de mapa de bits de Windows Presentation Foundation (WPF) se representan mediante software.
Cualquier objeto que aplique un objeto BitmapEffecttambin se representar en el software. Evite utilizar
objetos BitmapEffect en presentaciones visuales o animaciones grandes porque este escenario puede
provocar una disminucin del rendimiento del sistema.
Ejemplo
En el ejemplo siguiente se utiliza DrawingGroup para aplicar BlurBitmapEffect a varios objetos Geometry
Drawing.
En la siguiente ilustracin se muestra el resultado de este ejemplo.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Effects;
namespace SDKSample
{
/// <summary>
/// This example creates two DrawingGroup objects,
/// one with a bitmap effect and one without.
/// </summary>
public class BitmapEffectExample : Page
{
public BitmapEffectExample()
{
// Create a DrawingGroup that has no BitmapEffect
PathFigure pLineFigure = new PathFigure();
pLineFigure.StartPoint = new Point(25, 25);
PolyLineSegment pLineSegment = new PolyLineSegment();
pLineSegment.Points.Add(new Point(0,50));
pLineSegment.Points.Add(new Point(25, 75));
pLineSegment.Points.Add(new Point(50, 50));
pLineSegment.Points.Add(new Point(25, 25));
pLineSegment.Points.Add(new Point(25, 0));
pLineFigure.Segments.Add(pLineSegment);
PathGeometry pGeometry = new PathGeometry();
pGeometry.Figures.Add(pLineFigure);
GeometryDrawing drawing1 = new GeometryDrawing(Brushes.Lime,
new Pen(Brushes.Black, 10), pGeometry);
GeometryDrawing drawing2 = new GeometryDrawing(Brushes.Lime,
new Pen(Brushes.Black, 2),new EllipseGeometry(new Point(10,10), 5, 5));
// Create a DrawingGroup
DrawingGroup drawingGroupWithoutBitmapEffect = new DrawingGroup();
drawingGroupWithoutBitmapEffect.Children.Add(drawing1);
drawingGroupWithoutBitmapEffect.Children.Add(drawing2);
// Use an Image control and a DrawingImage to display the drawing.
DrawingImage drawingImage01=new DrawingImage(drawingGroupWithoutBitmapEffect);
// Freeze the DrawingImage for performance benefits.
drawingImage01.Freeze();
Image image01 = new Image();
image01.Source = drawingImage01;
image01.Stretch = Stretch.None;

MCT: Luis Dueas

Pag 420 de 473

Manual de Windows Presentation Foundation


image01.HorizontalAlignment = HorizontalAlignment.Left;
// Create another DrawingGroup and apply a blur effect to it.
// Create a clone of the first DrawingGroup.
DrawingGroup drawingGroupWithBitmapEffect=drawingGroupWithoutBitmapEffect.Clone();
// Create a blur effect.
BlurBitmapEffect blurEffect = new BlurBitmapEffect();
blurEffect.Radius = 3.0;
// Apply it to the drawing group.
drawingGroupWithBitmapEffect.BitmapEffect = blurEffect;
// Use another Image control and DrawingImage to display the drawing.
DrawingImage drawingImage02 = new DrawingImage(drawingGroupWithBitmapEffect);
// Freeze the DrawingImage for performance benefits.
drawingImage02.Freeze();
Image image02 = new Image();
image02.Source = drawingImage02;
image02.Stretch = Stretch.None;
image02.HorizontalAlignment = HorizontalAlignment.Left;
// Create borders around the images and add them to the page.
Border border01 = new Border();
border01.BorderBrush = Brushes.Gray;
border01.BorderThickness = new Thickness(1);
border01.VerticalAlignment = VerticalAlignment.Top;
border01.Margin = new Thickness(10);
border01.Child = image01;
Border border02 = new Border();
border02.BorderBrush = Brushes.Gray;
border02.BorderThickness = new Thickness(1);
border02.VerticalAlignment = VerticalAlignment.Top;
border02.Margin = new Thickness(50,10,10,10);
border02.Child = image02;
StackPanel mainPanel = new StackPanel();
mainPanel.Orientation = Orientation.Horizontal;
mainPanel.HorizontalAlignment = HorizontalAlignment.Left;
mainPanel.VerticalAlignment = VerticalAlignment.Top;
mainPanel.Children.Add(border01);
mainPanel.Children.Add(border02);
// Use a DrawingBrush to create a checkered background for the page.
GeometryDrawing backgroundSquareDrawing = new GeometryDrawing(Brushes.White,
null, new RectangleGeometry(new Rect(0,0,1,1)));
GeometryGroup twoRectangles = new GeometryGroup();
twoRectangles.Children.Add(new RectangleGeometry(new Rect(0,0,0.5,0.5)));
twoRectangles.Children.Add(new RectangleGeometry(new Rect(0.5,0.5,0.5,0.5)));
SolidColorBrush squaresBrush = new SolidColorBrush(Color.FromArgb(102,204,204,204));
squaresBrush.Opacity = 0.4;
GeometryDrawing checkerDrawing = new GeometryDrawing(squaresBrush, null,
twoRectangles);
DrawingGroup checkerDrawings = new DrawingGroup();
checkerDrawings.Children.Add(backgroundSquareDrawing);
checkerDrawings.Children.Add(checkerDrawing);
DrawingBrush checkerBrush = new DrawingBrush(checkerDrawings);
checkerBrush.Viewport = new Rect(0,0,10,10);
checkerBrush.ViewportUnits = BrushMappingMode.Absolute;
checkerBrush.TileMode = TileMode.Tile;
checkerBrush.Freeze();
this.Background = checkerBrush;
this.Content = mainPanel;
}
}
}
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

MCT: Luis Dueas

Pag 421 de 473

Manual de Windows Presentation Foundation


xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PresentationOption="http://schemas.microsoft.com/winfx/2006/xaml/presentation/option"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions">
<StackPanel Margin="20" Orientation="Horizontal" HorizontalAlignment="Left"
VerticalAlignment="Top">
<!-- Shows the DrawingGroup without the blur effect. -->
<Border BorderBrush="Gray" BorderThickness="1" Margin="10" VerticalAlignment="Top">
<Image Stretch="None" HorizontalAlignment="Left">
<Image.Source>
<DrawingImage PresentationOptions:Freeze="True">
<DrawingImage.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="Lime" Geometry="M 25,25 L 0,50 25,75 50,50 25,25 25,0">
<GeometryDrawing.Pen>
<Pen Thickness="10" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
<GeometryDrawing Brush="Lime">
<GeometryDrawing.Geometry>
<EllipseGeometry Center="10,10" RadiusX="5" RadiusY="5" />
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="2" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Border>
<Border BorderBrush="Gray" BorderThickness="1" Margin="50,10,10,10"
VerticalAlignment="Top">
<Image Stretch="None" HorizontalAlignment="Left">
<Image.Source>
<DrawingImage PresentationOptions:Freeze="True">
<DrawingImage.Drawing>
<!-- The drawing group, with a BlurBitmapEffect. -->
<DrawingGroup>
<GeometryDrawing Brush="Lime" Geometry="M 25,25 L 0,50 25,75 50,50 25,25 25,0">
<GeometryDrawing.Pen>
<Pen Thickness="10" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
<GeometryDrawing Brush="Lime">
<GeometryDrawing.Geometry>
<EllipseGeometry Center="10,10" RadiusX="5" RadiusY="5" />
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="2" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
<DrawingGroup.BitmapEffect>
<BlurBitmapEffect Radius="5" />
</DrawingGroup.BitmapEffect>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Border>

MCT: Luis Dueas

Pag 422 de 473

Manual de Windows Presentation Foundation


</StackPanel>
<Page.Background>
<!-- Creates a checkered background. -->
<DrawingBrush Viewport="0,0,10,10" ViewportUnits="Absolute" TileMode="Tile"
PresentationOptions:Freeze="True">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="White">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0 0 1 1"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="#66CCCCCC">
<GeometryDrawing.Geometry>
<GeometryGroup>
<RectangleGeometry Rect="0 0 0.5 0.5" />
<RectangleGeometry Rect="0.5 0.5 0.5 0.5"/>
</GeometryGroup>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Page.Background>
</Page>

7.3.3.2.2. Cmo: Aplicar un Objeto GuidelineSet a un Dibujo


En este ejemplo se muestra cmo aplicar una propiedad GuidelineSet a un objeto DrawingGroup.
La clase DrawingGroup es el nico tipo de Drawing que tiene una propiedad GuidelineSet. Para aplicar
GuidelineSet a otro tipo de Drawing, deber agregarla a un objeto DrawingGroup y, a continuacin, aplicar
GuidelineSet a DrawingGroup.
Ejemplo
En el ejemplo siguiente se crean dos objetos DrawingGroup que son casi idnticos; la nica diferencia es que el
segundo objeto DrawingGroup tiene la propiedad GuidelineSet y el primero, no.
En la siguiente ilustracin se muestra el resultado del ejemplo. Dado que la diferencia de representacin entre
los dos objetos DrawingGroup es tan sutil, se han ampliado las partes correspondientes de los dibujos.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace SDKSample
{
/// <summary>

MCT: Luis Dueas

Pag 423 de 473

Manual de Windows Presentation Foundation


/// This example shows how to apply a GuidelineSet to a drawing.
/// </summary>
public class DrawingGroupGuidelineSetExample : Page
{
public DrawingGroupGuidelineSetExample()
{
// Create a DrawingGroup that has no guideline set
GeometryDrawing drawing1 = new GeometryDrawing(Brushes.Black, null,
new RectangleGeometry(new Rect(0,20,30,80)));
GeometryGroup whiteRectangles = new GeometryGroup();
whiteRectangles.Children.Add(new RectangleGeometry(new Rect(5.5, 25, 20, 20)));
whiteRectangles.Children.Add(new RectangleGeometry(new Rect(5.5, 50, 20, 20)));
whiteRectangles.Children.Add(new RectangleGeometry(new Rect(5.5, 75, 20, 20)));
GeometryDrawing drawing2 = new GeometryDrawing(Brushes.White, null,
whiteRectangles);
// Create a DrawingGroup
DrawingGroup drawingGroupWithoutGuidelines = new DrawingGroup();
drawingGroupWithoutGuidelines.Children.Add(drawing1);
drawingGroupWithoutGuidelines.Children.Add(drawing2);
// Use an Image control and a DrawingImage to display the drawing.
DrawingImage drawingImage01 = new DrawingImage(drawingGroupWithoutGuidelines);
// Freeze the DrawingImage for performance benefits.
drawingImage01.Freeze();
Image image01 = new Image();
image01.Source = drawingImage01;
image01.Stretch = Stretch.None;
image01.HorizontalAlignment = HorizontalAlignment.Left;
image01.Margin = new Thickness(10);
// Create another DrawingGroup and apply a blur effect to it.
// Create a clone of the first DrawingGroup.
DrawingGroup drawingGroupWithGuidelines=drawingGroupWithoutGuidelines.Clone();
// Create a guideline set.
GuidelineSet guidelines = new GuidelineSet();
guidelines.GuidelinesX.Add(5.5);
guidelines.GuidelinesX.Add(25.5);
guidelines.GuidelinesY.Add(25);
guidelines.GuidelinesY.Add(50);
guidelines.GuidelinesY.Add(75);
// Apply it to the drawing group.
drawingGroupWithGuidelines.GuidelineSet = guidelines;
// Use another Image control and DrawingImage to display the drawing.
DrawingImage drawingImage02 = new DrawingImage(drawingGroupWithGuidelines);
// Freeze the DrawingImage for performance benefits.
drawingImage02.Freeze();
Image image02 = new Image();
image02.Source = drawingImage02;
image02.Stretch = Stretch.None;
image02.HorizontalAlignment = HorizontalAlignment.Left;
image02.Margin = new Thickness(50, 10, 10, 10);
StackPanel mainPanel = new StackPanel();
mainPanel.Orientation = Orientation.Horizontal;
mainPanel.HorizontalAlignment = HorizontalAlignment.Left;
mainPanel.Margin = new Thickness(20);
mainPanel.Children.Add(image01);
mainPanel.Children.Add(image02);
// Use a DrawingBrush to create a grid background.
GeometryDrawing backgroundRectangleDrawing = new GeometryDrawing(Brushes.White,
null, new RectangleGeometry(new Rect(0,0,1,1)));
PolyLineSegment backgroundLine1 = new PolyLineSegment();
backgroundLine1.Points.Add(new Point(1, 0));
backgroundLine1.Points.Add(new Point(1, 0.1));
backgroundLine1.Points.Add(new Point(0, 0.1));

MCT: Luis Dueas

Pag 424 de 473

Manual de Windows Presentation Foundation


PathFigure line1Figure = new PathFigure();
line1Figure.Segments.Add(backgroundLine1);
PathGeometry backgroundLine1Geometry = new PathGeometry();
backgroundLine1Geometry.Figures.Add(line1Figure);
GeometryDrawing backgroundLineDrawing1 = new GeometryDrawing(
new SolidColorBrush(Color.FromArgb(255,204,204,255)),
null, backgroundLine1Geometry);
PolyLineSegment backgroundLine2 = new PolyLineSegment();
backgroundLine2.Points.Add(new Point(0, 1));
backgroundLine2.Points.Add(new Point(0.1, 1));
backgroundLine2.Points.Add(new Point(0.1, 0));
PathFigure line2Figure = new PathFigure();
line2Figure.Segments.Add(backgroundLine2);
PathGeometry backgroundLine2Geometry = new PathGeometry();
backgroundLine2Geometry.Figures.Add(line2Figure);
GeometryDrawing backgroundLineDrawing2 = new GeometryDrawing(
new SolidColorBrush(Color.FromArgb(255, 204, 204, 255)),
null, backgroundLine2Geometry);
DrawingGroup backgroundGroup = new DrawingGroup();
backgroundGroup.Children.Add(backgroundRectangleDrawing);
backgroundGroup.Children.Add(backgroundLineDrawing1);
backgroundGroup.Children.Add(backgroundLineDrawing2);
DrawingBrush gridPatternBrush = new DrawingBrush(backgroundGroup);
gridPatternBrush.Viewport = new Rect(0, 0, 10, 10);
gridPatternBrush.ViewportUnits = BrushMappingMode.Absolute;
gridPatternBrush.TileMode = TileMode.Tile;
gridPatternBrush.Freeze();
Border mainBorder = new Border();
mainBorder.Background = gridPatternBrush;
mainBorder.BorderThickness = new Thickness(1);
mainBorder.BorderBrush = Brushes.Gray;
mainBorder.HorizontalAlignment = HorizontalAlignment.Left;
mainBorder.VerticalAlignment = VerticalAlignment.Top;
mainBorder.Margin = new Thickness(20);
mainBorder.Child = mainPanel;
// Add the items to the page.
this.Content = mainBorder;
this.Background = Brushes.White;
}
}
}
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PresentationOption="http://schemas.microsoft.com/winfx/2006/xaml/presentation/option"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions">
<Border BorderThickness="1" BorderBrush="Gray" HorizontalAlignment="Left"
VerticalAlignment="Top" Margin="20">
<StackPanel Margin="20" Orientation="Horizontal">
<Image Stretch="None" Margin="10">
<Image.Source>
<DrawingImage PresentationOptions:Freeze="True">
<DrawingImage.Drawing>
<!-- This DrawingGroup has no GuidelineSet. -->
<DrawingGroup>
<GeometryDrawing Brush="Black">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,20,30,80" />
</GeometryDrawing.Geometry>
</GeometryDrawing>

MCT: Luis Dueas

Pag 425 de 473

Manual de Windows Presentation Foundation


<GeometryDrawing Brush="White">
<GeometryDrawing.Geometry>
<GeometryGroup>
<RectangleGeometry Rect="5.5,25, 20,20" />
<RectangleGeometry Rect="5.5,50, 20,20" />
<RectangleGeometry Rect="5.5,75, 20,20" />
</GeometryGroup>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<Image Stretch="None" Margin="50,10,10,10">
<Image.Source>
<DrawingImage PresentationOptions:Freeze="True">
<DrawingImage.Drawing>
<!-- This DrawingGroup has a GuidelineSet. -->
<DrawingGroup>
<GeometryDrawing Brush="Black">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,20,30,80" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="White">
<GeometryDrawing.Geometry>
<GeometryGroup>
<RectangleGeometry Rect="5.5,25, 20,20" />
<RectangleGeometry Rect="5.5,50, 20,20" />
<RectangleGeometry Rect="5.5,75, 20,20" />
</GeometryGroup>
</GeometryDrawing.Geometry>
</GeometryDrawing>
<DrawingGroup.GuidelineSet>
<!-- The GuidelineSet -->
<GuidelineSet GuidelinesX="5.5,25.5" GuidelinesY="25,50,75" />
</DrawingGroup.GuidelineSet>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</StackPanel>
<Border.Background>
<DrawingBrush Viewport="0,0,10,10" ViewportUnits="Absolute" TileMode="Tile"
PresentationOptions:Freeze="True">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="White">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,1,1" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Geometry="M0,0 L1,0 1,0.1, 0,0.1Z " Brush="#CCCCFF" />
<GeometryDrawing Geometry="M0,0 L0,1 0.1,1, 0.1,0Z" Brush="#CCCCFF" />
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Border.Background>
</Border>
</Page>

MCT: Luis Dueas

Pag 426 de 473

Manual de Windows Presentation Foundation

7.3.3.2.3. Cmo: Aplicar una Mscara de Opacidad a un Dibujo


En este ejemplo se muestra cmo aplicar una mscara de opacidad a un objeto Drawing. La clase
DrawingGroup es el nico tipo de objeto Drawing que admite mscaras de opacidad.
Para aplicar una mscara de opacidad a un objeto Drawing, agrguelo a DrawingGroup y establezca la
propiedad OpacityMask del objeto DrawingGroup.
En la ilustracin siguiente se observan tres vistas de DrawingGroup: el dibujo sin mscara de opacidad, la
mscara de opacidad sola y el objeto DrawingGroup despus de aplicarle la mscara de opacidad.

Ejemplo
El ejemplo siguiente usa RadialGradientBrush como mscara de opacidad para DrawingGroup.
Nota:
Aunque en este ejemplo se usa RadialGradientBrush como mscara de opacidad, tambin son buenos
candidatos los objetos LinearGradientBrush, DrawingBrush, ImageBrush y VisualBrush.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace SDKSample
{
/// <summary>
/// Shows how to create and apply an OpacityMask to a DrawinGroup.
/// </summary>
public class OpacityMaskExample : Page
{
public OpacityMaskExample()
{
// Create a GeometryDrawing. Define the drawing's contents.
PathFigure pLineFigure = new PathFigure();
pLineFigure.StartPoint = new Point(25, 25);
PolyLineSegment pLineSegment = new PolyLineSegment();
pLineSegment.Points.Add(new Point(0, 50));
pLineSegment.Points.Add(new Point(25, 75));
pLineSegment.Points.Add(new Point(50, 50));
pLineSegment.Points.Add(new Point(25, 25));
pLineSegment.Points.Add(new Point(25, 0));
pLineFigure.Segments.Add(pLineSegment);
PathGeometry pGeometry = new PathGeometry();
pGeometry.Figures.Add(pLineFigure);
GeometryDrawing drawing1 = new GeometryDrawing(Brushes.Lime,
new Pen(Brushes.Black, 10), pGeometry);
// Create another GeometryDrawing.
GeometryDrawing drawing2 = new GeometryDrawing(Brushes.Lime,
new Pen(Brushes.Black, 2), new EllipseGeometry(new Point(10, 10), 5, 5));
// Create the DrawingGroup and add the geometry drawings.
DrawingGroup aDrawingGroup = new DrawingGroup();
aDrawingGroup.Children.Add(drawing1);
aDrawingGroup.Children.Add(drawing2);
// Define an opacity mask and apply it to the drawing group.

MCT: Luis Dueas

Pag 427 de 473

Manual de Windows Presentation Foundation


RadialGradientBrush opacityMask = new RadialGradientBrush();
opacityMask.GradientStops.Add(new GradientStop(Color.FromArgb(255,0,0,0),0.0));
opacityMask.GradientStops.Add(new GradientStop(Color.FromArgb(0,0,0,0),0.55));
opacityMask.GradientStops.Add(new GradientStop(Color.FromArgb(255,0,0,0),0.65));
opacityMask.GradientStops.Add(new GradientStop(Color.FromArgb(0,0,0,0),0.75));
opacityMask.GradientStops.Add(new GradientStop(Color.FromArgb(255,0,0,0),0.80));
opacityMask.GradientStops.Add(new GradientStop(Color.FromArgb(0,0,0,0),0.90));
opacityMask.GradientStops.Add(new GradientStop(Color.FromArgb(255,0,0,0),1.0));
aDrawingGroup.OpacityMask = opacityMask;
// Use an Image control and a DrawingImage to display the drawing.
DrawingImage aDrawingImage = new DrawingImage(aDrawingGroup);
// Freeze the DrawingImage for performance benefits.
aDrawingImage.Freeze();
Image anImage = new Image();
anImage.Source = aDrawingImage;
anImage.Stretch = Stretch.None;
anImage.HorizontalAlignment = HorizontalAlignment.Left;
// Create a border around the images and add it to the page.
Border imageBorder = new Border();
imageBorder.BorderBrush = Brushes.Gray;
imageBorder.BorderThickness = new Thickness(1);
imageBorder.VerticalAlignment = VerticalAlignment.Top;
imageBorder.HorizontalAlignment = HorizontalAlignment.Left;
imageBorder.Margin = new Thickness(20);
imageBorder.Child = anImage;
this.Background = Brushes.White;
this.Margin = new Thickness(20);
this.Content = imageBorder;
}
}
}
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PresentationOption="http://schemas.microsoft.com/winfx/2006/xaml/presentation/option"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions" Margin="20" Background="White">
<Border BorderBrush="Gray" BorderThickness="1" HorizontalAlignment="Left"
VerticalAlignment="Top" Margin="20">
<Image Stretch="None" HorizontalAlignment="Left">
<Image.Source>
<DrawingImage PresentationOptions:Freeze="True">
<DrawingImage.Drawing>
<!-- A DrawingGroup with a RadialGradientBrush as its opacity mask. -->
<DrawingGroup>
<GeometryDrawing Brush="Lime" Geometry="M 25,25 L 0,50 25,75 50,50 25,25 25,0">
<GeometryDrawing.Pen>
<Pen Thickness="10" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
<GeometryDrawing Brush="Lime">
<GeometryDrawing.Geometry>
<EllipseGeometry Center="10,10" RadiusX="5" RadiusY="5" />
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="2" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
<DrawingGroup.OpacityMask>
<!-- The opacity mask. -->
<RadialGradientBrush>

MCT: Luis Dueas

Pag 428 de 473

Manual de Windows Presentation Foundation


<GradientStop Offset="0.0" Color="#FF000000" />
<GradientStop Offset="0.55" Color="#00000000" />
<GradientStop Offset="0.65" Color="#FF000000" />
<GradientStop Offset="0.75" Color="#00000000" />
<GradientStop Offset="0.80" Color="#FF000000" />
<GradientStop Offset="0.90" Color="#00000000" />
<GradientStop Offset="1.0" Color="#FF000000" />
</RadialGradientBrush>
</DrawingGroup.OpacityMask>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Border>
</Page>

7.3.3.2.4. Cmo: Aplicar una Transformacin a un Dibujo


En este ejemplo se muestra cmo aplicar una propiedad Transform a un objeto Drawing. Para transformar un
objeto Drawing, debe agregarlo a un objeto DrawingGroup y establecer la propiedad Transform del objeto
DrawingGroup.
La clase DrawingGroup es el nico tipo de objeto Drawing que admite transformaciones. Para aplicar varias
transformaciones a un mismo objeto DrawingGroup, utilice una clase TransformGroup.
Ejemplo
En el ejemplo siguiente se utiliza DrawingGroup para combinar varios objetos GeometryDrawing y, a
continuacin, transformarlos mediante RotateTransform.
La ilustracin muestra el objeto DrawingGroup antes y despus de aplicar RotateTransform.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace SDKSample
{
public class TransformExample : Page
{
public TransformExample()
{
// Create a GeometryDrawing. Define the drawing's contents.
PathFigure pLineFigure = new PathFigure();
pLineFigure.StartPoint = new Point(25, 25);
PolyLineSegment pLineSegment = new PolyLineSegment();
pLineSegment.Points.Add(new Point(0, 50));
pLineSegment.Points.Add(new Point(25, 75));
pLineSegment.Points.Add(new Point(50, 50));
pLineSegment.Points.Add(new Point(25, 25));
pLineSegment.Points.Add(new Point(25, 0));
pLineFigure.Segments.Add(pLineSegment);
PathGeometry pGeometry = new PathGeometry();
pGeometry.Figures.Add(pLineFigure);
GeometryDrawing drawing1 = new GeometryDrawing(Brushes.Lime,

MCT: Luis Dueas

Pag 429 de 473

Manual de Windows Presentation Foundation


new Pen(Brushes.Black, 10), pGeometry);
// Create another GeometryDrawing.
GeometryDrawing drawing2 = new GeometryDrawing(Brushes.Lime,
new Pen(Brushes.Black, 2), new EllipseGeometry(new Point(10,10),5,5));
// Create the DrawingGroup and add the geometry drawings.
DrawingGroup aDrawingGroup = new DrawingGroup();
aDrawingGroup.Children.Add(drawing1);
aDrawingGroup.Children.Add(drawing2);
// Create a RotateTransform and apply it to the drawing group.
RotateTransform rotation = new RotateTransform(45, 50, 75);
aDrawingGroup.Transform = rotation;
// Use an Image control and a DrawingImage to display the drawing.
DrawingImage aDrawingImage = new DrawingImage(aDrawingGroup);
// Freeze the DrawingImage for performance benefits.
aDrawingImage.Freeze();
Image anImage = new Image();
anImage.Source = aDrawingImage;
anImage.Stretch = Stretch.None;
anImage.HorizontalAlignment = HorizontalAlignment.Left;
// Create a border around the images and add it to the page.
Border imageBorder = new Border();
imageBorder.BorderBrush = Brushes.Gray;
imageBorder.BorderThickness = new Thickness(1);
imageBorder.VerticalAlignment = VerticalAlignment.Top;
imageBorder.HorizontalAlignment = HorizontalAlignment.Left;
imageBorder.Margin = new Thickness(20);
imageBorder.Child = anImage;
this.Background = Brushes.White;
this.Margin = new Thickness(20);
this.Content = imageBorder;
}
}
}
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PresentationOption="http://schemas.microsoft.com/winfx/2006/xaml/presentation/option"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions" Background="White" Margin="20">
<Border BorderBrush="Gray" BorderThickness="1" HorizontalAlignment="Left"
VerticalAlignment="Top" Margin="20">
<Image Stretch="None" HorizontalAlignment="Left">
<Image.Source>
<DrawingImage PresentationOptions:Freeze="True">
<DrawingImage.Drawing>
<!-- A drawing group with a RotateTransform. -->
<DrawingGroup>
<GeometryDrawing Brush="Lime" Geometry="M 25,25 L 0,50 25,75 50,50 25,25 25,0">
<GeometryDrawing.Pen>
<Pen Thickness="10" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
<GeometryDrawing Brush="Lime">
<GeometryDrawing.Geometry>
<EllipseGeometry Center="10,10" RadiusX="5" RadiusY="5" />
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="2" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
<!-- Rotate the drawing 45 degrees about (50,75). -->

MCT: Luis Dueas

Pag 430 de 473

Manual de Windows Presentation Foundation


<DrawingGroup.Transform>
<RotateTransform CenterX="50" CenterY="75" Angle="45" />
</DrawingGroup.Transform>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Border>
</Page>

7.3.3.2.5. Cmo: Recortar un Dibujo


En este ejemplo se muestra cmo definir una regin de recorte para un objeto Drawing.
Utilice una clase DrawingGroup para definir un clip de Drawing. La clase DrawingGroup es el nico tipo de
objeto Drawing que permite definir su propia regin Clip.
Utilice una clase Geometry para describir el clip y aplicarlo a la propiedad ClipGeometry del objeto
DrawingGroup.
Ejemplo
En la ilustracin se muestra DrawingGroup antes y despus de aplicar un clip elptico.

En el ejemplo siguiente se utiliza DrawingGroup para aplicar una propiedad ClipGeometry a varios objetos
GeometryDrawing.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PresentationOption="http://schemas.microsoft.com/winfx/2006/xaml/presentation/option"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions" Background="White" Margin="20">
<Border BorderBrush="Gray" BorderThickness="1" HorizontalAlignment="Left"
VerticalAlignment="Top" Margin="20">
<Image Stretch="None" HorizontalAlignment="Left">
<Image.Source>
<DrawingImage PresentationOptions:Freeze="True">
<DrawingImage.Drawing>
<!-- A DrawingGeometry with an elliptical clip region. -->
<DrawingGroup>
<GeometryDrawing Brush="Pink">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,50,85" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="Lime" Geometry="M 25,25 L 0,50 25,75 50,50 25,25 25,0">
<GeometryDrawing.Pen>
<Pen Thickness="10" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
<GeometryDrawing Brush="Lime">
<GeometryDrawing.Geometry>
<EllipseGeometry Center="10,10" RadiusX="5" RadiusY="5" />
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="2" Brush="Black" />
</GeometryDrawing.Pen>

MCT: Luis Dueas

Pag 431 de 473

Manual de Windows Presentation Foundation


</GeometryDrawing>
<DrawingGroup.ClipGeometry>
<EllipseGeometry Center="25,50" RadiusX="25" RadiusY="50" />
</DrawingGroup.ClipGeometry>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Border>
</Page>

7.3.3.2.6. Cmo: Controlar la Opacidad de un Dibujo


En este ejemplo se muestra cmo modificar la opacidad de un objeto Drawing. La clase DrawingGroup es el
nico tipo de objeto Drawing que tiene una propiedad Opacity.
Para cambiar la opacidad de un objeto Drawing, debe agregarlo a un objeto DrawingGroup y establecer la
propiedad Opacity del objeto DrawingGroup.
El valor Opacity del objeto DrawingGroup se multiplica por la opacidad de sus dibujos secundarios; por ejemplo,
si un objeto DrawingGroup tiene un valor de 0.5 para Opacity y contiene un elemento GeometryDrawing que
tiene un valor de opacidad del 50 por ciento para Brush, el pincel tendr una opacidad del 25 por ciento (0.5 *
0.5).
Para modificar la opacidad de determinadas partes de un dibujo, use OpacityMask.
Ejemplo
En el ejemplo siguiente se usa DrawingGroup para combinar varios objetos GeometryDrawing. En l tambin se
establece la opacidad del objeto DrawingGroup en 0.25, para que los dibujos tengan una opacidad del 25 por
ciento.
En esta ilustracin se muestra el objeto DrawingGroup antes y despus de establecer en 0.25 su propiedad
Opacity.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace SDKSample
{
public class OpacityExample : Page
{
public OpacityExample()
{
// Create a GeometryDrawing. Define the drawing's contents.
PathFigure pLineFigure = new PathFigure();
pLineFigure.StartPoint = new Point(25, 25);
PolyLineSegment pLineSegment = new PolyLineSegment();
pLineSegment.Points.Add(new Point(0, 50));
pLineSegment.Points.Add(new Point(25, 75));
pLineSegment.Points.Add(new Point(50, 50));
pLineSegment.Points.Add(new Point(25, 25));
pLineSegment.Points.Add(new Point(25, 0));

MCT: Luis Dueas

Pag 432 de 473

Manual de Windows Presentation Foundation


pLineFigure.Segments.Add(pLineSegment);
PathGeometry pGeometry = new PathGeometry();
pGeometry.Figures.Add(pLineFigure);
GeometryDrawing drawing1 = new GeometryDrawing(Brushes.Lime,
new Pen(Brushes.Black, 10), pGeometry);
// Create another GeometryDrawing.
GeometryDrawing drawing2 = new GeometryDrawing(Brushes.Lime,
new Pen(Brushes.Black, 2), new EllipseGeometry(new Point(10, 10), 5, 5));
// Create the DrawingGroup and add the geometry drawings.
DrawingGroup aDrawingGroup = new DrawingGroup();
aDrawingGroup.Children.Add(drawing1);
aDrawingGroup.Children.Add(drawing2);
// Set the opacity of the DrawingGroup to 0.25.
aDrawingGroup.Opacity = 0.25;
// Use an Image control and a DrawingImage to display the drawing.
DrawingImage aDrawingImage = new DrawingImage(aDrawingGroup);
// Freeze the DrawingImage for performance benefits.
aDrawingImage.Freeze();
Image anImage = new Image();
anImage.Source = aDrawingImage;
anImage.Stretch = Stretch.None;
anImage.HorizontalAlignment = HorizontalAlignment.Left;
// Create a border around the images and add it to the page.
Border imageBorder = new Border();
imageBorder.BorderBrush = Brushes.Gray;
imageBorder.BorderThickness = new Thickness(1);
imageBorder.VerticalAlignment = VerticalAlignment.Top;
imageBorder.HorizontalAlignment = HorizontalAlignment.Left;
imageBorder.Margin = new Thickness(20);
imageBorder.Child = anImage;
this.Background = Brushes.White;
this.Margin = new Thickness(20);
this.Content = imageBorder;
}
}
}
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PresentationOption="http://schemas.microsoft.com/winfx/2006/xaml/presentation/option"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions" Background="White" Margin="20">
<Border BorderBrush="Gray" BorderThickness="1" HorizontalAlignment="Left"
VerticalAlignment="Top" Margin="20">
<Image Stretch="None">
<Image.Source>
<DrawingImage PresentationOptions:Freeze="True">
<DrawingImage.Drawing>
<!-- The drawing group, with an Opacity of 0.25. -->
<DrawingGroup Opacity="0.25">
<GeometryDrawing Brush="Lime" Geometry="M 25,25 L 0,50 25,75 50,50 25,25 25,0">
<GeometryDrawing.Pen>
<Pen Thickness="10" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
<GeometryDrawing Brush="Lime">
<GeometryDrawing.Geometry>
<EllipseGeometry Center="10,10" RadiusX="5" RadiusY="5" />
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="2" Brush="Black" />

MCT: Luis Dueas

Pag 433 de 473

Manual de Windows Presentation Foundation


</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Border>
</Page>

7.3.3.2.7. Cmo: Crear un Dibujo Compuesto


En este ejemplo se muestra cmo utilizar un objeto DrawingGroup para crear dibujos complejos combinando
varios objetos Drawing en un mismo dibujo compuesto.
Ejemplo
En el ejemplo siguiente se utiliza DrawingGroup para crear un dibujo compuesto a partir de los objetos
GeometryDrawing y ImageDrawing. En la ilustracin siguiente se muestra el resultado de aplicar este ejemplo.
Dibujo compuesto creado mediante DrawingGroup

Observe el borde gris, que muestra los lmites del dibujo.


// Create three drawings.
GeometryDrawing ellipseDrawing = new GeometryDrawing(
new SolidColorBrush(Color.FromArgb(102, 181, 243, 20)), new Pen(Brushes.Black, 4),
new EllipseGeometry(new Point(50,50), 50, 50));
ImageDrawing kiwiPictureDrawing = new ImageDrawing(
new BitmapImage(new Uri(@"sampleImages\kiwi.png", UriKind.Relative)),
new Rect(50,50,100,100));
GeometryDrawing ellipseDrawing2 = new GeometryDrawing(
new SolidColorBrush(Color.FromArgb(102,181,243,20)), new Pen(Brushes.Black, 4),
new EllipseGeometry(new Point(150, 150), 50, 50));
// Create a DrawingGroup to contain the drawings.
DrawingGroup aDrawingGroup = new DrawingGroup();
aDrawingGroup.Children.Add(ellipseDrawing);
aDrawingGroup.Children.Add(kiwiPictureDrawing);
aDrawingGroup.Children.Add(ellipseDrawing2);
<DrawingGroup>
<GeometryDrawing Brush="#66B5F314">
<GeometryDrawing.Geometry>
<EllipseGeometry Center="50,50" RadiusX="50" RadiusY="50"/>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness="4" />
</GeometryDrawing.Pen>
</GeometryDrawing>
<ImageDrawing ImageSource="sampleImages\kiwi.png" Rect="50,50,100,100"/>
<GeometryDrawing Brush="#66B5F314">
<GeometryDrawing.Geometry>
<EllipseGeometry Center="150,150" RadiusX="50" RadiusY="50"/>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness="4" />
</GeometryDrawing.Pen>

MCT: Luis Dueas

Pag 434 de 473

Manual de Windows Presentation Foundation


</GeometryDrawing>
</DrawingGroup>
Puede utilizar DrawingGroup para aplicar un valor de Transform, Opacity, OpacityMask, BitmapEffect,
ClipGeometry o GuidelineSet a los dibujos que contiene. Dado que DrawingGroup tambin es un objeto
Drawing, puede contener otros objetos DrawingGroup.
El ejemplo siguiente es similar al anterior, pero utiliza objetos DrawingGroup adicionales para aplicar efectos de
mapa de bits y una mscara de opacidad a algunos de sus dibujos. En la ilustracin siguiente se muestra el
resultado de aplicar este ejemplo.
Dibujo compuesto que tiene varios objetos DrawingGroup

Observe el borde gris, que muestra los lmites del dibujo.


// Create a DrawingGroup.
DrawingGroup mainGroup = new DrawingGroup();
// Create a GeometryDrawing
GeometryDrawing ellipseDrawing = new GeometryDrawing(
new SolidColorBrush(Color.FromArgb(102, 181, 243, 20)),
new Pen(Brushes.Black, 4), new EllipseGeometry(new Point(50, 50), 50, 50));
// Use a DrawingGroup to apply a blur bitmap effect to the drawing.
DrawingGroup blurGroup = new DrawingGroup();
blurGroup.Children.Add(ellipseDrawing);
BlurBitmapEffect blurEffect = new BlurBitmapEffect();
blurEffect.Radius = 5;
blurGroup.BitmapEffect = blurEffect;
// Add the DrawingGroup to the main DrawingGroup.
mainGroup.Children.Add(blurGroup);
// Create an ImageDrawing.
ImageDrawing kiwiPictureDrawing = new ImageDrawing(new BitmapImage(new
Uri(@"sampleImages\kiwi.png", UriKind.Relative)), new Rect(50, 50, 100, 100));
// Use a DrawingGroup to apply an opacity mask and a bevel.
DrawingGroup maskedAndBeveledGroup = new DrawingGroup();
maskedAndBeveledGroup.Children.Add(kiwiPictureDrawing);
// Create an opacity mask.
RadialGradientBrush rgBrush =new RadialGradientBrush();
rgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0,0,0,0), 0.55));
rgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(255,0,0,0), 0.65));
rgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0,0,0,0), 0.75));
rgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(255,0,0,0), 0.80));
rgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0,0,0,0), 0.90));
rgBrush.GradientStops.Add(new GradientStop(Color.FromArgb(255,0,0,0), 1.0));
maskedAndBeveledGroup.OpacityMask = rgBrush;
// Apply a bevel.
maskedAndBeveledGroup.BitmapEffect = new BevelBitmapEffect();
// Add the DrawingGroup to the main group.
mainGroup.Children.Add(maskedAndBeveledGroup);
// Create another GeometryDrawing.
GeometryDrawing ellipseDrawing2 = new GeometryDrawing(
new SolidColorBrush(Color.FromArgb(102, 181, 243, 20)),
new Pen(Brushes.Black, 4), new EllipseGeometry(new Point(150, 150), 50, 50));
// Add the DrawingGroup to the main group.
mainGroup.Children.Add(ellipseDrawing2);
<DrawingGroup>
<DrawingGroup>

MCT: Luis Dueas

Pag 435 de 473

Manual de Windows Presentation Foundation


<GeometryDrawing Brush="#66B5F314">
<GeometryDrawing.Geometry>
<EllipseGeometry Center="50,50" RadiusX="50" RadiusY="50"/>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness="4" />
</GeometryDrawing.Pen>
</GeometryDrawing>
<DrawingGroup.BitmapEffect>
<BlurBitmapEffect Radius="5" />
</DrawingGroup.BitmapEffect>
</DrawingGroup>
<DrawingGroup>
<ImageDrawing ImageSource="sampleImages\kiwi.png" Rect="50,50,100,100"/>
<DrawingGroup.BitmapEffect>
<BevelBitmapEffect />
</DrawingGroup.BitmapEffect>
<DrawingGroup.OpacityMask>
<RadialGradientBrush>
<GradientStop Offset="0.55" Color="#00000000" />
<GradientStop Offset="0.65" Color="#FF000000" />
<GradientStop Offset="0.75" Color="#00000000" />
<GradientStop Offset="0.80" Color="#FF000000" />
<GradientStop Offset="0.90" Color="#00000000" />
<GradientStop Offset="1.0" Color="#FF000000" />
</RadialGradientBrush>
</DrawingGroup.OpacityMask>
</DrawingGroup>
<GeometryDrawing Brush="#66B5F314">
<GeometryDrawing.Geometry>
<EllipseGeometry Center="150,150" RadiusX="50" RadiusY="50"/>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness="4" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup>

7.3.3.2.8. Cmo: Crear un Objeto GeometryDrawing


En este ejemplo se muestra cmo crear y mostrar un objeto GeometryDrawing. GeometryDrawing permite
crear la forma con un relleno y un contorno asociando un objeto Pen y Brush a un objeto Geometry. Geometry
describe la estructura de la forma, Brush describe el relleno de la forma y Pen describe el contorno de la forma.
Ejemplo
En el ejemplo siguiente se utiliza GeometryDrawing para representar una forma. La forma se describe mediante
un

objeto

GeometryGroup

dos

objetos

EllipseGeometry.

El

interior

de

la

forma

se

pinta

con

LinearGradientBrush y su contorno se dibuja con un objeto Pen con la propiedad Black. GeometryDrawing se
muestra utilizando ImageDrawing y un elemento Image.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace SDKSample
{
public class GeometryDrawingExample : Page
{
public GeometryDrawingExample()
{

MCT: Luis Dueas

Pag 436 de 473

Manual de Windows Presentation Foundation


// Create the Geometry to draw.
GeometryGroup ellipses = new GeometryGroup();
ellipses.Children.Add(new EllipseGeometry(new Point(50,50), 45, 20));
ellipses.Children.Add(new EllipseGeometry(new Point(50, 50), 20, 45));
// Create a GeometryDrawing.
GeometryDrawing aGeometryDrawing = new GeometryDrawing();
aGeometryDrawing.Geometry = ellipses;
// Paint the drawing with a gradient.
aGeometryDrawing.Brush = new LinearGradientBrush(Colors.Blue,
Color.FromRgb(204,204,255), new Point(0,0), new Point(1,1));
// Outline the drawing with a solid color.
aGeometryDrawing.Pen = new Pen(Brushes.Black, 10);
// Use a DrawingImage and an Image control to display the drawing.
DrawingImage geometryImage = new DrawingImage(aGeometryDrawing);
// Freeze the DrawingImage for performance benefits.
geometryImage.Freeze();
Image anImage = new Image();
anImage.Source = geometryImage;
anImage.Stretch = Stretch.None;
anImage.HorizontalAlignment = HorizontalAlignment.Left;
// Place the image inside a border and add it to the page.
Border exampleBorder = new Border();
exampleBorder.Child = anImage;
exampleBorder.BorderBrush = Brushes.Gray;
exampleBorder.BorderThickness = new Thickness(1);
exampleBorder.HorizontalAlignment = HorizontalAlignment.Left;
exampleBorder.VerticalAlignment = VerticalAlignment.Top;
exampleBorder.Margin = new Thickness(10);
this.Margin = new Thickness(20);
this.Background = Brushes.White;
this.Content = exampleBorder;
}
}
}
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PresentationOption="http://schemas.microsoft.com/winfx/2006/xaml/presentation/option"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions" Margin="20" Background="White">
<Border BorderBrush="Gray" BorderThickness="1" HorizontalAlignment="Left"
VerticalAlignment="Top" Margin="10">
<Image Stretch="None" HorizontalAlignment="Left">
<Image.Source>
<DrawingImage PresentationOptions:Freeze="True">
<DrawingImage.Drawing>
<GeometryDrawing>
<GeometryDrawing.Geometry>
<!-- Create a composite shape. -->
<GeometryGroup>
<EllipseGeometry Center="50,50" RadiusX="45" RadiusY="20" />
<EllipseGeometry Center="50,50" RadiusX="20" RadiusY="45" />
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Brush>
<!-- Paint the drawing with a gradient. -->
<LinearGradientBrush>
<GradientStop Offset="0.0" Color="Blue" />
<GradientStop Offset="1.0" Color="#CCCCFF" />
</LinearGradientBrush>
</GeometryDrawing.Brush>

MCT: Luis Dueas

Pag 437 de 473

Manual de Windows Presentation Foundation


<GeometryDrawing.Pen>
<!-- Outline the drawing with a solid color. -->
<Pen Thickness="10" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Border>
</Page>
En la siguiente ilustracin se muestra el objeto GeometryDrawing resultante.

Para crear complejos ms dibujos, puede combinar varios objetos de dibujo en un mismo dibujo compuesto
utilizando un DrawingGroup.

7.3.3.2.9. Cmo: Dibujar una Imagen usando un Objeto ImageDrawing


En este ejemplo se muestra cmo utilizar un objeto ImageDrawing para dibujar una imagen. Un objeto
ImageDrawing permite mostrar un objeto ImageSource con un objeto DrawingBrush, DrawingImage o Visual.
Para dibujar una imagen, se crea un objeto ImageDrawing y se establecen sus propiedades ImageDrawing.
ImageSource y ImageDrawing.Rect. La propiedad ImageDrawing.ImageSource especifica la imagen que se va a
dibujar, y la propiedad ImageDrawing.Rect especifica la posicin y el tamao de cada imagen.
Ejemplo
En el ejemplo siguiente se crea un dibujo compuesto mediante cuatro objetos ImageDrawing. Este ejemplo
genera la imagen siguiente.
Cuatro objetos ImageDrawing

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;
namespace SDKSample
{
public class ImageDrawingExample : Page
{
public ImageDrawingExample()
{
// Create a DrawingGroup to combine the ImageDrawing objects.
DrawingGroup imageDrawings = new DrawingGroup();
// Create a 100 by 100 image with an upper-left point of (75,75).
ImageDrawing bigKiwi = new ImageDrawing();
bigKiwi.Rect = new Rect(75, 75, 100, 100);
bigKiwi.ImageSource = new BitmapImage(new Uri(@"sampleImages\kiwi.png",
UriKind.Relative));
imageDrawings.Children.Add(bigKiwi);
// Create a 25 by 25 image with an upper-left point of (0,150).

MCT: Luis Dueas

Pag 438 de 473

Manual de Windows Presentation Foundation


ImageDrawing smallKiwi1 = new ImageDrawing();
smallKiwi1.Rect = new Rect(0, 150, 25, 25);
smallKiwi1.ImageSource = new BitmapImage(new Uri(@"sampleImages\kiwi.png",
UriKind.Relative));
imageDrawings.Children.Add(smallKiwi1);
// Create a 25 by 25 image with an upper-left point of (150,0).
ImageDrawing smallKiwi2 = new ImageDrawing();
smallKiwi2.Rect = new Rect(150, 0, 25, 25);
smallKiwi2.ImageSource = new BitmapImage(new Uri(@"sampleImages\kiwi.png",
UriKind.Relative));
imageDrawings.Children.Add(smallKiwi2);
// Create a 75 by 75 image with an upper-left point of (0,0).
ImageDrawing wholeKiwi = new ImageDrawing();
wholeKiwi.Rect = new Rect(0, 0, 75, 75);
wholeKiwi.ImageSource = new BitmapImage(new Uri(@"sampleImages\wholekiwi.png",
UriKind.Relative));
imageDrawings.Children.Add(wholeKiwi);
// Use a DrawingImage and an Image control to display the drawings.
DrawingImage drawingImageSource = new DrawingImage(imageDrawings);
// Freeze the DrawingImage for performance benefits.
drawingImageSource.Freeze();
Image imageControl = new Image();
imageControl.Stretch = Stretch.None;
imageControl.Source = drawingImageSource;
// Create a border to contain the Image control.
Border imageBorder = new Border();
imageBorder.BorderBrush = Brushes.Gray;
imageBorder.BorderThickness = new Thickness(1);
imageBorder.HorizontalAlignment = HorizontalAlignment.Left;
imageBorder.VerticalAlignment = VerticalAlignment.Top;
imageBorder.Margin = new Thickness(20);
imageBorder.Child = imageControl;
this.Background = Brushes.White;
this.Margin = new Thickness(20);
this.Content = imageBorder;
}
}
}
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PresentationOption="http://schemas.microsoft.com/winfx/2006/xaml/presentation/option"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions" Background="White" Margin="20">
<Border BorderBrush="Gray" BorderThickness="1" HorizontalAlignment="Left"
VerticalAlignment="Top" Margin="20">
<Image Stretch="None">
<Image.Source>
<DrawingImage PresentationOptions:Freeze="True">
<DrawingImage.Drawing>
<DrawingGroup>
<!-- The Rect property specifies that the image only fill a 100 by 100
rectangular area. -->
<ImageDrawing Rect="75,75,100,100" ImageSource="sampleImages\kiwi.png"/>
<!-- This image is set to fill a 25 by 25 rectangular area. -->
<ImageDrawing Rect="0,150,25,25" ImageSource="sampleImages\kiwi.png"/>
<!-- This image is set to fill a 25 by 25 rectangular area. -->
<ImageDrawing Rect="150,0,25,25" ImageSource="sampleImages\kiwi.png"/>
<!-- This image is set to fill a 75 by 75 rectangular area. -->
<ImageDrawing Rect="0,0,75,75" ImageSource="sampleImages\wholekiwi.png"/>
</DrawingGroup>
</DrawingImage.Drawing>

MCT: Luis Dueas

Pag 439 de 473

Manual de Windows Presentation Foundation


</DrawingImage>
</Image.Source>
</Image>
</Border>
</Page>

7.3.3.2.10. Cmo: Reproducir Elementos Multimedia con un Objeto


VideoDrawing
Para reproducir un archivo de audio o vdeo, se utiliza un objeto VideoDrawing y un objeto MediaPlayer. Hay
dos maneras de cargar y reproducir elementos multimedia. La primera es utilizar MediaPlayer y VideoDrawing
por s solos; la segunda consiste en crear su propio objeto MediaTimeline para utilizarlo con MediaPlayer y
VideoDrawing.
Nota:
Al distribuir los objetos multimedia con la aplicacin, no se puede usar ningn archivo multimedia como
recurso del proyecto, como se hara con una imagen. En lugar de ello, en el archivo del proyecto debe
establecer en Content el tipo de medios y establecer CopyToOutputDirectory en PreserveNewest o Always.
Ejemplo
En el ejemplo siguiente se utilizan los objetos VideoDrawing y MediaPlayer para reproducir una vez un archivo
de vdeo.
// Create a VideoDrawing.
MediaPlayer player = new MediaPlayer();
player.Open(new Uri(@"sampleMedia\xbox.wmv", UriKind.Relative));
VideoDrawing aVideoDrawing = new VideoDrawing();
aVideoDrawing.Rect = new Rect(0, 0, 100, 100);
aVideoDrawing.Player = player;
// Play the video once.
player.Play();
Para controlar mejor el tiempo en los elementos multimedia, utilice un objeto MediaTimeline con los objetos
MediaPlayer y VideoDrawing. MediaTimeline permite especificar si el vdeo se debe repetir.
En el ejemplo siguiente se utiliza MediaTimeline con los objetos MediaPlayer y VideoDrawing para reproducir
repetidamente un vdeo.
// Create a VideoDrawing that repeats.
// Create a MediaTimeline.
MediaTimeline mTimeline = new MediaTimeline(new Uri(@"sampleMedia\xbox.wmv",
UriKind.Relative));
// Set the timeline to repeat.
mTimeline.RepeatBehavior = RepeatBehavior.Forever;
// Create a clock from the MediaTimeline.
MediaClock mClock = mTimeline.CreateClock();
MediaPlayer repeatingVideoDrawingPlayer = new MediaPlayer();
repeatingVideoDrawingPlayer.Clock = mClock;
VideoDrawing repeatingVideoDrawing = new VideoDrawing();
repeatingVideoDrawing.Rect = new Rect(150, 0, 100, 100);
repeatingVideoDrawing.Player = repeatingVideoDrawingPlayer;
Tenga en cuenta que, al utilizar MediaTimeline, se utiliza el objeto ClockController interactivo devuelto de la
propiedad Controller de MediaClock para controlar la reproduccin multimedia, en lugar de los mtodos
interactivos de MediaPlayer.

7.3.3.2.11. Cmo: Usar un Dibujo como el Origen de una Imagen


En este ejemplo se muestra cmo utilizar un objeto Drawing como la propiedad Source de un control Image.
Para mostrar Drawing con un control Image, utilice DrawingImage como propiedad Source del control Image y
establezca la propiedad DrawingImage.Drawing del objeto DrawingImage en el dibujo que desea mostrar.

MCT: Luis Dueas

Pag 440 de 473

Manual de Windows Presentation Foundation


Ejemplo
En el ejemplo siguiente se utiliza un objeto DrawingImage y un control Image para mostrar GeometryDrawing.
Este ejemplo produce el siguiente resultado.
Objeto DrawingImage

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace SDKSample
{
public class DrawingImageExample : Page
{
public DrawingImageExample()
{
// Create the Geometry to draw.
GeometryGroup ellipses = new GeometryGroup();
ellipses.Children.Add(new EllipseGeometry(new Point(50,50), 45, 20));
ellipses.Children.Add(new EllipseGeometry(new Point(50, 50), 20, 45));
// Create a GeometryDrawing.
GeometryDrawing aGeometryDrawing = new GeometryDrawing();
aGeometryDrawing.Geometry = ellipses;
// Paint the drawing with a gradient.
aGeometryDrawing.Brush = new LinearGradientBrush(Colors.Blue,
Color.FromRgb(204,204,255), new Point(0,0), new Point(1,1));
// Outline the drawing with a solid color.
aGeometryDrawing.Pen = new Pen(Brushes.Black, 10);
// Use a DrawingImage and an Image control to display the drawing.
DrawingImage geometryImage = new DrawingImage(aGeometryDrawing);
// Freeze the DrawingImage for performance benefits.
geometryImage.Freeze();
Image anImage = new Image();
anImage.Source = geometryImage;
anImage.HorizontalAlignment = HorizontalAlignment.Left;
// Place the image inside a border and add it to the page.
Border exampleBorder = new Border();
exampleBorder.Child = anImage;
exampleBorder.BorderBrush = Brushes.Gray;
exampleBorder.BorderThickness = new Thickness(1);
exampleBorder.HorizontalAlignment = HorizontalAlignment.Left;
exampleBorder.VerticalAlignment = VerticalAlignment.Top;
exampleBorder.Margin = new Thickness(10);
this.Margin = new Thickness(20);
this.Background = Brushes.White;
this.Content = exampleBorder;
}
}
}
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PresentationOption="http://schemas.microsoft.com/winfx/2006/xaml/presentation/option"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions" Background="White" Margin="20">
<Border BorderBrush="Gray" BorderThickness="1" HorizontalAlignment="Left"

MCT: Luis Dueas

Pag 441 de 473

Manual de Windows Presentation Foundation


VerticalAlignment="Top" Margin="10">
<!-- This image uses a Drawing object for its source. -->
<Image>
<Image.Source>
<DrawingImage PresentationOptions:Freeze="True">
<DrawingImage.Drawing>
<GeometryDrawing>
<GeometryDrawing.Geometry>
<GeometryGroup>
<EllipseGeometry Center="50,50" RadiusX="45" RadiusY="20" />
<EllipseGeometry Center="50,50" RadiusX="20" RadiusY="45" />
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Brush>
<LinearGradientBrush>
<GradientStop Offset="0.0" Color="Blue" />
<GradientStop Offset="1.0" Color="#CCCCFF" />
</LinearGradientBrush>
</GeometryDrawing.Brush>
<GeometryDrawing.Pen>
<Pen Thickness="10" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Border>
</Page>

7.3.4. Geometras
Geometry es una clase verstil, que se utiliza para representar grficos 2D, realizar pruebas de acceso de
objetos y definir zonas de recorte.

7.3.4.1. Sintaxis de Marcado de Trazados


En este tema se describe en detalle el minilenguaje eficaz y complejo que se puede utilizar para especificar
geometras de trazado de un modo ms compacto mediante Lenguaje de marcado de aplicaciones extensible.
Minilenguajes de StreamGeometry y PathFigureCollection
WPF proporciona dos clases que proporcionan minilenguajes para describir trazados geomtricos: Stream
Geometry y PathFigureCollection.

El minilenguaje de StreamGeometry se utiliza para establecer propiedades de tipo Geometry, como la


propiedad Clip de un elemento UIElement, o la propiedad Data de un elemento Path. En el ejemplo
siguiente se utiliza la sintaxis de atributo para crear StreamGeometry.
<Path Stroke="Black" Fill="Gray" Data="M 10,100 C 10,300 300,-200 300,100" />

El minilenguaje de PathFigureCollection se utiliza para establecer la propiedad Figures de un elemento


PathGeometry. En el ejemplo siguiente se utiliza una sintaxis de atributo para crear una coleccin
PathFigureCollection para PathGeometry.
<Path Stroke="Black" Fill="Gray">
<Path.Data>
<PathGeometry Figures="M 10,100 C 10,300 300,-200 300,100" />
</Path.Data>
</Path>

Como puede verse en los ejemplos anteriores, los dos minilenguajes son muy similares. Siempre es posible de
utilizar

un

elemento

PathGeometry

en

cualquier

situacin

en

que

se

pueda utilizar

un

elemento

StreamGeometry; as pues, cul de ellos se debe utilizar? Utilice StreamGeometry cuando no necesite

MCT: Luis Dueas

Pag 442 de 473

Manual de Windows Presentation Foundation


modificar el trazado despus de crearlo; utilice PathGeometry en caso de que exista la necesidad de modificar
el trazado.
Nota sobre el espacio en blanco
Para mayor brevedad, en las secciones de sintaxis que figuran a continuacin se muestra un espacio simple,
pero tambin son aceptables varios espacios en aquellos lugares donde se muestra un espacio simple.
En realidad, no es preciso que dos nmeros estn separados por una coma o espacio en blanco, pero
nicamente se pueden omitir cuando la cadena resultante es inequvoca. Por ejemplo, 2..3 representa en la
realidad dos nmeros: 2. y .3. Igualmente, 2-3 es 2 y -3. Tampoco se necesitan espacios antes ni
despus de los comandos.
Sintaxis
La sintaxis de uso de atributos de Lenguaje de marcado de aplicaciones extensible (XAML) de un objeto
StreamGeometry est compuesta de un valor FillRule opcional y de una o ms descripciones de figuras.
Uso de los atributos de StreamGeometry en XAML
<objetopropiedad="[fillRule] figureDescription[figureDescription]*" ... />
La sintaxis de uso de atributos de Lenguaje de marcado de aplicaciones extensible (XAML) para una coleccin
PathFigureCollection est compuesta de una o ms descripciones de figuras.
Uso de los atributos de PathFigureCollection en XAML
<objetopropiedad="figureDescription[figureDescription]*" ... />
Trmino

Descripcin

fillRule

System.Windows.Media.FillRule
Especifica si StreamGeometry utiliza el valor EvenOdd o Nonzero para la propiedad
FillRule.

F0 especifica la regla de relleno EvenOdd.

F1 especifica la regla de relleno Nonzero.

Si omite este comando, el subtrazado utiliza el comportamiento predeterminado, que


es EvenOdd. Si especifica este comando, debe colocarlo primero.
figureDescription

Una figura compuesta de un comando de movimiento, comandos de dibujo y un


comando de cierre opcional.
moveCommanddrawCommands [closeCommand]

moveCommand

Un comando de movimiento que especifica el punto de inicio de la figura.

drawCommands

Uno o ms comandos de dibujo que describen el contenido de la figura.

closeCommand

Un comando de cierre opcional que cierra la figura.

Comando de movimiento
Especifica el punto de inicio de una nueva figura.
Sintaxis
MMstartPoint O bien mmstartPoint
Trmino

Descripcin

startPoint

System.Windows.Point
El punto de inicio de una nueva figura.

MCT: Luis Dueas

Pag 443 de 473

Manual de Windows Presentation Foundation


Una M mayscula indica que startPoint es un valor absoluto; una m minscula indica que startPoint es un
desplazamiento con respecto al punto anterior, o (0,0) si este ltimo no existe. Si enumera varios puntos
despus del comando de movimiento, se dibuja una lnea por ellos como si se hubiera especificado el comando
de lnea.
Comandos de dibujo
Un comando de dibujo puede estar compuesto de varios comandos de forma. Estn disponibles los comandos
de forma siguientes: lnea, lnea horizontal, lnea vertical, curva Bzier cbica, curva Bzier cuadrtica, curva
Bzier cbica suavizada, curva Bzier cuadrtica suavizada y arco elptico.
Cada comando se escribe con una mayscula o una minscula. Las letras maysculas denotan valores
absolutos y las minsculas, valores relativos: los puntos de control para ese segmento son relativos al punto
final del ejemplo anterior. Cuando se escribe secuencialmente ms de un comando del mismo tipo, no es
necesario escribir el comando para cada entrada; por ejemplo, L 100,200 300,400 es equivalente a L 100,200 L
300,400. En la tabla siguiente se describen los comandos de dibujo (draw) y de movimiento (move).
Comando de lnea
Crea una lnea recta entre el punto actual y el punto final especificado. l 20 30 y L 20,30 son ejemplos de
comandos de lnea (line) vlidos.
Sintaxis
LLendPoint O bien llendPoint
Trmino

Descripcin

endPoint

System.Windows.Point
El punto final de la lnea.

Comando de lnea horizontal


Crea una lnea horizontal entre el punto actual y la coordenada X especificada. H 90 es un ejemplo de un
comando de lnea horizontal vlido.
Sintaxis
H x O bien h x
Trmino

Descripcin

System.Double
Coordenada x del punto final de la lnea.

Comando de lnea vertical


Crea una lnea vertical entre el punto actual y la coordenada Y especificada. v 90 es un ejemplo de un comando
de lnea vertical vlido.
Sintaxis
V y O bien v y
Trmino

Descripcin

System.Double
Coordenada Y del punto final de la lnea.

Comando de curva Bzier cbica


Crea una curva Bzier cbica entre el punto actual y el punto final especificado utilizando los dos puntos de
control especificados (controlPoint1 y controlPoint2). C 100,200 200,400 300,200 es un ejemplo de un comando
de curva vlido.

MCT: Luis Dueas

Pag 444 de 473

Manual de Windows Presentation Foundation

Sintaxis
C controlPoint1 controlPoint2 endPoint O bien c controlPoint1 controlPoint2 endPoint
Trmino

Descripcin

controlPoint1

System.Windows.Point
El primer punto de control de la curva, que determina la tangente inicial de la curva.

controlPoint2

System.Windows.Point
El segundo punto de control de la curva, que determina la tangente final de la curva.

endPoint

System.Windows.Point
Punto en el que se dibuja la curva.

Comando de curva Bzier cuadrtica


Crea una curva Bzier cuadrtica entre el punto actual y el punto final especificado utilizando el punto de
control especificado (controlPoint). q 100,200 300,200 es un ejemplo de un comando de curva Bzier
cuadrtica vlido.
Sintaxis
Q controlPoint endPoint O bien q controlPoint endPoint
Trmino

Descripcin

controlPoint

System.Windows.Point
El primer punto de control de la curva, que determina las tangentes inicial y final de la
curva.

endPoint

System.Windows.Point
Punto en el que se dibuja la curva.

Comando de curva Bzier cbica suavizada


Crea una curva Bzier cbica entre el punto actual y el punto final especificado. Se da por hecho que el primer
punto de control es la reflexin del segundo punto de control del comando anterior en relacin con el punto
actual. Si no hay ningn comando anterior o si el comando anterior no es un comando de curva Bzier cbica o
cbica suavizada, se dar por hecho que el primer punto de control coincide con el punto actual. El segundo
punto de control, el punto de control del final de la curva, se especifica mediante controlPoint2. Por ejemplo, S
100,200 200,300 es un comando de curva Bzier cbica suavizada vlido.
Sintaxis
S controlPoint2 endPoint O bien s controlPoint2 endPoint
Trmino

Descripcin

controlPoint2

System.Windows.Point
El punto de control de la curva, que determina la tangente final de la curva.

endPoint

System.Windows.Point
Punto en el que se dibuja la curva.

Comando de curva Bzier cuadrtica suavizada


Crea una curva Bzier cuadrtica entre el punto actual y el punto final especificado. Se da por hecho que el
punto de control es la reflexin del punto de control del comando anterior en relacin con el punto actual. Si no
hay ningn comando anterior o si el comando anterior no es un comando de curva Bzier cuadrtica o
cuadrtica suavizada, se dar por hecho que el primer punto de control coincide con el punto actual.
Sintaxis

MCT: Luis Dueas

Pag 445 de 473

Manual de Windows Presentation Foundation

T controlPoint endPoint O bien t controlPoint endPoint


Trmino

Descripcin

controlPoint

System.Windows.Point
El punto de control de la curva, que determina el inicio y la tangente de la curva.

endPoint

System.Windows.Point
Punto en el que se dibuja la curva.

Comando de arco elptico


Crea un arco elptico entre el punto actual y el punto final especificado.
Sintaxis
A size rotationAngle isLargeArcFlag sweepDirectionFlag endPoint
O bien
a size rotationAngle isLargeArcFlag sweepDirectionFlag endPoint
Trmino

Descripcin

size

System.Windows.Size
Radio de X e Y del arco.

rotationAngle

System.Double
Rotacin de la elipse, en grados.

isLargeArcFlag

Establezca este valor en 1 si el ngulo del arco debe ser de 180 grados o mayor; de
lo contrario, establzcalo en 0.

sweepDirectionFlag

Establezca este valor en 1 si el arco se dibuja en una direccin angular positiva; de


lo contrario, establzcalo en 0.

endPoint

System.Windows.Point
Punto en el que se dibuja el arco.

Comando de cierre
Finaliza la figura actual y crea una lnea que conecta el punto actual al punto inicial de la figura. Este comando
crea una unin de lnea (esquina) entre el ltimo segmento y el primer segmento de la figura.
Sintaxis
Z O bien Z
Sintaxis de puntos
Describe las coordenadas X e Y de un punto.
Sintaxis
x,y O bien x y
Trmino

Descripcin

System.Double
Coordenada X del punto.

System.Double
Coordenada Y del punto.
Valores especiales

En lugar de un valor numrico estndar, puede utilizar tambin los valores especiales siguientes. Estos valores
distinguen maysculas de minsculas.

MCT: Luis Dueas

Pag 446 de 473

Manual de Windows Presentation Foundation


Infinity
Representa Double.PositiveInfinity.
-Infinity
Representa Double.NegativeInfinity.
NaN
Representa Double.NaN.
Tambin puede utilizar la notacin cientfica. Por ejemplo, +1.e17 es un valor vlido.

7.3.4.2. Informacin General sobre Geometra


En esta informacin general se describe cmo utilizar las clases Windows Presentation Foundation Geometry
para describir formas. En este tema tambin se contrastan las diferencias entre los objetos Geometry y los
elementos Shape.
Qu es una geometra?
La

clase

Geometry

las

clases

que

derivan

de

ella,

como

EllipseGeometry,

PathGeometry

CombinedGeometry, permiten describir la geometra de una forma 2D. Estas descripciones geomtricas tienen
muchos usos, tales como la definicin de una forma para pintarla en la pantalla o la definicin de pruebas de
posicionamiento y de zonas de recorte. Incluso puede utilizar una geometra para definir un trazado de
animacin.
Los objetos Geometry pueden ser simples, tales como rectngulos y crculos, o bien compuestos, creados a
partir de dos o ms objetos de geometra. Se pueden crear geometras ms complejas utilizando las clases
PathGeometry y StreamGeometry, que permiten describir arcos y curvas.
Dado que Geometry es un tipo de Freezable, los objetos Geometry proporcionan varias caractersticas
especiales: pueden declararse como recursos, compartirse entre varios objetos, convertirse en objetos de slo
lectura para mejorar el rendimiento, clonarse y convertirse en seguros para subprocesos.
Comparacin entre geometras y formas
Las clases Geometry y Shape parecen similares porque ambas describen formas 2D (compare EllipseGeometry
y Ellipse, por ejemplo), pero hay diferencias importantes.
En primer lugar, la clase Geometry hereda de la clase Freezable, mientras que la clase Shape hereda de
FrameworkElement. Dado que son elementos, los objetos Shape se pueden representar y participar en el
sistema del diseo, mientras que los objetos Geometry no pueden.
Aunque los objetos Shape se pueden utilizar de un modo ms directo que los objetos Geometry, los objetos
Geometry son ms verstiles. Aunque un objeto Shape se utiliza para representar grficos 2D, un objeto
Geometry se puede utilizar para definir el rea geomtrica para grficos 2D, definir una zona de recorte o
definir un rea para pruebas de posicionamiento, por ejemplo.
Forma del trazado
Una forma (Shape), la clase Path, en realidad utiliza una geometra (Geometry) para describir su contenido.
Estableciendo la propiedad Data de Path con Geometry y estableciendo sus propiedades Fill y Stroke, puede
representar Geometry.
Propiedades comunes que aceptan un objeto de geometra
En las secciones anteriores se ha mencionado que los objetos de geometra se pueden utilizar con otros objetos
para diversos propsitos, tales como dibujar formas, realizar animaciones o recortar. En la tabla siguiente se
muestra una lista con varias clases que tienen propiedades que aceptan un objeto Geometry.
Tipo

MCT: Luis Dueas

Propiedad

Pag 447 de 473

Manual de Windows Presentation Foundation

DoubleAnimationUsingPath

PathGeometry

DrawingGroup

ClipGeometry

GeometryDrawing

Geometry

Path

Data

UIElement

Clip

Tipos de geometra simples


La clase base para todas las geometras es la clase abstracta Geometry. Las clases que se derivan de la clase
Geometry se pueden agrupar a grandes rasgos en tres categoras: geometras simples, geometras de trazado y
geometras compuestas.
Las clases de geometras simples incluyen LineGeometry, RectangleGeometry y EllipseGeometry, y se utilizan
para crear las formas geomtricas bsicas, como lneas, rectngulos y crculos.

Una LineGeometry se define especificando los puntos inicial y final de la lnea.

Una RectangleGeometry se define con una estructura Rect que especifica su posicin relativa, as como
su alto y ancho. Puede crear un rectngulo redondeado estableciendo las propiedades RadiusX y
RadiusY.

Una EllipseGeometry se define mediante un punto central, un radio X y un radio Y. En los ejemplos
siguientes se muestra cmo crear geometras simples con fines de representacin y recorte.

Estas mismas formas, as como otras ms complejas, se pueden crear utilizando una PathGeometry o
combinando objetos de geometra entre s, pero estas clases proporcionan un medio ms sencillo de generar
estas formas geomtricas bsicas.
En el ejemplo siguiente se muestra cmo crear y representar una LineGeometry. Como se ha indicado
previamente, un objeto Geometry no puede dibujarse a s mismo, por lo que en el ejemplo se utiliza una forma
Path para representar la lnea. Dado que una lnea no tiene rea, establecer la propiedad Fill de Path no tendra
ningn efecto; en su lugar, se especifican slo las propiedades Stroke y StrokeThickness. En la siguiente
ilustracin se muestra el resultado del ejemplo.
Objeto LineGeometry dibujado desde (10,20) hasta (100,130)

<Path Stroke="Black" StrokeThickness="1" >


<Path.Data>
<LineGeometry StartPoint="10,20" EndPoint="100,130" />
</Path.Data>
</Path>
LineGeometry myLineGeometry = new LineGeometry();
myLineGeometry.StartPoint = new Point(10,20);
myLineGeometry.EndPoint = new Point(100,130);
Path myPath = new Path();
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
myPath.Data = myLineGeometry;
En el ejemplo siguiente se muestra cmo crear y representar un objeto EllipseGeometry. En los ejemplos se
establece el Center de EllipseGeometry en el punto 50,50 y el radio X y el radio Y se establecen ambos en 50,
lo que da lugar a un crculo con un dimetro de 100. El interior de la elipse se pinta asignando un valor a la

MCT: Luis Dueas

Pag 448 de 473

Manual de Windows Presentation Foundation


propiedad Fill del elemento Path, en este caso, Gold. En la siguiente ilustracin se muestra el resultado del
ejemplo.
Objeto EllipseGeometry dibujado en (50,50)

<Path Fill="Gold" Stroke="Black" StrokeThickness="1">


<Path.Data>
<EllipseGeometry Center="50,50" RadiusX="50" RadiusY="50" />
</Path.Data>
</Path>
EllipseGeometry myEllipseGeometry = new EllipseGeometry();
myEllipseGeometry.Center = new Point(50, 50);
myEllipseGeometry.RadiusX = 50;
myEllipseGeometry.RadiusY = 50;
Path myPath = new Path();
myPath.Fill = Brushes.Gold;
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
myPath.Data = myEllipseGeometry;
En el ejemplo siguiente se muestra cmo crear y representar una RectangleGeometry. Una estructura Rect
define la posicin y las dimensiones del rectngulo. La posicin es 50,50; por su parte, el alto y el ancho son
ambos 25, con lo que se crea un cuadrado. En la siguiente ilustracin se muestra el resultado del ejemplo.
Objeto RectangleGeometry dibujado en 50,50

<Path Fill="LemonChiffon" Stroke="Black" StrokeThickness="1">


<Path.Data>
<RectangleGeometry Rect="50,50,25,25" />
</Path.Data>
</Path>
RectangleGeometry myRectangleGeometry = new RectangleGeometry();
myRectangleGeometry.Rect = new Rect(50,50,25,25);
Path myPath = new Path();
myPath.Fill = Brushes.LemonChiffon;
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
myPath.Data = myRectangleGeometry;
En el ejemplo siguiente se muestra cmo utilizar EllipseGeometry como zona de recorte de una imagen. Un
objeto Image se define con un ancho (Width) de 200 y un alto (Height) de 150. Un objeto EllipseGeometry, con
un valor de radio X (RadiusX) de 100, un valor de radio Y (RadiusY) de 75 y un valor de centro (Center) de
100,75, se establece en la propiedad Clip de la imagen. nicamente se muestra la parte de la imagen que est
dentro del rea de la elipse. En la siguiente ilustracin se muestra el resultado del ejemplo.

MCT: Luis Dueas

Pag 449 de 473

Manual de Windows Presentation Foundation


Objeto EllipseGeometry utilizado para recortar un control de imagen

<Image Source="sampleImages\Waterlilies.jpg" Width="200" Height="150"


HorizontalAlignment="Left">
<Image.Clip>
<EllipseGeometry RadiusX="100" RadiusY="75" Center="100,75"/>
</Image.Clip>
</Image>
// Create the image to clip.
Image myImage = new Image();
Uri imageUri = new Uri(@"C:\\Documents and Settings\\All Users\\Documents\My
Pictures\\Sample Pictures\\Water lilies.jpg", UriKind.Relative);
myImage.Source = new BitmapImage(imageUri);
myImage.Width = 200;
myImage.Height = 150;
myImage.HorizontalAlignment = HorizontalAlignment.Left;
// Use an EllipseGeometry to define the clip region.
EllipseGeometry myEllipseGeometry = new EllipseGeometry();
myEllipseGeometry.Center = new Point(100, 75);
myEllipseGeometry.RadiusX = 100;
myEllipseGeometry.RadiusY = 75;
myImage.Clip = myEllipseGeometry;
Geometras de trazado
La clase PathGeometry y su equivalente ligera, la clase StreamGeometry, proporcionan recursos para describir
varias figuras complejas compuestas de arcos, curvas y lneas.
En el ncleo de PathGeometry hay una coleccin de objetos PathFigure, que se denominan as porque cada
figura describe una forma discreta de PathGeometry. Cada PathFigure, a su vez, est compuesta de uno o
varios objetos PathSegment, cada uno de los cuales describe un segmento de la figura.
Existen muchos tipos de segmentos.
Tipo de segmento

Descripcin

Ejemplo

ArcSegment

Crea un arco elptico entre


dos puntos.

Cmo: Crear un arco elptico.

BezierSegment

Crea una curva Bzier


cbica entre dos puntos.

Cmo: Crear una curva Bzier cbica.

LineSegment

Crea una lnea entre dos


puntos.

Cmo: Crear un segmento de lnea en una


clase PathGeometry

PolyBezierSegment

Crea una serie de curvas


Bzier cbicas.

Consulte la pgina del tipo


PolyBezierSegment.

PolyLineSegment

Crea una serie de lneas.

Consulte la pgina del tipo

MCT: Luis Dueas

Pag 450 de 473

Manual de Windows Presentation Foundation

PolyLineSegment.
PolyQuadraticBezierSegment

Crea una serie de curvas


Bzier cuadrticas.

Consulte la pgina de
PolyQuadraticBezierSegment.

QuadraticBezierSegment

Crea una curva Bzier


cuadrtica.

Cmo: Crear una curva Bzier cuadrtica.

Los segmentos de PathFigure se combinan en una sola forma geomtrica donde el punto final de cada
segmento es el punto inicial del segmento siguiente. La propiedad StartPoint de PathFigure especifica el punto
desde el que se dibuja el primer segmento. Cada segmento posterior comienza en el punto final del segmento
anterior. Por ejemplo, para definir una lnea vertical de 10,50 a 10,150 se establece la propiedad StartPoint en
10,50 y se crea un LineSegment con un valor de 10,150 para la propiedad Point.
En el ejemplo siguiente se crea un objeto PathGeometry simple compuesto de una sola PathFigure con un
LineSegment y se muestra mediante un elemento Path. La propiedad StartPoint del objeto PathFigure se
establece en 10,20 y LineSegment se define con un punto final de 100,130. En la ilustracin siguiente se
muestra el PathGeometry creado por este ejemplo.
Objeto PathGeometry que contiene un solo elemento LineSegment

<Path Stroke="Black" StrokeThickness="1">


<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure StartPoint="10,20">
<PathFigure.Segments>
<LineSegment Point="100,130"/>
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
// Create a figure that describes a line from (10,20) to (100,130).
PathFigure myPathFigure = new PathFigure();
myPathFigure.StartPoint = new Point(10,20);
myPathFigure.Segments.Add(new LineSegment(new Point(100,130),true /* IsStroked */ ));
/// Create a PathGeometry to contain the figure.
PathGeometry myPathGeometry = new PathGeometry();
myPathGeometry.Figures.Add(myPathFigure);
// Display the PathGeometry.
Path myPath = new Path();
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
myPath.Data = myPathGeometry;
Es interesante comparar este ejemplo con el anterior, de LineGeometry. La sintaxis utilizada para un objeto
PathGeometry es mucho ms detallada que la utilizada para un objeto LineGeometry simple y, en este caso,
puede ser ms lgico utilizar la clase LineGeometry; no obstante la sintaxis detallada de PathGeometry permite
utilizar reas geomtricas sumamente intrincadas y complejas.
Puede crear geometras ms complejas utilizando una combinacin de objetos PathSegment.

MCT: Luis Dueas

Pag 451 de 473

Manual de Windows Presentation Foundation


En el ejemplo siguiente se utiliza un BezierSegment, un LineSegment y un ArcSegment para crear una forma.
En el ejemplo, se crea primero una curva Bzier cbica definiendo cuatro puntos: un punto inicial, que es el
punto final del segmento anterior, un punto final (Point3) y dos puntos de control (Point1 y Point2). Los dos
puntos de control de una curva Bzier cbica se comportan como imanes: atraen hacia s los segmentos que,
de otra forma, seran una lnea recta, con lo que se crea una curva. El primer punto de control, Point1, afecta a
la parte inicial de la curva; el segundo punto de control, Point2, afecta a la parte final de la curva.
A continuacin, en el ejemplo se agrega un LineSegment, que se dibuja entre el punto final del BezierSegment
anterior que lo precede y el punto especificado por su propiedad LineSegment.
Luego, en el ejemplo se agrega un ArcSegment, que se dibuja desde el punto final del LineSegment anterior
hasta el punto especificado por su propiedad Point. En el ejemplo se especifican asimismo los radios X e Y
(Size), un ngulo de rotacin (RotationAngle), un marcador que indica la amplitud que deber tener el ngulo
del arco resultante (IsLargeArc), y un valor que indica la direccin en que se dibuja el arco (SweepDirection).
En la ilustracin siguiente se muestra la forma creada por este ejemplo.
Objeto PathGeometry

<Path Stroke="Black" StrokeThickness="1" >


<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure StartPoint="10,50">
<PathFigure.Segments>
<BezierSegment Point1="100,0" Point2="200,200" Point3="300,100"/>
<LineSegment Point="400,100" />
<ArcSegment Size="50,50" RotationAngle="45" IsLargeArc="True"
SweepDirection="Clockwise" Point="200,100"/>
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
// Create a figure.
PathFigure myPathFigure = new PathFigure();
myPathFigure.StartPoint = new Point(10,50);
myPathFigure.Segments.Add(new BezierSegment(new Point(100,0),new Point(200,200),
new Point(300,100),true /* IsStroked */ ));
myPathFigure.Segments.Add(new LineSegment(new Point(400,100),true /* IsStroked */ ));
myPathFigure.Segments.Add(new ArcSegment(new Point(200,100),new Size(50,50),45,
true, /* IsLargeArc */ SweepDirection.Clockwise, true /* IsStroked */ ));
/// Create a PathGeometry to contain the figure.
PathGeometry myPathGeometry = new PathGeometry();
myPathGeometry.Figures.Add(myPathFigure);
// Display the PathGeometry.
Path myPath = new Path();
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
myPath.Data = myPathGeometry;
Se pueden crear geometras an ms complejas utilizando varios objetos PathFigure dentro de un
PathGeometry.

MCT: Luis Dueas

Pag 452 de 473

Manual de Windows Presentation Foundation


En el ejemplo siguiente se crea un objeto PathGeometry con dos objetos PathFigure, cada uno de los cuales
contiene varios objetos PathSegment. Se utilizan la PathFigure del ejemplo anterior y una PathFigure con un
PolyLineSegment y un QuadraticBezierSegment. PolyLineSegment se define con una matriz de puntos y
QuadraticBezierSegment se define con un punto de control y un punto final. En la ilustracin siguiente se
muestra la forma creada por este ejemplo.
Objeto PathGeometry con varias figuras

<Path Stroke="Black" StrokeThickness="1" >


<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure StartPoint="10,50">
<PathFigure.Segments>
<BezierSegment Point1="100,0" Point2="200,200" Point3="300,100"/>
<LineSegment Point="400,100" />
<ArcSegment Size="50,50" RotationAngle="45" IsLargeArc="True"
SweepDirection="Clockwise" Point="200,100"/>
</PathFigure.Segments>
</PathFigure>
<PathFigure StartPoint="10,100">
<PathFigure.Segments>
<PolyLineSegment Points="50,100 50,150" />
<QuadraticBezierSegment Point1="200,200" Point2="300,100"/>
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
PathGeometry myPathGeometry = new PathGeometry();
// Create a figure.
PathFigure pathFigure1 = new PathFigure();
pathFigure1.StartPoint = new Point(10,50);
pathFigure1.Segments.Add(new BezierSegment(new Point(100,0),new Point(200,200),
new Point(300,100),true /* IsStroked */ ));
pathFigure1.Segments.Add(new LineSegment(new Point(400,100),true /* IsStroked */ ));
pathFigure1.Segments.Add(new ArcSegment(new Point(200,100),new Size(50,50),45,
true, /* IsLargeArc */ SweepDirection.Clockwise, true /* IsStroked */ ));
myPathGeometry.Figures.Add(pathFigure1);
// Create another figure.
PathFigure pathFigure2 = new PathFigure();
pathFigure2.StartPoint = new Point(10,100);
Point[] polyLinePointArray = new Point[]{ new Point(50, 100), new Point(50, 150)};
PolyLineSegment myPolyLineSegment = new PolyLineSegment();
myPolyLineSegment.Points = new PointCollection(polyLinePointArray);
pathFigure2.Segments.Add(myPolyLineSegment);
pathFigure2.Segments.Add(new QuadraticBezierSegment(new Point(200,200),new Point(300,100),
true /* IsStroked */ ));
myPathGeometry.Figures.Add(pathFigure2);
// Display the PathGeometry.
Path myPath = new Path();
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
myPath.Data = myPathGeometry;
StreamGeometry

MCT: Luis Dueas

Pag 453 de 473

Manual de Windows Presentation Foundation


Al igual que la clase PathGeometry, StreamGeometry define una forma geomtrica compleja que puede
contener curvas, arcos y lneas. A diferencia de PathGeometry, el contenido de StreamGeometry no admite el
enlace de datos, la animacin ni la modificacin. Utilice un objeto StreamGeometry cuando necesite describir
una geometra compleja pero no desee la sobrecarga que implica la compatibilidad con el enlace de datos, la
animacin o la modificacin. Debido a su eficacia, la clase StreamGeometry es una buena opcin para describir
adornos.
Sintaxis de marcado de trazados
Los tipos PathGeometry y StreamGeometry admiten una sintaxis de atributo de Lenguaje de marcado de
aplicaciones extensible (XAML) mediante una serie especial de comandos de movimiento y de dibujo.
Geometras compuestas
Los objetos de geometras compuestas se pueden crear mediante GeometryGroup, que es un objeto
CombinedGeometry, o llamando al mtodo del objeto Geometry esttico, Combine.

El objeto CombinedGeometry y el mtodo Combine realizan una operacin de tipo Boolean para
combinar el rea definida por dos geometras. Se descartan los objetos Geometry que no tienen rea.
nicamente es posible combinar dos objetos Geometry (aunque estas dos geometras tambin pueden
ser geometras compuestas).

La clase GeometryGroup crea una fusin de los objetos Geometry que contiene sin combinar su rea.
Se puede agregar cualquier nmero de objetos Geometry a un GeometryGroup.

Dado que no realizan una operacin de combinacin, el uso de objetos GeometryGroup proporciona ventajas de
rendimiento con respecto al uso de objetos CombinedGeometry o del mtodo Combine.
Geometras combinadas
En la seccin anterior se menciona que el objeto CombinedGeometry y el mtodo Combine combinan el rea
definida por las geometras que contienen. La enumeracin GeometryCombineMode especifica cmo se
combinan las geometras. Los valores posibles para la propiedad GeometryCombineMode son: Union, Intersect,
Exclude y Xor.
En el ejemplo siguiente, se define una CombinedGeometry con el modo de combinacin Union. Tanto
Geometry1 como Geometry2 se definen como crculos del mismo radio, pero con los centros desplazados en 50.
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<Path.Data>
<!-- Combines two geometries using the union combine mode. -->
<CombinedGeometry GeometryCombineMode="Union">
<CombinedGeometry.Geometry1>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="75,75" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="125,75" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>

En el ejemplo siguiente, se define una CombinedGeometry con el modo de combinacin Xor. Tanto Geometry1
como Geometry2 se definen como crculos del mismo radio, pero con los centros desplazados en 50.
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<Path.Data>

MCT: Luis Dueas

Pag 454 de 473

Manual de Windows Presentation Foundation


<!-- Combines two geometries using the XOR combine mode. -->
<CombinedGeometry GeometryCombineMode="Xor">
<CombinedGeometry.Geometry1>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="75,75" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="125,75" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>

Caractersticas de elementos Freezable


Como hereda de la clase Freezable, la clase Geometry proporciona varias caractersticas especiales: los objetos
Geometry se pueden declarar como Informacin general sobre recursos, se pueden compartir entre varios
objetos, se puede hacer que sean de slo lectura para mejorar el rendimiento, se pueden clonar y se pueden
hacer seguros para subprocesos.
Otras caractersticas de las geometras
La clase Geometry tambin proporciona mtodos de utilidad prcticos, como los siguientes:

GetArea: obtiene el rea de Geometry.


FillContains: determina si la geometra contiene otra Geometry.
StrokeContains: determina si el trazo de una Geometry contiene un punto especificado.

7.3.4.3. Temas Cmo de Objetos de Geometra


En los temas de esta seccin se muestra cmo utilizar los objetos Geometry en las aplicaciones.

7.3.4.3.1. Cmo: Animar una Regin de Recorte


En este ejemplo se muestra cmo animar la zona de recorte (Clip) de un elemento de marco. En el ejemplo
siguiente, se utiliza un elemento EllipseGeometry para definir una zona de recorte elptica para un elemento
Image. Un objeto PointAnimation anima la propiedad Center de la geometra de elipse desde (0, 0) hasta (200,
150). La animacin comienza a reproducirse despus de que se carga la imagen, y se repite indefinidamente.
Ejemplo
<Image Source="sampleImages\Waterlilies.jpg" Width="200" Height="150"
HorizontalAlignment="Left">
<Image.Clip>
<EllipseGeometry x:Name="MyEllipseGeometry1" RadiusX="100" RadiusY="75" Center="100,75"/>
</Image.Clip>
<Image.Triggers>
<EventTrigger RoutedEvent="Image.Loaded">
<BeginStoryboard>
<Storyboard>
<PointAnimation Storyboard.TargetName="MyEllipseGeometry1"
Storyboard.TargetProperty="(EllipseGeometry.Center)" From="0,0" To="200,150"
Duration="0:0:3" RepeatBehavior="Forever" AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>

MCT: Luis Dueas

Pag 455 de 473

Manual de Windows Presentation Foundation

7.3.4.3.2. Cmo: Animar un EllipseGeometry


En este ejemplo se muestra cmo animar un Geometry dentro de un Path. En el ejemplo siguiente se usa
PointAnimation para animar la propiedad Center de EllipseGeometry.
Ejemplo
<!-- EllipseGeometryExample.xaml
This example shows how to animate an EllipseGeometry. -->
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowTitle="EllipseGeometry Animation Example">
<Canvas Height="400" Width="400" Margin="20">
<Path Name="myPathShape" Fill="Blue" Stroke="Black" StrokeThickness="5">
<Path.Data>
<EllipseGeometry x:Name="MyEllipseGeometry" Center="200,200" RadiusX="25" RadiusY="50"/>
</Path.Data>
<Path.Triggers>
<!-- Animates the Center position of the EllipseGeometry. -->
<EventTrigger RoutedEvent="Path.Loaded">
<BeginStoryboard>
<Storyboard>
<PointAnimation Storyboard.TargetName="MyEllipseGeometry"
Storyboard.TargetProperty="Center" From="200,200" To="50,50"
Duration="0:0:5" AutoReverse="true" RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Path.Triggers>
</Path>
</Canvas>
</Page>
' EllipseGeometryExample.cs
' This sample demonstrates how to animate the center
' position of an EllipseGeometry using a PointAnimation.
Imports System
Imports System.Windows
Imports System.Windows.Navigation
Imports System.Windows.Media
Imports System.Windows.Media.Animation
Imports System.Windows.Shapes
Imports System.Windows.Controls
Namespace Microsoft.Samples.Animation.AnimatePathShapeSample
' Create the demonstration.
Public Class PathElementAnimationExample
Inherits Page
Private Dim WithEvents myPath As Path
Private Dim myStoryboard As Storyboard
Public Sub New()
NameScope.SetNameScope(Me, new NameScope())
Me.WindowTitle = "EllipseGeometry Animation Example"
Dim myCanvas As New Canvas()
myCanvas.Width = 400
myCanvas.Height = 400
myCanvas.Margin = New Thickness(20)
' Create a Path object to contain the geometry.
myPath = New Path()
myPath.Fill = Brushes.Blue
myPath.Stroke = Brushes.Black
myPath.StrokeThickness = 5
' Create an EllipseGeometry.

MCT: Luis Dueas

Pag 456 de 473

Manual de Windows Presentation Foundation


Dim myEllipseGeometry As New EllipseGeometry()
myEllipseGeometry.Center = New System.Windows.Point(200, 200)
myEllipseGeometry.RadiusX = 25
myEllipseGeometry.RadiusY = 50
' Register a name for the geometry so that it can be targeted by animations.
Me.RegisterName("MyEllipseGeometry", myEllipseGeometry)
' Add the geometry to the Path.
myPath.Data = myEllipseGeometry
myCanvas.Children.Add(myPath)
Me.Content = myCanvas
' Create a PointAnimation to animate the center of the Ellipse.
Dim myPointAnimation As New PointAnimation()
myPointAnimation.From = New System.Windows.Point(200, 200)
myPointAnimation.To = New System.Windows.Point(50, 50)
myPointAnimation.Duration = _
New Duration(TimeSpan.FromMilliseconds(5000))
myPointAnimation.AutoReverse = True
myPointAnimation.RepeatBehavior = RepeatBehavior.Forever
Storyboard.SetTargetName(myPointAnimation, "MyEllipseGeometry")
Storyboard.SetTargetProperty(myPointAnimation, _
new PropertyPath(EllipseGeometry.CenterProperty))
myStoryboard = New Storyboard()
myStoryboard.Children.Add(myPointAnimation)
End Sub
' Start the animation when the path is loaded.
Private Sub myPath_Loaded(ByVal sender as object, ByVal args as RoutedEventArgs)
Handles myPath.Loaded
myStoryboard.Begin(myPath)
End Sub
End Class
End Namespace

7.3.4.3.3. Cmo: Animar el Tamao de un Objeto ArcSegment


En este ejemplo se muestra cmo animar la propiedad Size de un objeto ArcSegment.
Ejemplo
En el ejemplo siguiente se crea un objeto ArcSegment que anima su propiedad Size cuando se carga en la
pantalla.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Media.Animation;
using System.Windows.Media;
namespace SDKSamples
{
public class SizeAnimationExample : Page
{
public SizeAnimationExample()
{
// Create a NameScope for this page so that Storyboards can be used.
NameScope.SetNameScope(this, new NameScope());
// Create an ArcSegment to define the geometry of the path.
// The Size property of this segment is animated.
ArcSegment myArcSegment = new ArcSegment();
myArcSegment.Size = new Size(90, 80);
myArcSegment.SweepDirection = SweepDirection.Clockwise;
myArcSegment.Point = new Point(500, 200);
// Assign the segment a name so that it can be targeted by a Storyboard.
this.RegisterName("myArcSegment", myArcSegment);
PathSegmentCollection myPathSegmentCollection = new PathSegmentCollection();

MCT: Luis Dueas

Pag 457 de 473

Manual de Windows Presentation Foundation


myPathSegmentCollection.Add(myArcSegment);
// Create a PathFigure to be used for the PathGeometry of myPath.
PathFigure myPathFigure = new PathFigure();
// Set the starting point for the PathFigure specifying that the
// geometry starts at point 100,200.
myPathFigure.StartPoint = new Point(100, 200);
myPathFigure.Segments = myPathSegmentCollection;
PathFigureCollection myPathFigureCollection = new PathFigureCollection();
myPathFigureCollection.Add(myPathFigure);
PathGeometry myPathGeometry = new PathGeometry();
myPathGeometry.Figures = myPathFigureCollection;
// Create a path to draw a geometry with.
Path myPath = new Path();
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
// specify the shape of the path using the path geometry.
myPath.Data = myPathGeometry;
SizeAnimation mySizeAnimation = new SizeAnimation();
mySizeAnimation.Duration = TimeSpan.FromSeconds(2);
// Set the animation to repeat forever.
mySizeAnimation.RepeatBehavior = RepeatBehavior.Forever;
// Set the From and To properties of the animation.
mySizeAnimation.From = new Size(90, 80);
mySizeAnimation.To = new Size(200, 200);
// Set the animation to target Size property of the object named myArcSegment
Storyboard.SetTargetName(mySizeAnimation, "myArcSegment");
Storyboard.SetTargetProperty(mySizeAnimation,
new PropertyPath(ArcSegment.SizeProperty));
// Create a storyboard to apply the animation.
Storyboard ellipseStoryboard = new Storyboard();
ellipseStoryboard.Children.Add(mySizeAnimation);
// Start the storyboard when the Path loads.
myPath.Loaded += delegate(object sender, RoutedEventArgs e)
{
ellipseStoryboard.Begin(this);
};
Canvas containerCanvas = new Canvas();
containerCanvas.Children.Add(myPath);
Content = containerCanvas;
}
}
}
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Canvas HorizontalAlignment="Left" Margin="0" >
<!-- Create an arc on the screen that animates its size when it loads. -->
<Path Stroke="Black" StrokeThickness="2" >
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure StartPoint="100,200">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment x:Name="myArcSegment" Size="90,80"
SweepDirection="Clockwise" Point="500,200" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>

MCT: Luis Dueas

Pag 458 de 473

Manual de Windows Presentation Foundation


</PathGeometry>
</Path.Data>
<Path.Triggers>
<EventTrigger RoutedEvent="Path.Loaded">
<BeginStoryboard Name="myBeginStoryBoard">
<Storyboard>
<!-- Animate the size of the ArcSegment to a width and height of 200. -->
<SizeAnimation Storyboard.TargetName="myArcSegment"
Storyboard.TargetProperty="Size" From="90,80" To="200,200"
Duration="0:0:2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Path.Triggers>
</Path>
</Canvas>
</Page>

7.3.4.3.4. Cmo: Controlar el Relleno de una Forma Compuesta


La propiedad FillRule de un GeometryGroup o una PathGeometry, especifica una "regla" que la forma
compuesta utiliza para determinar si un punto determinado forma parte de la geometra. Existen dos valores
posibles para FillRule: EvenOdd y Nonzero. En las secciones siguientes se describe cmo utilizar estas dos
reglas.
EvenOdd: esta regla determina si un punto est en la regin de relleno; para ello, dibuja un rayo desde ese
punto al infinito en cualquier direccin y cuenta el nmero de segmentos de la trayectoria dentro de la forma
dada que el rayo cruza. Si este nmero es impar, el punto se encuentra dentro de la regin; si es par, el punto
est fuera.
Por ejemplo, el XAML siguiente crea una forma compuesta de una serie de anillos concntricos (destino) con
una propiedad FillRule establecida en EvenOdd.
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<Path.Data>
<GeometryGroup FillRule="EvenOdd">
<EllipseGeometry RadiusX="50" RadiusY="50" Center="75,75" />
<EllipseGeometry RadiusX="70" RadiusY="70" Center="75,75" />
<EllipseGeometry RadiusX="100" RadiusY="100" Center="75,75" />
<EllipseGeometry RadiusX="120" RadiusY="120" Center="75,75" />
</GeometryGroup>
</Path.Data>
</Path>
En la ilustracin siguiente se muestra la forma creada en el ejemplo anterior.

En la ilustracin anterior, observe que no se rellenan el centro ni el tercer anillo. Esto se debe a que el rayo
dibujado desde cualquier punto situado en el interior de cualquiera de esos dos anillos pasa a travs de un
nmero par de segmentos. Vea la ilustracin siguiente:

MCT: Luis Dueas

Pag 459 de 473

Manual de Windows Presentation Foundation


NonZero: esta regla determina si un punto est en la regin de relleno de la trayectoria; para ello, dibuja un
rayo desde ese punto al infinito en cualquier direccin y, a continuacin, examina los lugares donde un
segmento de la forma cruza el rayo. Partiendo de cero, sume una ubicacin cada vez que un segmento cruce el
radio de izquierda a derecha y reste una ubicacin cada vez que un segmento de la trayectoria cruce el radio de
derecha a izquierda. Despus de contar el nmero de veces que cruza, si el resultado es cero, el punto est
fuera de la trayectoria. De lo contrario, est dentro.
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<Path.Data>
<GeometryGroup FillRule="NonZero">
<EllipseGeometry RadiusX="50" RadiusY="50" Center="75,75" />
<EllipseGeometry RadiusX="70" RadiusY="70" Center="75,75" />
<EllipseGeometry RadiusX="100" RadiusY="100" Center="75,75" />
<EllipseGeometry RadiusX="120" RadiusY="120" Center="75,75" />
</GeometryGroup>
</Path.Data>
</Path>
Con el ejemplo anterior, un valor de Nonzero para FillRule proporciona como resultado la ilustracin siguiente:

Como puede ver, se rellenan todos los anillos. Esto se debe a que todos los segmentos estn dibujados en la
misma direccin y, por tanto, un rayo dibujado desde cualquier punto cruzar uno o ms segmentos, y la suma
de las veces que cruza no ser igual a cero. Por ejemplo, en la ilustracin siguiente, las flechas rojas
representan la direccin en que se dibujan los segmentos y la flecha blanca representa un rayo arbitrario
trazado desde un punto situado en el anillo ms profundo. Partiendo de cero, se suma una unidad cada vez que
el rayo cruza un segmento, porque el segmento cruza el radio de izquierda a derecha.

Para ilustrar mejor el comportamiento de la regla Nonzero, se necesita una forma ms compleja con segmentos
trazados en distintas direcciones. En el cdigo XAML que se muestra a continuacin se crea una forma similar a
la del ejemplo anterior, pero con PathGeometry en lugar de con EllipseGeometry, de manera que se crean
cuatro arcos concntricos en lugar de crculos concntricos totalmente cerrados.
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<Path.Data>
<GeometryGroup FillRule="NonZero">
<PathGeometry>
<PathGeometry.Figures>
<!-- Inner Ring -->
<PathFigure StartPoint="10,120">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="50,50" IsLargeArc="True" SweepDirection="CounterClockwise"
Point="25,120" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
<!-- Second Ring -->
<PathFigure StartPoint="10,100">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="70,70" IsLargeArc="True" SweepDirection="CounterClockwise"

MCT: Luis Dueas

Pag 460 de 473

Manual de Windows Presentation Foundation


Point="25,100" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
<!-- Third Ring (Not part of path) -->
<PathFigure StartPoint="10,70">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="100,100" IsLargeArc="True"
SweepDirection="CounterClockwise" Point="25,70" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
<!-- Outer Ring -->
<PathFigure StartPoint="10,300">
<PathFigure.Segments>
<ArcSegment Size="130,130" IsLargeArc="True" SweepDirection="Clockwise"
Point="25,300" />
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</GeometryGroup>
</Path.Data>
</Path>
En la ilustracin siguiente se muestra la forma creada en el ejemplo anterior.

Observe que no se rellena el tercer arco contando desde el centro. En la ilustracin siguiente se muestra por
qu. En la ilustracin, las flechas rojas representan la direccin en que se dibujan los segmentos. Las dos
flechas blancas representan dos rayos arbitrarios que parten desde un punto situado en la regin "no rellena".
Como se puede apreciar en la ilustracin, la suma de los valores de un rayo determinado que cruza los
segmentos que encuentra en su trayectoria, es cero. Como se ha definido anteriormente, una suma de cero
significa que el punto no forma parte de la geometra (no es parte del relleno) mientras que si la suma es
distinta de cero, aunque sea un valor negativo, s forma parte de la geometra.

Nota: para los fines de FillRule, todas las formas se consideran cerradas. Si existe una abertura en un
segmento, dibuje una lnea imaginaria para cerrarlo. En el ejemplo anterior, hay pequeas aberturas en los
anillos. En semejante caso, cabra esperar que un rayo trazado a travs de las aberturas diese un resultado
distinto que uno que se trazase en otra direccin. A continuacin se muestra una ilustracin ampliada de una de
estas aberturas y el "segmento imaginario" (segmento que se dibuja para aplicar FillRule) que lo cierra.

MCT: Luis Dueas

Pag 461 de 473

Manual de Windows Presentation Foundation

7.3.4.3.5. Cmo: Crear una Regin de Recorte


En este ejemplo se muestra cmo definir la zona de recorte (Clip) de un elemento de marco. Para definir un
recorte, utilice una Geometry (por ejemplo, EllipseGeometry) para establecer la propiedad Clip del elemento.
nicamente el rea que se encuentre dentro de la zona de la geometra quedar visible.
Ejemplo
En el ejemplo siguiente se muestra un elemento Image sin una zona de recorte definida. Al no haber ninguna
zona de recorte, se muestra la imagen completa.
<Image Source="sampleImages\Waterlilies.jpg" Width="200" Height="150"
HorizontalAlignment="Left" />
Imagen sin zona de recorte

En el ejemplo siguiente, se crea una imagen idntica pero con una zona de recorte definida. nicamente se
muestra la parte de la imagen que est dentro del rea de EllipseGeometry.
<Image Source="sampleImages\Waterlilies.jpg" Width="200" Height="150"
HorizontalAlignment="Left">
<Image.Clip>
<EllipseGeometry RadiusX="100" RadiusY="75" Center="100,75"/>
</Image.Clip>
</Image>
Imagen con una zona de recorte elptica

7.3.4.3.6. Cmo: Crear una Geometra Combinada


En este ejemplo se muestra cmo combinar geometras. Para combinar dos geometras, utilice un objeto
CombinedGeometry. Establezca sus propiedades Geometry1 y Geometry2 en las dos geometras que desea
combinar y establezca la propiedad GeometryCombineMode, que determina cmo se combinarn entre s las
geometras, en Union, Intersect, Exclude o Xor.
Para crear una geometra compuesta a partir de dos o ms geometras, utilice un objeto GeometryGroup.
Ejemplo
En el ejemplo siguiente, se define un objeto CombinedGeometry con el modo de combinacin de geometra
Exclude. Tanto Geometry1 como Geometry2 se definen como crculos del mismo radio, pero con los centros
desplazados en 50.
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<Path.Data>
<!-- Combines two geometries using the exclude combine mode. -->
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>

MCT: Luis Dueas

Pag 462 de 473

Manual de Windows Presentation Foundation


<EllipseGeometry RadiusX="50" RadiusY="50" Center="75,75" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="125,75" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
Geometra combinada con el valor Exclude

En el marcado siguiente, se define una CombinedGeometry con el modo de combinacin Intersect. Tanto
Geometry1 como Geometry2 se definen como crculos del mismo radio, pero con los centros desplazados en 50.
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<Path.Data>
<!-- Combines two geometries using the intersect combine mode. -->
<CombinedGeometry GeometryCombineMode="Intersect">
<CombinedGeometry.Geometry1>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="75,75" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="125,75" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
Geometra combinada con el valor Intersect

En el marcado siguiente, se define una CombinedGeometry con el modo de combinacin Union. Tanto
Geometry1 como Geometry2 se definen como crculos del mismo radio, pero con los centros desplazados en 50.
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<Path.Data>
<!-- Combines two geometries using the union combine mode. -->
<CombinedGeometry GeometryCombineMode="Union">
<CombinedGeometry.Geometry1>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="75,75" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="125,75" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
Geometra combinada con el valor Union

MCT: Luis Dueas

Pag 463 de 473

Manual de Windows Presentation Foundation


En el marcado siguiente, se define una CombinedGeometry con el modo de combinacin Xor. Tanto Geometry1
como Geometry2 se definen como crculos del mismo radio, pero con los centros desplazados en 50.
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<Path.Data>
<!-- Combines two geometries using the XOR combine mode. -->
<CombinedGeometry GeometryCombineMode="Xor">
<CombinedGeometry.Geometry1>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="75,75" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="125,75" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
Geometra combinada con el valor Xor

7.3.4.3.7. Cmo: Crear una Forma Compuesta


En este ejemplo se muestra cmo crear formas compuestas mediante objetos Geometry y mostrarlas utilizando
un elemento Path. En el ejemplo siguiente, se utilizan LineGeometry, EllipseGeometry y RectangleGeometry con
GeometryGroup para crear una forma compuesta. A continuacin, las geometras se dibujan mediante un
elemento Path.
Ejemplo
<!-- Displays the geometry. -->
<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
<Path.Data>
<!-- Creates a composite shape from three geometries. -->
<GeometryGroup FillRule="EvenOdd">
<LineGeometry StartPoint="10,10" EndPoint="50,30" />
<EllipseGeometry Center="40,70" RadiusX="30" RadiusY="30" />
<RectangleGeometry Rect="30,55 100 30" />
</GeometryGroup>
</Path.Data>
</Path>
// Create a Path to be drawn to the screen.
Path myPath = new Path();
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush.Color = Color.FromArgb(255, 204, 204, 255);
myPath.Fill = mySolidColorBrush;
// Create the line geometry to add to the Path
LineGeometry myLineGeometry = new LineGeometry();
myLineGeometry.StartPoint = new Point(10, 10);
myLineGeometry.EndPoint = new Point(50, 30);
// Create the ellipse geometry to add to the Path
EllipseGeometry myEllipseGeometry = new EllipseGeometry();
myEllipseGeometry.Center = new Point(40, 70);
myEllipseGeometry.RadiusX = 30;
myEllipseGeometry.RadiusY = 30;
// Create a rectangle geometry to add to the Path
RectangleGeometry myRectGeometry = new RectangleGeometry();

MCT: Luis Dueas

Pag 464 de 473

Manual de Windows Presentation Foundation


myRectGeometry.Rect = new Rect(30, 55, 100, 30);
// Add all the geometries to a GeometryGroup.
GeometryGroup myGeometryGroup = new GeometryGroup();
myGeometryGroup.Children.Add(myLineGeometry);
myGeometryGroup.Children.Add(myEllipseGeometry);
myGeometryGroup.Children.Add(myRectGeometry);
myPath.Data = myGeometryGroup;
// Add path shape to the UI.
StackPanel mainPanel = new StackPanel();
mainPanel.Children.Add(myPath);
this.Content = mainPanel;
En la ilustracin siguiente se muestra la forma creada en el ejemplo anterior.
Geometra compuesta

Se pueden crear formas ms complejas, tales como polgonos y formas con segmentos curvados, utilizando un
objeto PathGeometry. Aunque en este ejemplo se representa una forma en la pantalla mediante un elemento
Path, tambin se pueden utilizar objetos Geometry para describir el contenido de GeometryDrawing o
DrawingContext. Adems, se pueden utilizar para el recorte y las pruebas de posicionamiento.

7.3.4.3.8. Cmo: Crear una Curva Bzier Cbica


En este ejemplo se muestra cmo crear una curva Bzier cbica. Para crear una curva Bzier cbica, utilice las
clases PathGeometry, PathFigure y BezierSegment. Para mostrar la geometra resultante, utilice un elemento
Path, o bien utilcelo con GeometryDrawing o DrawingContext. En los ejemplos siguientes, se dibuja una curva
Bzier cbica desde (10,100) hasta (300,100). Los puntos de control de la curva son ((100, 0) y (200, 200).
Ejemplo
En Lenguaje de marcado de aplicaciones extensible (XAML), puede utilizar la sintaxis de marcado abreviada
para describir un trazado.
<Path Stroke="Black" StrokeThickness="1"
Data="M 10,100 C 100,0 200,200 300,100" />
En XAML, puede dibujar tambin una curva Bzier cbica mediante etiquetas de objeto. El cdigo siguiente
equivale al ejemplo de XAML anterior.
<Path Stroke="Black" StrokeThickness="1">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure StartPoint="10,100">
<PathFigure.Segments>
<PathSegmentCollection>
<BezierSegment Point1="100,0" Point2="200,200" Point3="300,100" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>

7.3.4.3.9. Cmo: Crear una Lnea mediante la Clase LineGeometry


En este ejemplo se muestra cmo utilizar la clase LineGeometry para describir una lnea. Sus puntos inicial y
final definen un elemento LineGeometry.

MCT: Luis Dueas

Pag 465 de 473

Manual de Windows Presentation Foundation


Ejemplo
En el ejemplo siguiente se muestra cmo crear y representar una LineGeometry. Se utiliza un elemento Path
para representar la lnea. Puesto que una lnea no tiene rea, no se especifica la propiedad Fill del objeto Path;
en su lugar se utilizan las propiedades Stroke y StrokeThickness.
<Path Stroke="Black" StrokeThickness="1" >
<Path.Data>
<LineGeometry StartPoint="10,20" EndPoint="100,130" />
</Path.Data>
</Path>
LineGeometry myLineGeometry = new LineGeometry();
myLineGeometry.StartPoint = new Point(10,20);
myLineGeometry.EndPoint = new Point(100,130);
Path myPath = new Path();
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
myPath.Data = myLineGeometry;
Objeto LineGeometry dibujado desde (10,20) hasta (100,130)

Otras clases de geometras simples son LineGeometry y EllipseGeometry. Estas geometras, as como otras
complejas, tambin se pueden crear utilizando una clase PathGeometry o StreamGeometry.

7.3.4.3.10. Cmo: Crear un Segmento de Lnea en una Clase PathGeometry


En este ejemplo se muestra cmo crear un segmento de lnea. Para crear un segmento de lnea, se utilizan las
clases PathGeometry, PathFigure y LineSegment.
Ejemplo
En los ejemplos siguientes se dibuja un objeto LineSegment desde (10, 50) hasta (200, 70). En la ilustracin
siguiente se muestra el objeto LineSegment resultante; se agreg un fondo de cuadrcula para mostrar el
sistema de coordenadas.
Objeto LineSegment dibujado de (10,50) a (200,700)
xaml

En Lenguaje de marcado de aplicaciones extensible (XAML), puede utilizar la sintaxis de atributo para describir
un trazado.
<Path Stroke="Black" StrokeThickness="1"

Data="M 10,50 L 200,70" />

(Tenga en cuenta que, en realidad, esta sintaxis de atributo crea un objeto StreamGeometry, que es una
versin ligera de PathGeometry.)
En XAML, tambin puede dibujar un segmento de lnea utilizando la sintaxis de elementos de objeto. El cdigo
siguiente equivale al ejemplo de XAML anterior.
PathFigure myPathFigure = new PathFigure();
myPathFigure.StartPoint = new Point(10, 50);
LineSegment myLineSegment = new LineSegment();
myLineSegment.Point = new Point(200, 70);
PathSegmentCollection myPathSegmentCollection = new PathSegmentCollection();
myPathSegmentCollection.Add(myLineSegment);
myPathFigure.Segments = myPathSegmentCollection;
PathFigureCollection myPathFigureCollection = new PathFigureCollection();
myPathFigureCollection.Add(myPathFigure);

MCT: Luis Dueas

Pag 466 de 473

Manual de Windows Presentation Foundation


PathGeometry myPathGeometry = new PathGeometry();
myPathGeometry.Figures = myPathFigureCollection;
Path myPath = new Path();
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
myPath.Data = myPathGeometry;
<Path Stroke="Black" StrokeThickness="1">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="10,50">
<LineSegment Point="200,70" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>

7.3.4.3.11. Cmo: Crear una Forma mediante una Clase PathGeometry


En este ejemplo se muestra cmo crear una forma mediante la clase PathGeometry. Los objetos PathGeometry
estn compuestos de uno o ms objetos PathFigure; cada PathFigure representa una "figura" o forma diferente.
Cada PathFigure, a su vez, est compuesta de uno o varios objetos PathSegment, cada uno de los cuales
representa una parte conectada de la figura o forma. Los tipos de segmentos incluyen: LineSegment,
ArcSegment y BezierSegment.
Ejemplo
En el ejemplo siguiente se usa un objeto PathGeometry para crear un tringulo. PathGeometry se muestra
mediante un elemento Path.
<Path Stroke="Black" StrokeThickness="1">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure IsClosed="True" StartPoint="10,100">
<PathFigure.Segments>
<PathSegmentCollection>
<LineSegment Point="100,100" />
<LineSegment Point="100,50" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
En la ilustracin siguiente se muestra la forma creada en el ejemplo anterior.
Tringulo creado con un objeto PathGeometry

En el ejemplo anterior se ha mostrado cmo crear una forma relativamente simple, un tringulo. PathGeometry
tambin se puede utilizar para crear formas ms complejas, incluidos arcos y curvas.

MCT: Luis Dueas

Pag 467 de 473

Manual de Windows Presentation Foundation

7.3.4.3.12. Cmo: Crear una Forma utilizando StreamGeometry


StreamGeometry es la alternativa ligera a PathGeometry para la creacin de formas geomtricas. Utilice un
objeto StreamGeometry cuando necesite describir una geometra compleja pero no desee la sobrecarga que
implica la compatibilidad con el enlace de datos, la animacin o la modificacin. Por ejemplo, debido a su
eficacia, la clase StreamGeometry es una buena opcin para describir adornos.
Ejemplo
En el ejemplo siguiente se utiliza la sintaxis de atributo para crear una StreamGeometry triangular en XAML.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Path Data="F0 M10,100 L100,100 100,50Z"
StrokeThickness="1" Stroke="Black"/>
</StackPanel>
</Page>
El ejemplo siguiente utiliza un objeto StreamGeometry para definir un tringulo en el cdigo. En primer lugar,
el ejemplo crea un objeto StreamGeometry, a continuacin obtiene un objeto StreamGeometryContext y lo
utiliza para describir el tringulo.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace SDKSample
{
// Use StreamGeometry with StreamGeometryContext to define a triangle shape.
public partial class StreamGeometryTriangleExample : Page
{
public StreamGeometryTriangleExample()
{
// Create a path to draw a geometry with.
Path myPath = new Path();
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
// Create a StreamGeometry to use to specify myPath.
StreamGeometry geometry = new StreamGeometry();
geometry.FillRule = FillRule.EvenOdd;
// Open a StreamGeometryContext that can be used to describe this
// StreamGeometry object's contents.
using (StreamGeometryContext ctx = geometry.Open())
{
// Begin the triangle at the point specified. Notice that the shape is set
// to be closed so only two lines need to be specified below to make the
// triangle.
ctx.BeginFigure(new Point(10,100),true/* is filled */,true/* is closed */);
// Draw a line to the next specified point.
ctx.LineTo(new Point(100,100),true/* is stroked */,false/* is smooth join */);
// Draw another line to the next specified point.
ctx.LineTo(new Point(100,50),true/* is stroked */,false/* is smooth join */);
}
// Freeze the geometry make it unmodifiable for additional performance benefits
geometry.Freeze();
// Specify the shape (triangle) of the Path using the StreamGeometry.
myPath.Data = geometry;
// Add path shape to the UI.
StackPanel mainPanel = new StackPanel();
mainPanel.Children.Add(myPath);
this.Content = mainPanel;
}
}

MCT: Luis Dueas

Pag 468 de 473

Manual de Windows Presentation Foundation


}
El ejemplo siguiente crea un mtodo que utiliza un objeto StreamGeometry y un objeto StreamGeometry
Context para definir una forma geomtrica basada en los parmetros especificados.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace SDKSample
{
public partial class StreamGeometryExample : Page
{
public StreamGeometryExample()
{
// Create a path to draw a geometry with.
Path myPath = new Path();
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
// Create a StreamGeometry to use to specify myPath.
StreamGeometry theGeometry = BuildRegularPolygon(new Point(200,200),200,8,0);
theGeometry.FillRule = FillRule.EvenOdd;
// Freeze the geometry make it unmodifiable for additional performance benefits
theGeometry.Freeze();
// Use the StreamGeometry returned by the BuildRegularPolygon to
// specify the shape of the path.
myPath.Data = theGeometry;
// Add path shape to the UI.
StackPanel mainPanel = new StackPanel();
mainPanel.Children.Add(myPath);
this.Content = mainPanel;
}
StreamGeometry BuildRegularPolygon(Point c, double r, int numSides, double
offsetDegree)
{
// c is the center, r is the radius, numSides the number of sides, offsetDegree
// the offset in Degrees. Do not add the last point.
StreamGeometry geometry = new StreamGeometry();
using (StreamGeometryContext ctx = geometry.Open())
{
ctx.BeginFigure(new Point(), true /* is filled */, true /* is closed */);
double step = 2 * Math.PI / Math.Max(numSides, 3);
Point cur = c;
double a = Math.PI * offsetDegree / 180.0;
for (int i = 0; i < numSides; i++, a += step)
{
cur.X = c.X + r * Math.Cos(a);
cur.Y = c.Y + r * Math.Sin(a);
ctx.LineTo(cur, true /* is stroked */, false /* is smooth join */);
}
}
return geometry;
}
}
}

7.3.4.3.13. Cmo: Crear una curva Bzier Cuadrtica


En este ejemplo se muestra cmo crear una curva Bzier cuadrtica. Para crear una curva Bzier cuadrtica,
utilice las clases PathGeometry, PathFigure y QuadraticBezierSegment.
Ejemplo

MCT: Luis Dueas

Pag 469 de 473

Manual de Windows Presentation Foundation


En los ejemplos siguientes, se dibuja una curva Bzier cuadrtica desde (10,100) hasta (300,100). El punto de
control de la curva es (200,200).
En Lenguaje de marcado de aplicaciones extensible (XAML), puede utilizar la sintaxis de atributo para describir
un trazado.
<Path Stroke="Black" StrokeThickness="1" Data="M 10,100 Q 200,200 300,100" />
(Tenga en cuenta que, en realidad, esta sintaxis de atributo crea un objeto StreamGeometry, que es una
versin ligera de PathGeometry.)
En XAML, puede dibujar tambin una curva Bzier cuadrtica mediante la sintaxis de elementos de objeto. El
cdigo siguiente equivale al ejemplo de XAML anterior.
<Path Stroke="Black" StrokeThickness="1">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure StartPoint="10,100">
<PathFigure.Segments>
<PathSegmentCollection>
<QuadraticBezierSegment Point1="200,200" Point2="300,100" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>

7.3.4.3.14. Cmo: Crear un Arco Elptico


En este ejemplo se muestra cmo dibujar un arco elptico. Para crear un arco elptico, utilice las clases
PathGeometry, PathFigure y ArcSegment.
Ejemplo
En el ejemplo siguiente, se dibuja un arco elptico desde (10,100) hasta (200,100). Para este arco, el valor de
Size de 100 por 50 pxeles independientes del dispositivo, el valor de RotationAngle es de 45 grados, la
propiedad IsLargeArc se establece en true y la propiedad SweepDirection se establece en Counterclockwise.
En Lenguaje de marcado de aplicaciones extensible (XAML), puede utilizar la sintaxis de atributo para describir
un trazado.
<Path Stroke="Black" StrokeThickness="1"

Data="M 10,100 A 100,50 45 1 0 200,100" />

(Tenga en cuenta que, en realidad, esta sintaxis de atributo crea un objeto StreamGeometry, que es una
versin ligera de PathGeometry.)
En XAML, tambin puede dibujar un arco elptico utilizando explcitamente las etiquetas de objeto. El cdigo
siguiente es equivalente al marcado XAML anterior.
<Path Stroke="Black" StrokeThickness="1">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure StartPoint="10,100">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="100,50" RotationAngle="45" IsLargeArc="True"
SweepDirection="CounterClockwise" Point="200,100" />
</PathSegmentCollection>
</PathFigure.Segments>

MCT: Luis Dueas

Pag 470 de 473

Manual de Windows Presentation Foundation


</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>

7.3.4.3.15. Cmo: Crear Varios Subtrazados en un PathGeometry


Este ejemplo muestra cmo crear varios subtrazados en un PathGeometry. Para crear varios subtrazados, se
crea una figura PathFigure para cada uno de ellos.
Ejemplo
En el ejemplo siguiente se crean dos subtrazados, cada uno es un tringulo.
<Path Stroke="Black" StrokeThickness="1">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure IsClosed="True" StartPoint="10,100">
<PathFigure.Segments>
<PathSegmentCollection>
<LineSegment Point="100,100" />
<LineSegment Point="100,50" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
<PathFigure IsClosed="True" StartPoint="10,10">
<PathFigure.Segments>
<PathSegmentCollection>
<LineSegment Point="100,10" />
<LineSegment Point="100,40" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
En el ejemplo siguiente se muestra cmo crear varios subtrazados mediante Path y la sintaxis de atributo de
XAML. Cada M crea un nuevo subtrazado para que el ejemplo cree dos subtrazados, cada uno de los cuales
dibuja un tringulo.
<Path Stroke="Black" StrokeThickness="1" Data="M 10,100 L 100,100 100,50 Z M 10,10 100,10
100,40 Z" />
(Tenga en cuenta que, en realidad, esta sintaxis de atributo crea un objeto StreamGeometry, que es una
versin ligera de PathGeometry.)

7.3.4.3.16. Cmo: Definir un Rectngulo usando una Clase RectangleGeometry


En este ejemplo se describe cmo utilizar la clase RectangleGeometry para describir un rectngulo.
Ejemplo
En el ejemplo siguiente se muestra cmo crear y representar una RectangleGeometry. Una estructura Rect
define la posicin y las dimensiones relativas del rectngulo. La posicin relativa es 50,50; por su parte, el alto
y el ancho son ambos 25, con lo que se crea un cuadrado. El interior del rectngulo se pinta con un pincel
LemonChiffon y su contorno se pinta con un trazo Black cuyo grosor es 1.
<Path Fill="LemonChiffon" Stroke="Black" StrokeThickness="1">
<Path.Data>

MCT: Luis Dueas

Pag 471 de 473

Manual de Windows Presentation Foundation


<RectangleGeometry Rect="50,50,25,25" />
</Path.Data>
</Path>
RectangleGeometry myRectangleGeometry = new RectangleGeometry();
myRectangleGeometry.Rect = new Rect(50,50,25,25);
Path myPath = new Path();
myPath.Fill = Brushes.LemonChiffon;
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
myPath.Data = myRectangleGeometry;
Objeto RectangleGeometry

Aunque en este ejemplo se utiliz un elemento Path para representar RectangleGeometry, hay muchas otras
maneras de utilizar los objetos RectangleGeometry. Por ejemplo, se puede utilizar RectangleGeometry para
especificar la propiedad Clip de UIElement o la propiedad Geometry de GeometryDrawing.
Otras clases de geometras simples son LineGeometry y EllipseGeometry. Estas geometras, as como otras
complejas, tambin se pueden crear utilizando una clase PathGeometry o StreamGeometry.

7.3.4.3.17. Cmo: Redondear las Esquinas de un RectangleGeometry


Para redondear las esquinas de un objeto RectangleGeometry, establezca las propiedades RadiusX y RadiusY en
un valor mayor que cero. Cuanto mayores sean los valores, ms redondeadas sern las esquinas del
rectngulo.
Ejemplo
En el ejemplo siguiente se muestran varios objetos RectangleGeometry con distintos valores de RadiusX y
RadiusY. Los objetos RectangleGeometry se muestran mediante elementos Path.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="GeoOvwSample.RectangleGeometryRoundedCornerExample">
<Page.Resources>
<!-- Create a grid background to highlight the coordinate system. -->
<DrawingBrush x:Key="GridDrawingBrushResource" Viewport="0,0,10,10"
ViewportUnits="Absolute" TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="White">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,1,1" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Geometry="M0,0 L1,0 1,0.1, 0,0.1Z" Brush="#CCCCFF" />
<GeometryDrawing Geometry="M0,0 L0,1 0.1,1, 0.1,0Z" Brush="#CCCCFF" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
<!-- Create a graph paper style border to frame the rectangles. -->
<Style x:Key="GraphPaperBorderStyle" TargetType="{x:Type Border}">
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Background" Value="{StaticResource GridDrawingBrushResource}" />
<Setter Property="BorderBrush" Value="Black" />

MCT: Luis Dueas

Pag 472 de 473

Manual de Windows Presentation Foundation


<Setter Property="BorderThickness" Value="1" />
<Setter Property="Margin" Value="10" />
<Setter Property="Width" Value="190" />
<Setter Property="Height" Value="90" />
</Style>
</Page.Resources>
<StackPanel Name="MainStackPanel">
<Border Style="{StaticResource GraphPaperBorderStyle}">
<Path Stroke="Black" StrokeThickness="1" Fill="#99CCCCFF">
<Path.Data>
<!-- Create a rectangle without rounded corners. -->
<RectangleGeometry Rect="20,20,150,50" />
</Path.Data>
</Path>
</Border>
<Border Style="{StaticResource GraphPaperBorderStyle}">
<Path Stroke="Black" StrokeThickness="1" Fill="#99CCCCFF">
<Path.Data>
<!-- Create a rectangle with rounded corners by giving the RectangleGeometry a
RadiusX and a RadiusY of 10. -->
<RectangleGeometry Rect="20,20,150,50" RadiusX="10" RadiusY="10" />
</Path.Data>
</Path>
</Border>
<Border Style="{StaticResource GraphPaperBorderStyle}" >
<Path Stroke="Black" StrokeThickness="1" Fill="#99CCCCFF">
<Path.Data>
<!-- Set RadiusX and RadiusY to their maximum values
(half the rectangle's width and half the rectangle's height). -->
<RectangleGeometry Rect="20,20,150,50" RadiusX="75" RadiusY="25" />
</Path.Data>
</Path>
</Border>
</StackPanel>
</Page>
Rectngulos con esquinas redondeadas

MCT: Luis Dueas

Pag 473 de 473

Das könnte Ihnen auch gefallen