Está en la página 1de 150

1/9/2023

UNIDAD I
MICROSOFT WINDOWS
PRESENTATION FOUNDATION

Como funciona XAML


Nomenclatura de elementos XAML
Ventajas de XAML
Configuración del entorno
Escritura XAML
1.9. 1.1. 1.2.
Openfiledialog CONTR XAML Uielement
Savefiledialog
ELEMEN Contenelement
OLES
TOS Dependency Object
DE
BASE
DIÁLOG Eventos enrutados
WPF
O Eventos adjuntos
1.8. 1.3.
TEMPOR EVENT Eventos de vida de objetos
Eventos de vista preliminar

Image
IZADOR
ES

1.7.
WPF OS
WPF
1.4.
Eventos de cambios de
propiedades

Slider CONTR ESTILOS


OLES Y Diccionario de
Scrollbar
AVANZA PLANTIL recursos
Progressbar 1.6.
DOS CONTROLE 15. LAS
Datagrid
S DE MENU CONTROL
Treeview Y BARRAS ES
Listview DE CONTENE
Datepicker HERRAMIE DORES Grid
NTAS Canvas
Stackpanel
TabControl 2

1
1/9/2023

1.1. XAML

Microsoft Windows Presentation Foundation (WPF)


Windows Presentation Foundation (WPF) es un framework de interfaz de usuario
que crea aplicaciones de cliente de escritorio. La plataforma de desarrollo de WPF
admite un amplio conjunto de características de desarrollo de aplicaciones,
incluido un modelo de aplicación, recursos, controles, gráficos, diseños, enlace de
datos, documentos y seguridad. El marco es parte de .NET, por lo que si ya se ha
creado aplicaciones con .NET mediante ASP.NET o Windows Forms, la experiencia
de programación debe resultar familiar. WPF usa el lenguaje de marcado de
aplicaciones extensible (XAML) para proporcionar un modelo declarativo para la
programación de aplicaciones.
En otras palabras, Windows Presentation Foundation (WPF) permite la creación de
aplicaciones de cliente de escritorio para Windows con experiencias de usuario
visualmente impactantes.
4

2
1/9/2023

El núcleo de WPF es un motor de


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

PROGRAMAR CON WPF


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

3
1/9/2023

MARCADO Y CÓDIGO SUBYACENTE


WPF permite desarrollar una aplicación que use tanto marcado como código
subyacente. En general, se usa marcado XAML para implementar la apariencia
de una aplicación y lenguajes de programación administrados (código
subyacente) para implementar su comportamiento. Esta separación entre la
apariencia y el comportamiento tiene las ventajas siguientes:
Se reducen los costes de desarrollo y mantenimiento porque el marcado
específico de la apariencia no está asociado estrechamente al código
específico del comportamiento.
La programación es más eficaz porque los diseñadores pueden implementar
la apariencia de una aplicación al mismo tiempo que los programadores
implementan su comportamiento.
La globalización y localización de las aplicaciones WPF se ha simplificado.

XAML
El significado de XAML proviene del inglés "eXtensible Application Markup Language" o
"Lenguaje de marcado extensible de aplicación". Esta técnología es una variante
provista por Microsoft para describir un GUI. En Frameworks GUI previos, como
WinForms, el GUI era generado en el mismo lenguaje que se utilizaba para interactuar
con el GUI, como por ejemplo C# o VB.NET, y usualmente mantenido por el diseñador
(Generalmente Visual Studio). El enfoque de Microsoft cambió con la implementación de
XAML, siendo mucho mas parecido a código HTML, permitiendo editar y escribir la GUI
fácilmente.
XAML es una parte esencial de WPF. Sin importar lo que se crea, una Ventana o una
Página, WPF consiste en un archivo XAML y un archivo CodeBehind(.cs), los cuales en
conjunto crean la Ventana/Página. El archivo XAML define la interfaz con todos sus
elementos, mientras que el CodeBehind maneja todos los eventos y puede manipularlos
mediante los controles XAML.
8

4
1/9/2023

2.1. COMO FUNCIONA XAML


Para poder escribir el código en XAML simplemente será necesario escribir el
nombre del control entre los signos de mayor que y menor que.

Código inicial de un proyecto WPF


<Window x:Class="InicioWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:InicioWPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>

</Grid>
</Window>

10

10

5
1/9/2023

Las etiquetas XAML tienen que ser cerradas, escribiendo un etiqueta de cierre o
insertando una barra diagonal al final del nombre de la etiqueta y antes que el
signo mayor de cierre:

<Button> <Button></Button> <Button />

En XAML existen varios controles que permiten colocar un contenido entre la


etiqueta de inicio y la de finalización, en los cuales formaría parte del contenido del
control.

<Button>Presiona aqui</Button>

11

11

<Window x:Class="InicioWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:InicioWPF"
mc:Ignorable="d"
Title="MainWindow" Height="150" Width="600">
<Grid>
<Button>Presiona aqui</Button>
</Grid>
</Window>

En HTML la escritura no es sensible a mayúsculas, pero en XAML si lo es. Esto se


debe a que los nombres corresponden a un tipo en el Framework de .NET. Lo
mismo aplica para los nombres de los atributos, que corresponden a propiedades
del control.
<Button FontWeight="Bold" Content=“Boton" /> 12

12

6
1/9/2023

La propiedad FontWeight, que otorga una letra de tipo negrita, y luego la


propiedad Content o contenido, que sería exactamente igual a poner el texto
entre las etiquetas de inicio y finalización. Sin embargo, todos los atributos de
un control pueden ser definidos de otra forma, donde parecerán como etiquetas
hijas del control principal, usando la notación Control-Punto-Propiedad:

<Button>
<Button.FontWeight>Bold</Button.FontWeight>
<Button.Content>Boton</Button.Content>
</Button>

13

13

Sin embargo, muchos controles permiten otro contenido diferente a texto,


como otros controles. Por ejemplo, es posible obtener un texto en diferentes
colores dentro de un mismo botón. Esto se puede lograr utilizando controles
TextBlock dentro del Button

<Button>
<Button.FontWeight>Bold</Button.FontWeight>
<Button.Content>
<WrapPanel>
<TextBlock Foreground="Blue">Multi</TextBlock>
<TextBlock Foreground="Red">Color</TextBlock>
<TextBlock>Button</TextBlock>
</WrapPanel>
</Button.Content>
</Button>

14

14

7
1/9/2023

La propiedad Content solamente te permite anexar un elemento hijo. Para


lograr anexar múltiples elementos en este caso usamos un WrapPanel para
contener los diferentes bloques coloreados de texto. Los paneles como el
WrapPanel juegan un papel importante en WPF

<Button FontWeight="Bold">
<WrapPanel>
<TextBlock Foreground="Blue">Multi</TextBlock>
<TextBlock Foreground="Red">Color</TextBlock>
<TextBlock>Button</TextBlock>
</WrapPanel>
</Button>

15

15

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

16

16

8
1/9/2023

public partial class MainWindow : Window


{
public MainWindow()
{
InitializeComponent();
}

private void Button_Click(object sender, RoutedEventArgs e)


{
MessageBox.Show("Hola, WPF!");
}
}

17

17

En el ejemplo anterior, el código subyacente implementa una clase que deriva de


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

18

18

9
1/9/2023

2.2. NOMENCLATURA DE ELEMENTOS XAML

Contenido basado en la
Etiqueta o TAG definida
etiqueta

<ETIQUETA> </ETIQUETA>

Apertura o inicio de Fin o cierre de


etiqueta etiqueta

19

19

➢ ATRIBUTOS

Apertura Contenido Cierre


<button FontWeight=“Bold”>Hola WPF…</button>
Nombre Valor

20

20

10
1/9/2023

2.1. 3. VENTAJAS
Es más nuevo y, por lo tanto, está más en consonancia con los estándares actuales
Microsoft lo está utilizando para muchas aplicaciones nuevas, por ejemplo, Visual
Studio
Es más flexible, por lo que puede hacer más cosas sin tener que escribir o comprar
nuevos controles
Cuando necesite utilizar controles de terceros, es probable que los desarrolladores
de estos controles se enfoquen más en WPF porque es más reciente
XAML hace que sea fácil crear y editar su GUI, y permite que el trabajo se divida
entre un diseñador (XAML) y un programador (C#, VB.net etc.)
Enlace de datos (Databinding), le permite obtener una separación más limpia de los
datos y el diseño
Utiliza la aceleración de hardware para dibujar la GUI, para un mejor rendimiento
Permite realizar interfaces de usuario tanto para aplicaciones Windows como para
aplicaciones web (Silverlight/XBAP)

21

21

2.1.4. CONFIGURACIÓN DEL ENTORNO

<Window x:Class="Ejemplo1WPF.MainWindow" <Button>

xmlns="http://schemas.microsoft.com/winfx/20 <Button.FontWeight>Bold</Button.FontWeight>
06/xaml/presentation" <Button.Content>
<WrapPanel>
xmlns:x="http://schemas.microsoft.com/winfx/ <TextBlock
2006/xaml" Foreground="Blue">Boton</TextBlock>
<TextBlock
xmlns:d="http://schemas.microsoft.com/expres Foreground="Red">Multi</TextBlock>
sion/blend/2008"
<TextBlock>Color</TextBlock>
xmlns:mc="http://schemas.openxmlformats.org/ </WrapPanel>
markup-compatibility/2006" </Button.Content>
xmlns:local="clr- </Button>
namespace:Ejemplo1WPF" </Grid>
mc:Ignorable="d" </Window>
Title="MainWindow" Height="450"
Width="800">
<Grid>

22

22

11
1/9/2023

Button btn = new Button(); txt = new TextBlock();


btn.FontWeight = txt.Text = "Button";
FontWeights.Bold; pnl.Children.Add(txt);

WrapPanel pnl = new WrapPanel(); btn.Content = pnl;


pnlMain.Children.Add(btn);
TextBlock txt = new TextBlock();
txt.Text = "Multi";
txt.Foreground = Brushes.Blue;
pnl.Children.Add(txt);

txt = new TextBlock();


txt.Text = "Color";
txt.Foreground = Brushes.Red;
pnl.Children.Add(txt);

23

23

➢ EVENTOS XAML
La mayoría de los marcos (frameworks) de UI modernos al igual que WPF,
están impulsados por eventos. Todos los controles, incluida la ventana
(Window) (que también hereda la clase control) exponen un rango de
eventos a los que puede suscribirse, lo que significa que la aplicación será
notificada cuando ocurran y se podrá entonces reaccionar a ellos.
Hay muchos tipos de eventos, pero algunos de los más comúnmente
utilizados están hechos para responder a la interacción del usuario con su
aplicación, con el ratón o el teclado. En la mayoría de los controles se
encuentran eventos como KeyDown, KeyUp, MouseDown, MouseEnter,
MouseLeave, MouseUp y otros.
El funcionamiento de los eventos en WPF es un tema complejo, pera para
empezar es necesario conocer cómo vincular un evento de control en XAML
con un fragmento de código en su archivo de código subyacente (Code-
behind).
24

24

12
1/9/2023

<Window x:Class="WpfTutorialSamples.XAML.EventsSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="EventsSample" Height="300" Width="300">
<Grid Name="pnlMainGrid" MouseUp=“Evento1”
Background="LightBlue">

</Grid>
</Window>

Donde se tiene el evento MouseUp del Grid escribiendo un nombre de


método pnlMainGrid_MouseUp. Este método debe definirse en código
subyacente (code-behind), utilizando la firma de eventos correcta.
25

25

Siendo el código subyacente:

private void Evento1(object sender, MouseButtonEventArgs e)


{
MessageBox.Show(“Hiciste click en mi " +
e.GetPosition(this).ToString());
}

El evento MouseUp utiliza un delegado llamado MouseButtonEventHandler, al que


debe suscribirse. Tiene dos parámetros, un emisor (el control que causó el evento)
y un objeto MouseButtonEventArgs que contendrá información útil. Se utiliza en
este ejemplo para obtener la posición del cursor y notificar al usuario.

26

26

13
1/9/2023

Varios eventos pueden utilizar el mismo tipo de delegado - por ejemplo,


MouseUp y MouseDown utilizan MouseButtonEventHandler, mientras que el
evento MouseMove utiliza el delegado MouseEventHandler. Cuando se define el
método de gestión del evento, se necesita saber que delegado utiliza. Si se
desconoce, se puede buscar en la documentación.
Afortunadamente, Visual Studio brinda ayuda generando el gestor del evento. La
manera más fácil para realizar esto es simplemente escribir el nombre del evento
en XAML y dejar que el IntelliSense de VS haga el resto:

27

27

Si bien lo más común es enlazar un evento desde XAML, pueden existir


situaciones donde se prefiera suscribir eventos directamente desde el código
subyacente. Esto se realiza utilizando el sintaxis += de C#, donde se agrega en el
gestor de eventos al evento directamente en el objeto.

<Grid Background="LightBlue" Name="grid1">


</Grid>

public partial class MainWindow : Window


{
public MainWindow()
{
InitializeComponent();
grid1.MouseUp+= new MouseButtonEventHandler(Evento1);
}
private void Evento1(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Hiciste click en mi "+e.GetPosition(this).ToString());
}
}
28

28

14
1/9/2023

Una vez más, es necesario conocer que delegado utilizar, y una vez más, Visual
Studio puede ayudar. Apenas se escribe:

Simplemente se presiona la tecla [Tab] dos veces para que Visual Studio genere el
gestor de evento de manera automática, directamente debajo del método actual,
listo para ser implementado. Cuando se suscribe a los eventos de esta manera, no
es necesario hacerlo en XAML.

29

29

➢ LAYOUTS
Layout es un término de la lengua inglesa que no forma parte del diccionario de
la Real Academia Española. El concepto puede traducirse como “disposición” o
“plan” y tiene un uso extendido en el ámbito de la tecnología.
La noción de layout suele utilizarse para nombrar al esquema de distribución
de los elementos dentro un diseño. Es habitual que un diseñador que se dedica
a la creación de páginas web desarrolle un layout y se lo presente a su cliente
para que éste lo apruebe y decida sobre la distribución de los contenidos.

30

30

15
1/9/2023

Primero se tiene en cuenta que un Layout puede tener 2 significados:


Es un archivo con extensión .XML , en el cual está todo el apartado de las
interfaces gráficas de usuario (conjunto de imágenes y objetos gráficos para
representar la información y acciones disponibles). En este apartado está
toda la parte gráfica de la aplicación, ya sean imágenes, botones, entre
otros; los cuales van a permitir al usuario ver e interactuar con los servicios
que ofrece nuestra aplicación.
Un Layout define la estructura visual de la interfaz gráfica, permitiendo
distribuir, dimensionar y posicionar los elementos que están en su interior
de la mejor manera; además de ser un elemento no visual que extiende de
una clase padre llamada ViewGroup.

31

31

➢ GRID LAYOUT
Por defecto se tiene el Grid Layout, el cual añade los diferentes componentes
en la parte central, los cuales pueden ser reubicados.

32

32

16
1/9/2023

➢ FRAME LAYOUT
Este es el Layout más básico, en donde todos los controles hijos quedan
ubicados en la esquina superior izquierda, además, si se colocan más objetos
van a quedar uno encima de otro en la misma posición ocultando al anterior.

33

33

➢ LINEAR LAYOUT
Este Layout es uno de los más comunes junto al Relative Layout, dispone los
elementos uno debajo o al lado del otro hijo, dependiendo de su orientación
vertical u horizontal.

34

34

17
1/9/2023

➢ RELATIVE LAYOUT
Este Layout permite posicionar los elementos de forma relativa a su Layout
contenedor o a cualquiera de sus elementos incluidos en él. Permite por
ejemplo, ubicar un texto a la derecha del botón, los cuales están debajo de una
imagen.

35

35

PANEL DESCRIPCIÓN
Canvas Define un área dentro de la cual puede colocar explícitamente
elementos secundarios por coordenadas relativas al área del canvas
.
DockPanel Define un área dentro de la cual puede organizar los elementos
secundarios, ya sea horizontal o verticalmente, entre sí..
Grid Define un área de cuadrícula flexible que consta de columnas y filas.
StackPanel Organiza los elementos secundarios en una sola línea que se puede
orientar horizontal o verticalmente.
VirtualizingPanel Proporciona un marco para los elementos del Panel que virtualizan
su recopilación de datos secundarios. Esto es una clase abstracta..
WrapPanel Coloca los elementos secundarios en una posición secuencial de
izquierda a derecha, dividiendo el contenido en la siguiente línea en
el borde del cuadro contenedor. El orden posterior ocurre
secuencialmente de arriba a abajo o de derecha a izquierda, según
el valor de la propiedad Orientación .
36

36

18
1/9/2023

➢ MARGIN Y PADDING

37

37

INTRODUCCIÓN A APLICACIONES WPF


Una aplicación de WPF requiere el Framework de .NET para ejecutarse, como
cualquier otra aplicación de .NET. Afortunadamente, Microsoft está incluyendo
el framework de .NET en todas las versiones de Windows desde el Vista, y ellos
han impulsado el framework en versiones viejas a través de Windows Update.
En otras palabras, es casi seguro que cualquier usuario de Windows podrá
correr una aplicación de WPF.
Cuando se crea una aplicación en WPF, el primer elemento generado
automáticamente es la clase Window. Esta sirve como raíz de la ventana y provee
algunos elementos estándar como un borde, una barra de títulos, y los botones de
control de ventana. Una ventana de WPF es una combinación de un archivo XAML
(.xaml), donde el elemento <Window> es la raíz, y este contiene un archivo
CodeBehind(.cs).

38

38

19
1/9/2023

<Window x:Class="IniciandoWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:IniciandoWPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>

</Grid>
</Window>

39

39

El atributo x:class le dice al XAML que clase utilizar,


NombreProyecto.MainWindow, que Visual Studio ha creado. Se podrá
encontrarlo en el árbol de proyectos en VS, como un nodo hijo del archivo
XAML (archivo .cs). Por defecto, se ve algo parecido a esto:

using System.Windows.Controls; /// Lógica de interacción para


using System.Windows.Data; MainWindow.xaml
using System.Windows.Documents; /// </summary>
using System.Windows.Input; public partial class MainWindow :
using System.Windows.Media; Window
using System.Windows.Media.Imaging; {
using System.Windows.Navigation; public MainWindow()
using System.Windows.Shapes; {
InitializeComponent();
namespace IniciandoWPF }
{ }
/// <summary> }

40

40

20
1/9/2023

Como se puede ver, la clase MainWindow esta definida como parcial,


porque se combina con el archivo XAML al momento de ejecución para
dar una ventana completa. Esto es lo que realiza InitializeComponent(),
por lo cual es requerido para obtener un funcionamiento y ejecución de
la ventana. Si se observa el archivo XAML, se notará un par de atributos
interesantes en los elementos de la ventana, como Titulo, que define el
titulo de la ventana (mostrado en la barra de titulo) así también el ancho y
alto inicial.
También se observa que Visual Studio ha creado un control Grid adentro
de la ventana. El control Grid es parte de los paneles de WPF, y mientras
pudo ser cualquier panel o control, la Ventana solo puede tener un control
hijo, así que un panel, que en cambio puede tener múltiples controles hijos
es una buena decisión.
41

41

➢ PROPIEDADES O ATRIBUTOS DE WINDOW


Icon - Permite definir el ícono de la ventana, que generalmente se muestra
en la esquina superior izquierda, justo antes del título de la ventana.
ResizeMode - Esto controla si el usuario final puede redimensionar la
ventana o no. Por defecto es CanResize, que permite al usuario
redimensionar la ventana como cualquier otra, ya sea usando los botones de
maximizar/minimizar o arrastrando alguno de sus bordes. CanMinimize
permitirá al usuario minimizar la ventana, pero no maximizarla o arrastrarla
para que se agrande o achique. NoResize es el más estricto, los botones de
maximizar y minimizar no aparecen y la ventana no puede cambiar su
tamaño arrastrándola.
ShowInTaskbar - Por defecto es verdadero, pero si se configura en falso, la
ventana no aparecerá en la barra de tareas de Windows. Es útil para
ventanas secundarias, o aplicaciones que deben minimizarse en la bandeja.

42

42

21
1/9/2023

SizeToContent - Decide se la ventana debe redimensionarse automáticamente


para ajustarse a su contenido. Por defecto es Manual, lo que significa que no se
redimensiona automáticamente. Otras opciones son Width: Ancho, Height: Alto y
WidthAndHeight: Ancho y Alto, cada una de ellas podrá ajustar automáticamente
el tamaño de la ventana horizontalmente, verticalmente o ambos.
Topmost - Por defecto es falso, pero si se lo pone verdadero, la ventana
permanecerá por encima de otras ventanas a menos que esté minimizada. Sólo
útil para ocasiones especiales.
WindowStartupLocation - Controla la posición inicial de su ventana. Por defecto
es Manual, lo que significa que la ventana inicialmente se posicionará de acuerdo
a sus propiedades Top y Left. Otras opciones son CenterOwner, la cual
posicionará a la ventana en el centro de la ventana que la contenga, y
CenterScreen, la cual posicionará a la ventana en el centro de la pantalla.
WindowState - Controla el estado inicial de la ventana. Puede ser Normal,
Maximized (maximizada) o Minimized (minimizada). Por defecto es Normal, que es
lo que se debería usar, a menos que se quiera que la ventana comience
maximizada o minimizada.
43

43

➢ APP.XML
App.xaml es el punto declarativo inicial de una aplicación. Visual Studio
automáticamente crea este archivo cuando se crea una nueva aplicación WPF,
incluyendo un archivo Code-behind llamado App.xaml.cs. Estas clases trabajan
similarmente a la de una Ventana, donde hay dos archivos que son clases
parciales, trabajando en conjunto para permitir tanto texto de marcado(XAML) y
Code-behind.
App.xaml.cs extiende la clase Application, la cual es una clase central en una
aplicación de WPF de Windows. .NET también busca esta clase para obtener
sus instrucciones iniciales para luego iniciar la ventana o página deseada desde
esta. Este también es el lugar para subscribir eventos importantes de aplicación
tales como, inicio de aplicación, excepciones sin manejar y otras. Una de las
características utilizadas con mayor frecuencia de un archivo App.xaml es la
definición de recursos globales que pueden ser utilizadas en toda la aplicación,
por ejemplo estilos globales.
44

44

22
1/9/2023

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

namespace IniciandoWPF
{
/// <summary>
/// Lógica de interacción para App.xaml
/// </summary>
public partial class App : Application
{
}
}

45

45

1.2. ELEMENTOS BASE WPF

46

23
1/9/2023

➢ EL CONTROL TEXTBLOCK
Aunque el control TextBlock es uno de los más básicos de WPF, es uno de
mucha utilidad. Permite colocar texto en la pantalla, de manera similar a como
lo hace el control Label, pero más simple y consumiendo menos recursos.
Normalmente se usa Label para textos cortos de una línea (que pueden
contener, por ejemplo, una imagen), por otra parte, TextBlock trabaja muy bien
con cadenas de texto de varias líneas, pero solo puede contener texto(strings).
Tanto el Label como el TextBlock ofrecen cada uno ventajas únicas, por lo que
su uso depende mucho de la situación.

47

47

<Window x:Class="IniciandoWPF.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-
compatibility/2006"
xmlns:local="clr-namespace:IniciandoWPF"
mc:Ignorable="d"
Title="MainWindow" Height="100" Width=“100">
<Grid>
<TextBlock>Hola WPF</TextBlock>
</Grid>
</Window>

48

48

24
1/9/2023

<Window x:Class="IniciandoWPF.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-
compatibility/2006"
xmlns:local="clr-namespace:IniciandoWPF"
mc:Ignorable="d"
Title="MainWindow" Height="100" Width=“100">
<Grid>
<TextBlock Margin="15">Este es un TEXTBLOCK con una gran línea
de código</TextBlock>
</Grid>
</Window>

49

49

Afortunadamente, el control TextBlock admite contenido en línea. Estas


pequeñas construcciones, similares a controles, derivan de la clase Inline, lo que
significa que pueden ser generadas en línea, como parte de un texto mayor. Los
elementos soportados incluyen AnchoredBlock (bloque anclado), Bold (negrita),
Hyperlink (hipervínculo), InlineUIContainer (contenedor de controles), Italic
(cursiva), LineBreak (salto de línea), Run (texto con formato), Span (texto y
elementos con formato) y Underline (subrayado).
El TextBlock es perfectamente capaz de lidiar con textos largos y de múltiples
líneas, pero no hace nada por defecto. En el caso de que el texto es demasiado
largo para ser mostrado dentro de la ventana, entonces WPF muestra todo el
texto que puede y luego solamente para.
✓ LINEBREAK
Introduce un salto de línea forzado en un punto del texto.

50

50

25
1/9/2023

✓ HIPERLINK
El elemento Hyperlink permite introducir enlaces en los textos, con un aspecto
visual acorde con el tema de Windows activo, lo que suele ser alguna tonalidad
de color azul cambiando a rojo cuando el cursor se coloca encima y teniendo
este último forma de mano. Puede usar la propiedad NavigateUri para definir la
URL a la que desea navegar

51

51

<Window x:Class="IniciandoWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:IniciandoWPF"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="400">
<StackPanel>
<TextBlock Margin="10" Foreground="Red">Este es un TEXTBLOCK
<LineBreak/>con una gran línea de código</TextBlock>
<TextBlock Margin="10" TextTrimming="CharacterEllipsis"
Foreground="Green">Este es un TEXTBLOCK con una gran línea de código</TextBlock>
<TextBlock Margin="10" TextWrapping="Wrap" Foreground="Blue">Este es un
TEXTBLOCK con una gran línea de código</TextBlock>
</StackPanel>
</Window>

52

52

26
1/9/2023

<Window x:Class="Wpf_AulaV.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_AulaV"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="250">
<StackPanel>
<TextBlock Margin="10" Foreground="Red">
Este es un control Textblock<LineBreak />con muchas líneas de texto.
</TextBlock>
<TextBlock Margin="10" TextTrimming="CharacterEllipsis"
Foreground="Green">
Este es un control TextBlock con texto que puede que no se muestre por completo,
lo que se indicará con puntos suspensivos.
</TextBlock>
<TextBlock Margin="10" TextWrapping="Wrap" Foreground="Blue">
Este es un control TextBlock con texto ajustado automáticamente,
usando la propiedad TextWrapping.
</TextBlock>
</StackPanel>
53
</Window>

53

54

54

27
1/9/2023

<Window x:Class="Wpf_AulaV.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_AulaV"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="250">
<Grid>
<TextBlock Margin="10" TextWrapping="Wrap">
TextBlock con texto en <Bold>Negrita</Bold>, <Italic>Cursiva</Italic> y
<Underline>Subrayado</Underline> texto.
</TextBlock>
</Grid>
</Window>

55

55

<Window x:Class="Wpf_AulaV.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_AulaV"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="250">
<Grid>
<TextBlock Margin="10" TextWrapping="Wrap">
<Hyperlink NavigateUri="http://www.google.com"
RequestNavigate="Hyperlink_RequestNavigate">google.com</Hyperlink>
</TextBlock>
</Grid>
</Window> private void
Hyperlink_RequestNavigate(object sender,
RequestNavigateEventArgs e)
{
Process.Start(new
ProcessStartInfo(e.Uri.AbsoluteUri) {
UseShellExecute = true });
using System.Diagnostics; }
56
using System.Windows.Navigation;
56

28
1/9/2023

✓ RUN
El atributo Run permite dar formato a una cadena de texto utilizando todas las
propiedades disponibles en el elemento Span, pero mientras que éste puede
contener en su interior otros elementos en línea, un elemento Run solo puede
contener texto. Eso hace del elemento Span más flexible y, por tanto, la elección
más lógica en la mayor parte de los casos
✓ SPAN
El elemento Span no tiene un formato específico predefinido, pero le permite
especificar casi cualquier tipo de formato, incluyendo el tamaño, estilo y grosor de
la fuente, los colores de fondo y del texto, etc. Lo mejor del elemento Span es que
permite contener otros elementos en línea en su interior, facilitando la
construcción de combinaciones avanzadas de textos y estilos.

57

57

<StackPanel>
<TextBlock Margin="10" TextWrapping="Wrap">
Este <Span FontWeight="Bold">es</Span> un
<Span Background="Silver" Foreground="Maroon">TextBlock</Span>
con <Span TextDecorations="Underline">muchos</Span>
<Span FontStyle="Italic">Span</Span> elementos,
<Span Foreground="Blue"> usando una <Bold>gran variedad</Bold>
de <Italic>estilos</Italic></Span>.
</TextBlock>
</StackPanel>

58

58

29
1/9/2023

✓ DANDO FORMATO AL TEXTO DESDE EL CÓDIGO SUBYACENTE


El dar formato al texto mediante XAML es una tarea bastante sencilla, sin
embargo, existen casos en los que se podría preferir, o incluso necesitar, hacerlo
desde un archivo de código subyaciente en C#.
<Window x:Class="Wpf_AulaV.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_AulaV"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="350">
<Grid>

</Grid>
</Window>

59

59

namespace Wpf_AulaV
{
/// <summary>
/// Lógica de interacción para MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TextBlock tb = new TextBlock();
tb.TextWrapping = TextWrapping.Wrap;
tb.Margin = new Thickness(10);
tb.Inlines.Add("Un ejemplo de ");
tb.Inlines.Add(new Run("un control TextBlock ") { FontWeight = FontWeights.Bold });
tb.Inlines.Add("usando ");
tb.Inlines.Add(new Run("texto en línea ") { FontStyle = FontStyles.Italic });
tb.Inlines.Add(new Run("con formato ") { Foreground = Brushes.Blue });
tb.Inlines.Add("desde ");
tb.Inlines.Add(new Run("Código Subyacente") { TextDecorations =
TextDecorations.Underline });
tb.Inlines.Add(".");
this.Content = tb;
}
}
} 60

60

30
1/9/2023

➢ EL CONTROL LABEL
El control Label, en su forma más simple, es muy similar al control TextBlock.
Sin embargo, rápidamente se notará que en lugar de una propiedad Text, tiene
una propiedad Content. La razón de esto es que Label puede alojar cualquier
otro tipo de control directamente dentro de él, en lugar de solo texto. Este
contenido también puede ser una cadena.
<Grid>
<Label Content="Este es un control Label."/>
</Grid>

Es posible notar que el control Label, por defecto, tiene un poco de padding,
permitiendo al texto ser renderizado a pocos pixels del alto y de la esquina
izquierda. En un caso sencillo como este, donde el contenido es
simplemente una cadena de texto, el Label creará un TextBlock
61
internamente y mostrará la cadena en él.
61

✓ EL CONTROL LABEL VS EL CONTROL TEXTBLOCK

Desde esta perspectiva, la pregunta necesaria es: ¿Por qué usar el control
Label? Bien, hay pocas diferencias importantes entre el Label y el TextBlock. El
TextBlock sólo permite renderizar una cadena de texto, mientras que el Label
permite:
Especificar un borde
Renderizar otros controles, por ejemplo, una imagen.
Usar templates a través de la propiedad ContentTemplate
Usar teclas de acceso para posicionar el foco en el control relacionado.
El último punto es uno de los principales motivos para usar el Label en vez del
control TextBlock. Cuando se quiera renderizar texto sencillo, se debe usar el
TextBlock, ya que es más ligero y funciona mejor que el Label en la mayoría de
los casos.

62

62

31
1/9/2023

✓ LABEL Y TECLAS DE ACCESO (MNEMOTÉCNICOS)


En Windows y otros sistemas operativos, es una práctica común que se pueda
acceder a controles en un diálogo pulsando la tecla [Alt] y pulsando el caracter
que corresponda al control que se desea acceder. El caracter a presionar será
sobresaltado cuando se pulse la tecla [Alt]. El control TextBlock no soporta esta
funcionalidad, pero el Label sí, así que para controlar etiquetas, el control Label
es normalmente una excelente opción.
<StackPanel Margin="10">
<Label Content="_Name:"
Target="{Binding ElementName=txtName}" />
<TextBox Name="txtName" />
<Label Content="_Mail:"
Target="{Binding ElementName=txtMail}" />
<TextBox Name="txtMail" />
</StackPanel>

63

63

✓ USANDO CONTROLES COMO CONTENIDO DE UN LABEL


El control Label permite alojar otros controles, mientras mantiene los otros
beneficios.
<StackPanel Margin="10">
<Label Target="{Binding ElementName=txtName}">
<StackPanel Orientation="Horizontal">
<Image
Source="http://cdn1.iconfinder.com/data/icons/fatcow/16/bullet_green.png" />
<AccessText Text="_Name:" />
</StackPanel>
</Label>
<TextBox Name="txtName" />
<Label Target="{Binding ElementName=txtMail}">
<StackPanel Orientation="Horizontal">
<Image
Source="http://cdn1.iconfinder.com/data/icons/fatcow/16/bullet_blue.png" />
<AccessText Text="_Mail:" />
</StackPanel>
</Label>
<TextBox Name="txtMail" />
64
</StackPanel>

64

32
1/9/2023

<StackPanel Margin="10">
<Label Target="{Binding ElementName=txtName}">
<StackPanel Orientation="Horizontal">
<Image Source=“F:\Univalle 2022\Semestre 2-2022\Programacion
II\Iconos_C\windows_security_21075.png" Height="10" />
<AccessText Text="_Name:" />
</StackPanel>
</Label>
<TextBox Name="txtName" />
<Label Target="{Binding ElementName=txtMail}">
<StackPanel Orientation="Horizontal">
<Image Source=“F:\Univalle 2022\Semestre 2-2022\Programacion
II\Iconos_C\Windows_Security_25875.png" Height="10"/>
<AccessText Text="_Mail:" />
</StackPanel>
</Label>
<TextBox Name="txtMail" />
</StackPanel>

65

65

66

66

33
1/9/2023

➢ EL CONTROL TEXTBOX
El control TextBox es el control más básico para introducir texto en WPF,
permitiendo al usuario final escribir texto plano en una sola línea para un
formulario o como múltiples líneas como en un editor de texto.

<StackPanel Margin="10">
<TextBox />
</StackPanel>

<StackPanel Margin="10">
<TextBox Text=“Hola Mundo!" />
</StackPanel>

67

67

<Grid Margin="10">
<TextBox AcceptsReturn="True"
TextWrapping="Wrap" />
</Grid>

68

68

34
1/9/2023

✓ REVISIÓN ORTOGRÁFICA CON TEXTBOX


El control TextBox provee revisión automática de ortografía para Inglés y un par
de otros lenguajes.
Funciona igual a Microsoft Word, donde los errores ortográficos son subrayados
y puede hacer clic derecho para alternativas sugeridas. Habilitar la revisión
ortográfica es muy sencillo:
<Grid Margin="10">
<TextBox AcceptsReturn="True" TextWrapping="Wrap"
SpellCheck.IsEnabled="True" Language="es" />
</Grid>

69

69

<Grid Margin="10">
<TextBox AcceptsReturn="True" TextWrapping="Wrap"
SpellCheck.IsEnabled="True" Language="es" />
</Grid>

La propiedad de la clase SpellCheck llamada IsEnabled habilita la revisión


ortográfica en el control y la propiedad Language le indica al revisor ortográfico
que lenguaje utilizar.
70

70

35
1/9/2023

✓ TRABAJANDO CON SELECCIONES EN EL TEXTBOX


Como cualquier otro control editable en Windows, el TextBox permite la
selección de texto; por ejemplo, para borrar una palabra completa o para copiar
texto al portapapeles. El control TextBox de WPF posee ciertas propiedades
para trabajar con el texto seleccionado, todas ellas son modificables.
<DockPanel>
<TextBox DockPanel.Dock="Top" SelectionChanged="TextBox_SelectionChanged" />
<TextBox Name="txtStatus" AcceptsReturn="True" TextWrapping="Wrap"
IsReadOnly="True" />
</DockPanel>

71

71

SelectionStart, la cual brinda la posición actual del cursor o, si hay texto


seleccionado, donde éste inicia.
SelectionLength, brinda la longitud del texto seleccionado si hubiere. Sino, solo
retorna 0.
SelectedText, retorna el texto seleccionado si lo hubiere. Sino, se retorna un
texto vacío
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private void TextBox_SelectionChanged(object sender, RoutedEventArgs e)


{
TextBox textBox = sender as TextBox;
txtStatus.Text = "La selección comienza en el caracter #" +
textBox.SelectionStart + Environment.NewLine;
txtStatus.Text += "La selección tiene " + textBox.SelectionLength + "
caracteres(s) de longitud" + Environment.NewLine;
txtStatus.Text += "Texto seleccionado: '" + textBox.SelectedText + "'";
} 72
}

72

36
1/9/2023

73

73

➢ EL CONTROL BUTTON
El último de los componentes básicos es el control Button (botón), WPF tiene
incluido uno muy agradable, y como el resto de los controles de framework, es
muy flexible y permitirá lograr casi cualquier cosa.
<DockPanel>
<Button Click="Button_Click">Saludo!</Button>
</DockPanel>

private void Button_Click(object sender, RoutedEventArgs e)


{
MessageBox.Show(“Hola, WPF!");
}

74

74

37
1/9/2023

✓ BOTONES CON CONTENIDO CON FORMATO


Internamente, el texto simple dentro del Contenido del botón se convierte en un
control TextBlock, lo que también significa que puede controlar los mismos
aspectos del formato del texto. Se encontrarán varias propiedades en el control
Button para hacer esto, incluyendo (pero no limitado a) Foreground, Background,
FontWeight y así sucesivamente.
<DockPanel>
<Button Background="Yellow" Foreground="Red" FontWeight="Bold">Botón
con formato</Button>
</DockPanel>

75

75

✓ BOTONES CON CONTENIDO AVANZADO


En este caso también es posible reemplazar texto simple dentro de un control
con otros controles de WPF. Esto también significa que no se tiene que limitar
los botones a un texto simple, formateado de la misma manera, solo se deben
agregar algunos controles de texto con un formato diferente. El botón WPF solo
admite un control secundario directo, pero puede hacer que sea un Panel, que
luego albergará tantos controles como sea necesario.
<Grid>
<Button>
<StackPanel Orientation="Horizontal">
<TextBlock>Botón con</TextBlock>
<TextBlock Foreground="Blue" FontWeight="Bold" Margin="2,0">formato
avanzado</TextBlock>
<TextBlock Foreground="Red" FontStyle="Italic">[Varios]</TextBlock>
</StackPanel>
</Button>
</Grid>

76

76

38
1/9/2023

77

77

✓ BOTONES CON IMÁGENES (IMAGEBUTTON)


En muchos frameworks GUI, encontrará un botón regular y luego una o varias
variantes más, que ofrecerán características adicionales. Una de las variantes
más utilizadas es el ImageButton , que, como su nombre lo indica, es un botón
que generalmente le permitirá incluir una imagen antes del texto. Pero en WPF,
no hay necesidad de un control separado para lograr esto ya que es posible
colocar varios controles dentro de un botón, de modo que se puede agregarle un
Image control de la misma manera.

78

78

39
1/9/2023

<Grid>
<Button Padding="5">
<StackPanel Orientation="Horizontal">
<Image Source="E:\Univalle 2022\Semestre 2-2022\Programacion
II\Iconos_C\search_14765.png" Height="15"/>
<TextBlock Margin="5,0">Buscar</TextBlock>
</StackPanel>
</Button>
</Grid>

79

79

✓ ATRIBUTOS GLOBALES
En algunas ocasiones puede ser un trabajo moroso y aburrido, ir colocando
atributos a los componentes wpf, cuando los mismos tienen los mismos valores,
por ejemplo, tratar de aplicar padding de 5px superior e inferior y 2px izquierda y
derecha, sin embargo, se puede aplicar el relleno globalmente, ya sea en toda la
aplicación o solo en una ventana específica, usando un estilo, aplicando la
propiedad Window.Resources

<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Padding" Value="5,2"/>
</Style>
</Window.Resources>

80

80

40
1/9/2023

➢ EL CONTROL CHECKBOX
El control CheckBox permite al usuario final alternar una opción entre
verdadero y falso. Usualmente refleja un valor Boolean en el código subyacente.
<StackPanel Margin="10">
<Label FontWeight="Bold">Opciones de Aplicación</Label>
<CheckBox>Opción 1</CheckBox>
<CheckBox IsChecked="True">Opción 2</CheckBox>
<CheckBox>Opción 3</CheckBox>
</StackPanel>

81

81

✓ CHECKBOX PERSONALIZADOS
El control CheckBox hereda de la clase ContentControl, lo que significa que
puede tomar contenido personalizado y mostrarlo junto a él. Si se especifica un
trozo de texto, WPF lo pondrá dentro de un control de tipo TextBlock y lo
mostrará, pero es posible utilizar cualquier tipo de control dentro de él.

82

82

41
1/9/2023

<StackPanel Margin="10">
<Label FontWeight="Bold">Opciones de Aplicación</Label>
<CheckBox>
<TextBlock>
Opción <Run Foreground="Green" FontWeight="Bold">ABC</Run>
</TextBlock>
</CheckBox>
<CheckBox IsChecked="True">
<WrapPanel>
<TextBlock>
Opción <Run FontWeight="Bold">XYZ</Run>
</TextBlock>
<Image Source="F:\Univalle 2022\Semestre 2-2022\Programacion II\Iconos_C/carpeta.png"
Width="16" Height="16" Margin="5,0" />
</WrapPanel>
</CheckBox>
<CheckBox>
<TextBlock>
Opción <Run Foreground="Blue" TextDecorations="Underline" FontWeight="Bold">WWW</Run>
</TextBlock>
</CheckBox>
</StackPanel>

83

83

✓ LA PROPIEDAD ISTHREESTATE
El CheckBox normalmente corresponde a un valor lógico, lo que significa que
tiene sólo dos estados: true o false (verdadero o falso). En cualquier caso,
puesto que un tipo de datos boolean podría ser nullable, permitiendo de hecho
una tercera opción (true, false o null) el control CheckBox también permite este
escenario. Poniendo la propiedad IsThreeState a true, el CheckBox podrá
adquirir el tercer estado llamado el estado intermedio.
Una utilización habitual para esto es disponer de un CheckBox que habilite todo,
que puede controlar un conjunto de Checkboxes hijos así como mostrar su
estado colectivo.

84

84

42
1/9/2023

<StackPanel Margin="10">
<Label FontWeight="Bold">Opciones</Label>
<StackPanel Margin="10,5">
<CheckBox IsThreeState="True" Name="cbOpciones"
Checked="cbOpciones_CheckedChanged"
Unchecked="cbOpciones_CheckedChanged">Seleccionar todo</CheckBox>
<StackPanel Margin="20,5">
<CheckBox Name="cbOpcion1" Checked="cbOpcion_CheckedChanged"
Unchecked="cbOpcion_CheckedChanged">Opción ABC</CheckBox>
<CheckBox Name="cbOpcion2" IsChecked="True"
Checked="cbOpcion_CheckedChanged" Unchecked="cbOpcion_CheckedChanged">Opción
XYZ</CheckBox>
<CheckBox Name="cbOpcion3" Checked="cbOpcion_CheckedChanged"
Unchecked="cbOpcion_CheckedChanged">Opción WWW</CheckBox>
</StackPanel>
</StackPanel>
</StackPanel>

85

85

public partial class MainWindow : Window


{
public MainWindow()
{
InitializeComponent();
}
private void cbOpciones_CheckedChanged(object sender, RoutedEventArgs e)
{
bool newVal = (cbOpciones.IsChecked == true);
cbOpcion1.IsChecked = newVal;
cbOpcion2.IsChecked = newVal;
cbOpcion3.IsChecked = newVal;
}
private void cbOpcion_CheckedChanged(object sender, RoutedEventArgs e)
{
cbOpciones.IsChecked = null;
if ((cbOpcion1.IsChecked == true) && (cbOpcion2.IsChecked == true) && (cbOpcion3.IsChecked ==
true))
cbOpciones.IsChecked = true;
if ((cbOpcion1.IsChecked == false) && (cbOpcion2.IsChecked == false) && (cbOpcion2.IsChecked ==
false))
cbOpciones.IsChecked = false;
}
}

86

86

43
1/9/2023

87

87

➢ EL CONTROL RADIOBUTTON
El control RadioButton permite dar al usuario una lista de posibles opciones,
con solo un elemento seleccionado al mismo tiempo.
<StackPanel Margin="10">
<Label FontWeight="Bold">Estas listo?</Label>
<RadioButton>Si</RadioButton>
<RadioButton>No</RadioButton>
<RadioButton IsChecked="True">Tal vez</RadioButton>
</StackPanel>

88

88

44
1/9/2023

✓ AGRUPAMIENTO DE RADIOBUTTON
<StackPanel Margin="10">
<Label FontWeight="Bold">Estas listo?</Label>
<RadioButton GroupName="listo">Si</RadioButton>
<RadioButton GroupName="listo">No</RadioButton>
<RadioButton GroupName="listo" IsChecked="True">Tal vez</RadioButton>

<Label FontWeight="Bold">Género?</Label>
<RadioButton GroupName="sexo">Masculino</RadioButton>
<RadioButton GroupName="sexo">Femenino</RadioButton>
<RadioButton GroupName="sexo" IsChecked="True">No estoy seguro</RadioButton>
</StackPanel>

89

89

✓ PERSONALIZACIÓN DE RADIOBUTTON

Realice un programa para obtener la siguiente interfaz de usuario:

90

90

45
1/9/2023

➢ EL CONTROL PASSWORDBOX
Para editar texto normal, se utiliza un TextBox. Pero, ¿qué hay de la edición de
contraseñas? Si se requiere la misma funcionalidad básica, escondiendo los
caracteres ingresados para proteger las contraseñas. Para este propósito, WPF
provee el control de PasswordBox , que resulta tan fácil de usar como un
TextBox.

<StackPanel Margin="10">
<Label>Usuario:</Label>
<TextBox />
<Label>Contraseña:</Label>
<PasswordBox />
</StackPanel>

<PasswordBox PasswordChar="X" /> <PasswordBox MaxLength="6" /> 91

91

Cuando se necesite obtener el texto del elemento PasswordBox, se puede usar


la propiedad Password desde el código adyacente. De todas formas, por razones
de seguridad, la propiedad Password no está implementada como una
propiedad de dependencia, lo que significa que no será posible enlazarla.

92

92

46
1/9/2023

1.5. CONTROLES
CONTENEDORES

93

Paneles
Para el trabajo con WPF, es necesario contar con controles contenedores, que
permitan el posicionamiento de los diferentes controles o elementos.
Los paneles son uno de los tipos de controles más importantes de WPF. Actúan
como contenedores para otros controles, y manejan el layout de las ventanas o
páginas. A diferencia de una ventana, que sólo puede contener un control hijo;
un panel es usado comúnmente para dividir el espacio en áreas, donde cada área
puede contener un control u otro panel (que es, al fin y al cabo, otro control).
Los paneles presentan diferentes características, con cada uno de ellos
teniendo su propio modo de lidiar con el diseño y los controles hijos. Elegir el
panel correcto es esencial para obtener el comportamiento y el diseño que se
desea.

94

94

47
1/9/2023

➢ TIPOS DE PANELES
Los paneles que se encuentran a disposición en WPF son:
Canvas
Un panel simple, que imita la forma de hacer las cosas de WinForms. Permite
asignar coordenadas específicas a cada uno de los controles secundarios, lo
que te da un control total del diseño. Sin embargo, esto no es muy flexible,
porque se debe mover manualmente los controles secundarios y asegurarse de
que se alineen de la forma deseada. Se recomienda usarlo (solo) cuando se
desee un control completo de las posiciones de los controles hijos.
WrapPanel
El WrapPanel colocará cada uno de sus controles hijos uno junto al otro,
horizontalmente (predeterminado) o verticalmente, hasta que no haya más
espacio, donde se ajustará a la siguiente línea y luego continuará. Debe usarse
cuando se desee un control de lista vertical u horizontal que se ajusta
automáticamente cuando no hay más espacio.
95

95

StackPanel
El StackPanel actúa de manera muy parecida al WrapPanel, pero en lugar de
adaptarse si los controles secundarios ocupan demasiado espacio,
simplemente se expande, si es posible. Al igual que con WrapPanel, la
orientación puede ser horizontal o vertical, pero en lugar de ajustar el ancho o la
altura de los controles hijos en función del elemento más grande, cada elemento
se estira para abarcar todo el ancho o la altura. Se debe usar el StackPanel
cuando se desee una lista de controles que ocupe todo el cuadrante disponible,
sin adaptarlos.
DockPanel
El DockPanel permite acoplar los controles hijos a la parte superior, inferior,
izquierda o derecha. Por defecto, el último control, si no tiene una posición de
acoplamiento específica, llenará el espacio restante. Se puede lograr lo mismo
con el panel Cuadrícula (Grid), pero para las situaciones más simples, DockPanel
será más fácil de usar. Usar DockPanel siempre que se necesite acoplar uno o
varios controles a uno de los lados, como dividir la ventana en áreas específicas.
96

96

48
1/9/2023

Cuadrícula (Grid)
La Cuadrícula (Grid) es probablemente el más complejo de los tipos de paneles.
Una cuadrícula puede contener múltiples filas y columnas. Se define una altura
para cada una de las filas y un ancho para cada una de las columnas, ya sea en
una cantidad absoluta de píxeles, en un porcentaje del espacio disponible o
como automático, donde la fila o columna ajustará automáticamente su tamaño
según el contenido. Se usa la cuadrícula cuando los otros paneles no hagan el
trabajo, por ejemplo: cuando se necesitan varias columnas y, a menudo, en
combinación con los otros paneles.
Cuadrícula uniforme (UniformGrid)
UniformGrid es como la Cuadrícula (Grid), con la posibilidad de filas y columnas
múltiples, pero con una diferencia importante: todas las filas y columnas
tendrán el mismo tamaño. Se debe usar cuando se necesite el comportamiento
de cuadrícula sin la necesidad de especificar diferentes tamaños para las filas y
columnas.

97

97

✓ CANVAS
El panel Canvas es probablemente el panel más simple de todos. Realmente no
hace nada por defecto, solo permite poner controles en él y posicionarlos de
acuerdo al criterio del desarrollador usando coordenadas específicas. El panel
Canvas otorga control completo de las posiciones, no le preocupa realmente si
hay suficiente espacio para todos los controles o si uno está encima de otro
Si se tiene experiencia en IU como Windows Forms, Canvas será lo mismo, pero
si bien puede ser tentador tener control de todos los controles hijos, esto
también significa que el Panel no hará nada para mantener el diseño una vez que
el usuario comience a cambiar el tamaño de la ventana, si se coloca texto
posicionado de manera absoluta o si el contenido está escalado.

98

98

49
1/9/2023

<Canvas>
<Button>Boton 1</Button>
<Button>Boton 2</Button>
</Canvas>

<Canvas>
<Button Canvas.Left="10">Superior izquierda</Button>
<Button Canvas.Right="10">Superior derecha</Button>
<Button Canvas.Left="10" Canvas.Bottom="10">Inferior izquierda</Button>
<Button Canvas.Right="10" Canvas.Bottom="10">Inferior derecha</Button>
</Canvas>

99

99

Z-Index
<Canvas>
<Ellipse Fill="Gainsboro" Canvas.Left="25" Canvas.Top="25" Width="200" Height="200" />
<Rectangle Fill="LightBlue" Canvas.Left="25" Canvas.Top="25" Width="50" Height="50" />
<Rectangle Fill="LightCoral" Canvas.Left="50" Canvas.Top="50" Width="50" Height="50" />
<Rectangle Fill="LightCyan" Canvas.Left="75" Canvas.Top="75" Width="50" Height="50" />
</Canvas>

<Canvas>
<Ellipse Panel.ZIndex="2" Fill="Gainsboro" Canvas.Left="25" Canvas.Top="25" Width="200"
Height="200" />
<Rectangle Panel.ZIndex="3" Fill="LightBlue" Canvas.Left="25" Canvas.Top="25" Width="50"
Height="50" />
<Rectangle Panel.ZIndex="2" Fill="LightCoral" Canvas.Left="50" Canvas.Top="50" Width="50"
Height="50" />
<Rectangle Panel.ZIndex="4" Fill="LightCyan" Canvas.Left="75" Canvas.Top="75" Width="50"
Height="50" />
</Canvas>

100

100

50
1/9/2023

✓ WRAPPANEL
El control WrapPanel colocará cada uno de sus controles hijos uno al lado del
otro, horizontalmente (predeterminado) o verticalmente, hasta que no haya más
espacio, donde se ajustará a la siguiente línea y luego continuará. Su mejor uso
se da al buscar una lista de controles que se alineen vertical u horizontal de
forma automática cuando ya no tenga mas espacio.
Cuando el control WrapPanel use la orientación horizontal, los controles hijos
tomarán la misma altura, basado en el elemento mas alto. Cuando el control
WrapPanel tenga una orientación vertical, los controles hijos tomarán el mismo
ancho, basado en el elemento mas ancho.

101

101

<WrapPanel>
<Button>Boton de prueba 1</Button>
<Button>Boton de prueba 2</Button>
<Button>Boton de prueba 3</Button>
<Button Height="40">Boton de prueba 4</Button>
<Button>Boton de prueba 5</Button>
<Button>Boton de prueba 6</Button>
</WrapPanel>

<WrapPanel Orientation="Vertical">
<Button>Boton de prueba 1</Button>
<Button>Boton de prueba 2</Button>
<Button>Boton de prueba 3</Button>
<Button Height="40" Width="44">Boton de prueba 4</Button>
<Button>Boton de prueba 5</Button>
<Button>Boton de prueba 6</Button>
</WrapPanel>

102

102

51
1/9/2023

✓ STACKPANEL
El StackPanel es muy similar al WrapPanel, pero con al menos una diferencia
importante: El StackPanel no adapta el contenido. En lugar de eso lo estira en
una dirección, permitiendo apilar ítem tras ítem uno encima de otro. Siendo su
orientación por defecto Vertical.

<StackPanel>
<Button>Boton 1</Button>
<Button>Boton 2</Button>
<Button>Boton 3</Button>
<Button>Boton 4</Button>
<Button>Boton 5</Button>
<Button>Boton 6</Button>
</StackPanel>

103

103

<StackPanel Orientation="Horizontal">
<Button>Boton 1</Button>
<Button>Boton 2</Button>
<Button>Boton 3</Button>
<Button>Boton 4</Button>
<Button>Boton 5</Button>
<Button>Boton 6</Button>
</StackPanel>

104

104

52
1/9/2023

✓ DOCKPANEL
El DockPanel simplifica la tarea de acoplar contenido en función a los lados de la
ventana: arriba, abajo, izquierda y derecha. Esto lo convierte en una gran opción
en muchas situaciones donde se quiera dividir la ventana en áreas específicas
especialmente porque, por defecto, el último elemento dentro del DockPanel (a
no ser que esta funcionalidad esté específicamente deshabilitada) va a llenar el
resto del espacio (desde el centro), es decir, la propiedad DockPanel.Dock,
decide en que dirección se quiere que el control hijo se coloque. Si no se usa
esta propiedad, el primer control se colocará desde la izquierda, y el último
tomará todo el espacio sobrante.
<DockPanel>
<Button DockPanel.Dock="Left">Izquierda</Button>
<Button DockPanel.Dock="Top">Arriba</Button>
<Button DockPanel.Dock="Right">Derecha</Button>
<Button DockPanel.Dock="Bottom">Abajo</Button>
<Button>Centro</Button>
</DockPanel>
105

105

<DockPanel>
<Button DockPanel.Dock="Top" Height="50">Arriba</Button>
<Button DockPanel.Dock="Bottom" Height="50">Abajo</Button>
<Button DockPanel.Dock="Left" Width="50">Izquierda</Button>
<Button DockPanel.Dock="Right" Width="50">Derecha</Button>
<Button>Centro</Button>
</DockPanel>

106

106

53
1/9/2023

Propiedad LastChildFill
El comportamiento por defecto, es que el último elemento del DockPanel utilice
todo el espacio remanente, pero esto puede deshabilitarse usando la propiedad
LastChildFill, además es posible acoplar más de un control en el mismo lado.

<DockPanel LastChildFill="False">
<Button DockPanel.Dock="Top" Height="50">Arriba</Button>
<Button DockPanel.Dock="Bottom" Height="50">Abajo</Button>
<Button DockPanel.Dock="Left" Width="50">Izquierda</Button>
<Button DockPanel.Dock="Left" Width="50">Izquierda</Button>
<Button DockPanel.Dock="Right" Width="50">Derecha</Button>
<Button DockPanel.Dock="Right" Width="50">Derecha</Button>
</DockPanel>

107

107

✓ GRID
El control Grid es probablemente el más complejo de los controles tipo panel.
Puede contener múltiples filas y columnas. Se debe definir la altura para cada
fila y el ancho de cada columna. Esas dimensiones pueden establecerse tanto
en forma absoluta en cantidad de pixels, como porcentaje del espacio
disponible o en forma automática Auto, en la cual la fila o columna ajustará su
tamaño automáticamente dependiendo del contenido. Se utiliza un control Grid
cuando otro tipo de paneles no sean adecuados para el formato deseado. Por
ejemplo, cuando se requiere de varias filas y/columnas, a menudo en
combinación con otro tipo de paneles.
En su forma más simple, una Grid tomará todos los controles que se coloquen
dentro de ella, aumentará su tamaño hasta que usen todo el espacio disponible,
y los ubicará uno encima de otro.
<Grid>
<Button>Boton 1</Button>
<Button>Boton 2</Button>
</Grid> 108

108

54
1/9/2023

El último control toma la posición superior, lo cual significa que no es posible ver
el primer botón. Pero es posible dividir el espacio, que es lo que la grilla hace tan
bien. Para lo cual se utilizan las propiedades ColumnDefinitions y
RowDefinitions.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button>Boton 1</Button>
<Button Grid.Column="1">Boton 2</Button>
</Grid>

Como se puede comprobar, los controles toman todo el espacio disponible, el cual
es el comportamiento por defecto cuando el Grid organiza sus controles hijos. Lo
hace estableciendo las propiedades HorizontalAlignment y VerticalAlignment de
los mismos a Stretch.
109

109

En ciertas situaciones se puede requerir que los controles en el Grid tomen


solamente el espacio que necesitan, y/o controlar como se ubican en la misma.
La forma más fácil de lograr esto es establecer las propiedades
HorizontalAlignment y VerticalAlignment directamente en los controles que se
desea manipular.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button VerticalAlignment="Top" HorizontalAlignment="Center">Boton
1</Button>
<Button Grid.Column="1" VerticalAlignment="Center"
HorizontalAlignment="Right">Botton 2</Button>
</Grid>

110

110

55
1/9/2023

Filas y columnas
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Button>Button 1</Button>
<Button Grid.Column="1">Boton 2</Button>
<Button Grid.Column="2">Boton 3</Button>
<Button Grid.Row="1">Boton 4</Button>
<Button Grid.Column="1" Grid.Row="1">Boton 5</Button>
<Button Grid.Column="2" Grid.Row="1">Boton 6</Button>
<Button Grid.Row="2">Boton 7</Button>
<Button Grid.Column="1" Grid.Row="2">Boton 8</Button>
<Button Grid.Column="2" Grid.Row="2">Boton 9</Button>
</Grid> 111

111

Un total de nueve botones, cada uno ubicado en su propia celda, en una grilla
que contiene tres filas y tres columnas, configurando el ancho basado en *,
además de asignar números. Se podrá notar el uso de las propiedades Attached
Grid.Row y Grid.Column para ubicar los controles en la grilla, omitiendo las
mismas en los controles de la primera y/o segunda fila.

112

112

56
1/9/2023

Unidades
Además del uso del *, que indica que una columna o fila debería tomar cierto
porcentaje del espacio combinado, existen otras dos formas de especificar el
ancho y altura de una columna o fila: Unidades absolutas y el ancho y altura
automáticos (Auto).
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Button>Boton 1</Button>
<Button Grid.Column="1">Boton 2 con texto largo</Button>
<Button Grid.Column="2">Boton 3</Button>
</Grid>

113

113

Extendiendo el Grid
El comportamiento por defecto es que cada control utilice una celda, pero a
veces se requiere que un control ocupe múltiples filas o columnas.
Afortunadamente, el Grid lo permite fácilmente con las propiedades
ColumnSpan y RowSpan. El valor por defecto de esa propiedad es 1, pero es
posible cambiarlo por un número más grande para que el control abarque varias
filas o columnas.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button>Boton 1</Button>
<Button Grid.Column="1">Boton 2</Button>
<Button Grid.Row="1" Grid.ColumnSpan="2">Boton 3</Button> 114
</Grid>

114

57
1/9/2023

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.ColumnSpan="2">Boton 1</Button>
<Button Grid.Column="3">Boton 2</Button>
<Button Grid.Row="1">Boton 3</Button>
<Button Grid.Column="1" Grid.Row="1" Grid.RowSpan="2"
Grid.ColumnSpan="2">Boton 4</Button>
<Button Grid.Column="0" Grid.Row="2">Boton 5</Button>
</Grid>

115

115

GridSplitter
El panel Grid permite facilitar el trabajo de dividir el espacio disponible en
celdas individuales. Usando definiciones de columnas y filas es posible definir
el espacio que ocuparán en pantalla, pero en ocasiones se vuelve necesario que
el usuario cambie estas medidas. Esto puede realizarse con la propiedad
GridSplitter, aplicándola a una columna o una fila en un Grid, con la cantidad
adecuada de espacio para ello.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock FontSize="55" HorizontalAlignment="Center"
VerticalAlignment="Center" TextWrapping="Wrap">Lado izquierdo</TextBlock>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" />
<TextBlock Grid.Column="2" FontSize="55" HorizontalAlignment="Center"
VerticalAlignment="Center" TextWrapping="Wrap">Lado derecho</TextBlock>
</Grid> 116

116

58
1/9/2023

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="5" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock FontSize="55" HorizontalAlignment="Center"
VerticalAlignment="Center" TextWrapping="Wrap">Arriba</TextBlock>
<GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch" />
<TextBlock Grid.Row="2" FontSize="55" HorizontalAlignment="Center"
VerticalAlignment="Center" TextWrapping="Wrap">Abajo</TextBlock>
</Grid>

117

117

Ejemplo

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox>Nombre</TextBox>
<TextBox Grid.Row="1">E-mail</TextBox>
<TextBox Grid.Row="2" AcceptsReturn="True">Observación</TextBox>
</Grid>

118

118

59
1/9/2023

Ejercicio

119

119

<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label>Nombre:</Label>
<TextBox Grid.Column="1" Margin="0,0,0,10" />
<Label Grid.Row="1">E-mail:</Label>
<TextBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,10" />
<Label Grid.Row="2">Observación:</Label>
<TextBox Grid.Row="2" Grid.Column="1" AcceptsReturn="True" />
</Grid>

120

120

60
1/9/2023

El control Image
El control de WPF Image permite mostrar imágenes en aplicaciones. Es un control
muy versátil con muchas opciones y métodos útiles.
<Grid>
<Image Source="https://empresas.blogthinkbig.com/wp-content/uploads/2019/11/Imagen3-245003649.jpg?fit=960%2C720" />
</Grid>

121

121

<Grid>
<Image Source="F:\Univalle 2023\Semestre 2-2023\Programacion
II\Imagenes\Color_picker_35275.png" />
</Grid>

<Image Source="/Carpeta_Aplicación;component/Images/google.png" />

<Image Source="/Images/google.png" />

122

122

61
1/9/2023

✓ CARGANDO IMÁGENES DINÁMICAMENTE


<StackPanel>
<WrapPanel Margin="10" HorizontalAlignment="Center">
<Button Name="btnLoadFromFile" Margin="0,0,20,0"
Click="BtnLoadFromFile_Click">Load from File...</Button>
</WrapPanel>
<Image Name="imgDynamic" Margin="10" />
</StackPanel>

private void BtnLoadFromFile_Click(object sender, RoutedEventArgs e)


{
OpenFileDialog openFileDialog = new OpenFileDialog();
if (openFileDialog.ShowDialog() == true)
{
Uri fileUri = new Uri(openFileDialog.FileName);
imgDynamic.Source = new BitmapImage(fileUri);
}
}

using Microsoft.Win32; 123

123

✓ LA PROPIEDAD STRETCH
La propiedad Stretch
(estiramiento), controla lo
que ocurre cuando las
dimensiones de la imagen
cargada no encajan
completamente las
dimensiones del control
Image. Esto suele pasar
todo el tiempo, ya que el
tamaño de la ventana
puede ser controlada por el
usuario y, a menos que el
diseño sea estático, esto
significa que el tamaño del
control Image va a cambiar
también. 124

124

62
1/9/2023

Uniform: (Uniforme) Este es el modo por defecto. La imagen va a ser


escalada para que quepa en el área del control Image. La Relación de
aspecto de la imagen se preserva.
UniformToFill: (Uniforme hasta llenar) La imagen va a ser escalada
para que llene completamente en el área del control Image. La
relación de aspecto de la imagen se preserva.
Fill: (Llenar) La imagen va a ser escalada para llenar el área del
control Image. La relación de aspecto NO se preserva, porque la
altura y anchura de la imagen se escalan independientemente.
None: (Nada) Si la imagen es más pequeña que el control Image, no
pasa nada. Si la imagen es más grande que el control Image, ésta va
a ser cortada para llenar el espacio del control Image, lo que quiere
decir que solamente parte de la imagen será visible.
125

125

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Grid.Column="0" HorizontalAlignment="Center" FontWeight="Bold">Uniform</Label>
<Label Grid.Column="1" HorizontalAlignment="Center"
FontWeight="Bold">UniformToFill</Label>
<Label Grid.Column="2" HorizontalAlignment="Center" FontWeight="Bold">Fill</Label>
<Label Grid.Column="3" HorizontalAlignment="Center" FontWeight="Bold">None</Label>
<Image Source="E:\Univalle 2022\Semestre 2-2022\Programacion II\Imagenes\tigre.jpg"
Stretch="Uniform" Grid.Column="0" Grid.Row="1" Margin="5" />
<Image Source="E:\Univalle 2022\Semestre 2-2022\Programacion II\Imagenes\tigre.jpg"
Stretch="UniformToFill" Grid.Column="1" Grid.Row="1" Margin="5" />
<Image Source="E:\Univalle 2022\Semestre 2-2022\Programacion II\Imagenes\tigre.jpg"
Stretch="Fill" Grid.Column="2" Grid.Row="1" Margin="5" />
<Image Source="E:\Univalle 2022\Semestre 2-2022\Programacion II\Imagenes\tigre.jpg"
Stretch="None" Grid.Column="3" Grid.Row="1" Margin="5" />
</Grid>
126

126

63
1/9/2023

CLASES BASES
Un alto porcentaje de las clases en WPF se derivan de cuatro clases que se
conocen comúnmente como las clases de elemento base. Estas clases son:
UIElement, ContentElement, FrameworkElementy FrameworkContentElement. La
clase DependencyObject también está relacionada, porque es una clase base de
UIElement y ContentElement.

127

127

2.2.1. UIELEMENT
UIElement es una clase base para implementaciones de nivel de núcleo de WPF
que se compila a partir de características básicas de presentación y elementos
de Windows Presentation Foundation (WPF). Proviene del espacio de nombres:
System.Windows y genera el ensamblado: PresentationCore.dll.
[System.Windows.Markup.UidProperty("Uid")]
public class UIElement : System.Windows.Media.Visual,
System.Windows.IInputElement, System.Windows.Media.Animation.IAnimatable

128

128

64
1/9/2023

UIElement proporciona un punto de partida para las características de diseño de


elementos y también expone métodos virtuales que las clases derivadas pueden invalidar,
lo que puede influir en el comportamiento de representación del diseño del elemento y sus
elementos secundarios.
Gran parte del comportamiento de entrada y de enfoque para los elementos en general
también se define en la clase UIElement. Esto incluye los eventos de teclado, mouse y
entrada de lápiz, y las propiedades de estado relacionadas. Muchos de estos eventos son
eventos enrutados y muchos de los eventos relacionados con la entrada tienen tanto una
versión de enrutamiento de propagación como una versión de tunelización del evento.
Estos eventos emparejados suelen ser los eventos de mayor interés para controlar a los
autores.
UIElement también incluye las API relacionadas con el modelo de eventos WPF, incluidos
los métodos que pueden generar eventos enrutados especificados que se originan en una
instancia del elemento.
En cuanto a la arquitectura, UIElement se puede considerar aproximadamente equivalente
a un identificador de ventana en la programación Win32 o un elemento en la programación
HTML dinámica (DHTML). UIElement es un elemento base en el nivel de núcleo de WPF.
129

129

Un UIElement tiene las siguientes capacidades definidas específicamente por la


clase UIElement:
Se puede representar como un elemento secundario (UIElement deriva de
Visual, una clase de gráficos de alto nivel)
Contiene la lógica que se usa para ajustar el tamaño y la posición de los
posibles elementos secundarios de un UIElement (cuando lo interpreta un
sistema de diseño)
Puede responder a la entrada del usuario (incluido el control de dónde se
envía la entrada a través de su control de enrutamiento de eventos o
enrutamiento de comandos).
Puede provocar eventos enrutados que viajan por la ruta a través del árbol de
elementos lógicos.
Admite algunos aspectos del sistema de animación.

130

130

65
1/9/2023

✓ FRAMEWORKELEMENT
FrameworkElement es la clase de implementación de nivel de marco de WPF
que se basa en UIElement y agrega interacciones específicas con el nivel marco
de WPF. FrameworkElement agrega y define las siguientes capacidades:
Características de diseño específicas del marco de trabajo adicionales
Compatibilidad con informes de metadatos más completos en propiedades
Implementación específica de la clase de determinadas clases base de
entrada y sus propiedades adjuntas o eventos adjuntos
Compatibilidad de estilo
Mayor compatibilidad con animaciones
[System.Windows.Markup.RuntimeNameProperty("Name")]
[System.Windows.Markup.UsableDuringInitialization(true)]
[System.Windows.Markup.XmlLangProperty("Language")]
[System.Windows.StyleTypedProperty(Property="FocusVisualStyle",
StyleTargetType=typeof(System.Windows.Controls.Control))] public class
FrameworkElement: System.Windows.UIElement,
System.ComponentModel.ISupportInitialize,
System.Windows.IFrameworkInputElement, System.Windows.Markup.IQueryAmbient 131

131

2.2.2. CONTENELEMENT
La clase CONTENELEMENT proporciona una clase base de nivel principal de
WPF para los elementos de contenido. Los elementos de contenido están
diseñados para la presentación del estilo de flujo, mediante un modelo de
diseño orientado a marcado intuitivo y un modelo de objetos deliberadamente
simple.
public class ContentElement : System.Windows.DependencyObject,
System.Windows.IInputElement, System.Windows.Media.Animation.IAnimatable

132

132

66
1/9/2023

ContentElement define las siguientes características de contenido comunes:


Entrada: todas las clases derivadas de ContentElement proporcionan compatibilidad
con la captura de entrada básica desde el teclado, el mouse, las operaciones de
arrastrar y colocar, los controles del lápiz óptico y los aceleradores.
Focus: todas las clases derivadas de ContentElement pueden recibir el foco. (Sin
embargo, el estado de foco predeterminado de la ContentElement clase base es false.
Además, esta clase contiene las API que puede usar para recorrer el foco en los
elementos relacionados.
Eventos: ContentElement incluye los eventos relacionados con la entrada y el foco;
también incluye eventos para los cambios de estado. En muchos casos, los eventos
ContentElement son eventos enrutados. En algunos casos, los eventos enrutados
tienen estrategias de enrutamiento de tunelización y propagación, que se generan
como eventos independientes en respuesta al mismo estado o condición. Además,
ContentElement define las API que pueden generar eventos enrutados y que pueden
agregar o quitar controladores de eventos.

133

133

ContentElement comparte muchas API comunes con UIElement. Estas API


comunes no provienen de una herencia de clases compartidas. Pero comparten
nombres comunes, comportamiento similar e implementación interna similar
de las API en cada clase. La similitud se debe a que ContentElement y
UIElement son clases que son una clase base de elementos, aunque cada una
tiene intenciones diferentes para su comportamiento del modelo de objetos de
marcado.
En concreto, UIElement desciende de Visual, que proporciona la compatibilidad
con gráficos de nivel inferior para representar un ContentElement en una región
rectangular dentro de una ventana compuesta, mientras que ContentElement
aplaza la representación para que los conceptos más comunes a los escenarios
de documentos, como Flow y wrapping, se admitan más fácilmente. Estas dos
clases relacionadas también implementan las interfaces comunes
IInputElement y IAnimatable .

134

134

67
1/9/2023

✓ FRAMEWORKCONTENTELEMENT
FrameworkContentElement es la implementación de nivel de marco de WPF y la
expansión de la clase base ContentElement. FrameworkContentElement agrega
compatibilidad para las API de entrada adicionales (incluye información sobre
herramientas y menús contextuales), guiones gráficos, el contexto de datos para
el enlace de datos, la compatibilidad de estilos y las API del asistente del árbol
lógico.
[System.Windows.Markup.RuntimeNameProperty("Name")]
[System.Windows.Markup.UsableDuringInitialization(true)]
[System.Windows.Markup.XmlLangProperty("Language")]
[System.Windows.StyleTypedProperty(Property="FocusVisualStyle",
StyleTargetType=typeof(System.Windows.Controls.Control))] public
class FrameworkContentElement : System.Windows.ContentElement,
System.ComponentModel.ISupportInitialize,
System.Windows.IFrameworkInputElement,
System.Windows.Markup.IQueryAmbient

135

135

2.2.3. DEPENDENCYOBJECT
La clase DependencyObject representa un objeto que forma parte del sistema
de propiedades de dependencia.
[System.Windows.Markup.NameScopeProperty("NameScope",
typeof(System.Windows.NameScope))] public class DependencyObject :
System.Windows.Threading.DispatcherObject

La clase DependencyObject habilita los servicios WPF del sistema de


propiedades en sus muchas clases derivadas. La función principal del sistema de
propiedades es calcular los valores de las propiedades y proporcionar
notificaciones del sistema sobre los valores que han cambiado. Otra clase clave
que participa en el sistema de propiedades es DependencyProperty .
DependencyProperty habilita el registro de propiedades de dependencia en el
sistema de propiedades y proporciona identificación e información sobre cada
propiedad de dependencia, mientras que, DependencyObject como una clase
base, permite que los objetos usen las propiedades de dependencia.
136

136

68
1/9/2023

DependencyObject incluye entre sus servicios y características los siguientes:


Compatibilidad con el hospedaje de propiedades de dependencia. Para registrar una
propiedad de dependencia, se debe llamar al método Register y almacenar el valor
devuelto del método como un campo estático público en la clase.
Compatibilidad con el hospedaje de propiedades adjuntas. Para registrar una propiedad
adjunta, se llama al método RegisterAttached y se almacena el valor devuelto del
método como un campo público estático de solo lectura en la clase. (También hay
requisitos de miembros adicionales; tenga en cuenta que esto representa una
implementación WPF específica para las propiedades adjuntas. La propiedad adjunta se
puede establecer en cualquier clase que se derive de DependencyObject .
Obtener, establecer y borrar métodos de utilidad para los valores de las propiedades de
dependencia que existen en DependencyObject .
Metadatos, compatibilidad con la conversión de valores, notificación de cambio de
propiedad y devoluciones de llamada de invalidación para propiedades de dependencia
o propiedades adjuntas. Además, la clase DependencyObject facilita los metadatos de
propiedad por propietario para una propiedad de dependencia.
Clase base común para las clases derivadas de ContentElement , Freezable o Visual . (
UIElement , otra clase de elemento base, tiene una jerarquía de clases que incluye
Visual).
137

137

ARQUITECTURA WPF
La arquitectura de WPF representa la jerarquía de clases de Windows
Presentation Foundation (WPF). Abarca la mayoría de los subsistemas
principales de WPF y describe cómo interactúan.

✓ SYSTEM.OBJECT
El modelo principal de programación de WPF se expone a través del código
administrado. Al principio de la fase de diseño de WPF, había una serie de
debates sobre dónde se debe dibujar la línea entre los componentes
administrados del sistema y los no administrados. CLR proporciona una serie de
características que hacen que el desarrollo sea más productivo y robusto
(incluida la administración de la memoria, el control de errores, el sistema de
tipos común, etc.), pero tienen un costo.

138

138

69
1/9/2023

Las secciones rojas del diagrama


(PresentationFramework, PresentationCore y
Milcore) son las partes principales del código
de WPF. De estos, solo uno es un componente
no administrado: Milcore. Milcore está escrito
en código no administrado con el fin de
permitir una estrecha integración con DirectX.
Todas las pantallas en WPF se realizan a través
del motor de DirectX, lo que permite una eficaz
representación de software y hardware. WPF
también requiere un control preciso sobre la
memoria y la ejecución. El motor de
composición de Milcore es sumamente
sensible al rendimiento y se requiere que se
ofrezcan muchas ventajas de CLR para
obtener rendimiento.
139

139

1.3. EVENTOS WPF

140

70
1/9/2023

Eventos WPF
La mayoría de los marcos (frameworks) de UI modernos al igual que WPF, están
impulsados por eventos. Todos los controles, incluida la ventana (Window) (que
también hereda de la clase control) exponen un rango de eventos a los que es
posible suscribirse, lo que significa que la aplicación será notificada cuando
ocurran y podrá reaccionar a ellos.
Hay muchos tipos de eventos, pero algunos de los más comúnmente utilizados
están hechos para responder a la interacción del usuario con su aplicación, con el
ratón o el teclado. En la mayoría de los controles se encuentran eventos como
KeyDown, KeyUp, MouseDown, MouseEnter, MouseLeave, MouseUp y varios otros.

141

141

<Grid>
<Button Padding="5“Click="Button_Click">
<StackPanel Orientation="Horizontal">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE
Programacion II\Iconos\search_14765.png" Height="15"/>
<TextBlock Margin="5,0">Buscar</TextBlock>
</StackPanel>
</Button>
</Grid>

private void Button_Click(object sender, RoutedEventArgs e)


{
MessageBox.Show("Hello, WPF!");
}

142

142

71
1/9/2023

EVENTOS ENRUTADOS
Para entender lo que representan los eventos enrutados se pueden ver desde una
perspectiva funcional o de implementación.
Definición funcional: un evento enrutado es un tipo de evento que puede invocar
controladores en múltiples oyentes en un árbol de elementos, en lugar de solo en
el objeto que generó el evento.
Definición de implementación: un evento enrutado es un evento CLR (Common
Language Runtime) respaldado por una instancia de la clase RoutedEvent y
procesado por el sistema de eventos de Windows Presentation Foundation
(WPF).
Una aplicación típica de WPF contiene muchos elementos. Ya sea que se creen en
código o se declaren en XAML, estos elementos existen en una relación de árbol de
elementos entre sí. La ruta del evento puede viajar en una de dos direcciones
dependiendo de la definición del evento, pero generalmente la ruta viaja desde el
elemento de origen y luego burbujea hacia arriba a través del árbol de elementos
hasta que alcanza la raíz del árbol de elementos (generalmente una página o una
ventana) . 143

143

Si analizamos el siguiente código:


<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">
<StackPanel Background="LightGray" Orientation="Horizontal“
Button.Click=“ControladorComun“ >
<Button Name=“BotonSi" Width="Auto" >Si</Button>
<Button Name=“BotonNO" Width="Auto" >No</Button>
<Button Name=“BotonCancel" Width="Auto" >Cancelar</Button>
</StackPanel>
</Border>
En este árbol de elementos simple, el origen de
un evento Click es uno de los elementos botón, y
el botón en el que se hizo clic es el primer
elemento que tiene la oportunidad de manejar el
evento. Pero si ningún controlador adjunto al
Button actúa sobre el evento, entonces el
evento burbujeará hacia arriba hasta el padre
Button en el árbol de elementos, que es el
En otras palabras, la ruta del
evento para este evento Click
StackPanel. Potencialmente, el evento se
es: propaga a Border y luego a la raíz de la página del
Button -> StackPanel -> Border - árbol de elementos (no se muestra).
144
> ...
144

72
1/9/2023

➢ LOS MEJORES ESCENARIOS PARA UTILIZAR EVENTOS


ENRUTADOS
✓ CONTROL DE COMPOSICIÓN Y ENCAPSULACIÓN
Varios de los controles utilizados en WPF tienen un modelo de contenido
enriquecido. Por ejemplo, se puede colocar una imagen dentro de un botón, lo
que extiende efectivamente el árbol visual del botón. Sin embargo, la imagen
agregada no debe romper el comportamiento de prueba de aciertos que hace
que un botón responda a un clic de su contenido, incluso si el usuario hace clic
en píxeles que son técnicamente parte de la imagen.
✓ PUNTOS DE CONEXIÓN DE CONTROLADOR SINGULARES
En Windows Forms, se tendría que adjuntar el mismo controlador varias veces
para procesar eventos que podrían generarse a partir de varios elementos. Los
eventos enrutados permiten adjuntar ese controlador solo una vez.

145

145

private void ControladorComun(object sender, RoutedEventArgs e)


{
FrameworkElement feSource = e.Source as FrameworkElement;
switch (feSource.Name)
{
case “BotonSI":
// código...
break;
case “BotonNo":
// código...
break;
case “BotonCancel":
// código ...
break;
}
e.Handled=true;
}

146

146

73
1/9/2023

✓ MANEJO DE CLASES
Los eventos enrutados permiten un manejador estático definido por la clase.
Este controlador de clase tiene la oportunidad de manejar un evento antes que
cualquier controlador de instancia adjunto.
✓ HACER REFERENCIA A UN EVENTO SIN REFLEXIÓN
Ciertos códigos y técnicas de marcado requieren una forma de identificar un
evento específico. Un evento enrutado crea un campo RoutedEvent como
identificador, que proporciona una técnica sólida de identificación de eventos
que no requiere reflexión estática en tiempo de ejecución.

147

147

EVENTOS ADJUNTOS
El lenguaje XAML, define un componente de lenguaje y un tipo de evento
llamado evento adjunto. El concepto de evento adjunto permite agregar un
controlador para un evento en particular a un elemento arbitrario en lugar de a
un elemento específico. En este caso, ni el objeto que potencialmente genera el
evento ni la instancia de manejo de destino define o posee el evento.
Los eventos adjuntos tienen una sintaxis XAML y un patrón de codificación que
debe usar el código de respaldo para admitir el uso del evento adjunto.
En la sintaxis XAML, el evento adjunto se especifica no solo por su nombre de
evento, sino por su tipo propietario más el nombre del evento, separados por un
punto (.). Debido a que el nombre del evento se establece con el nombre de su
tipo propietario, la sintaxis del evento adjunto permite aplicar cualquier evento
adjunto a cualquier elemento del que se pueda crear una instancia.

148

148

74
1/9/2023

En WPF, los eventos adjuntos están respaldados por un campo RoutedEvent


y se enrutan a través del árbol una vez que se generan. Normalmente, la
fuente del evento adjunto (el objeto que genera el evento) es una fuente de
sistema o servicio, y el objeto que ejecuta el código que genera el evento, por
lo tanto, no es una parte directa del árbol de elementos.

<Grid Button.Click="botones_Click">
<Button Name="button1" Content="Botón 1" Height="25" Margin="0,10,0,81"/>
<Button Name="button2" Content="Botón 2" Height="25" Margin="0,40,0,51"/>
<Button Name="button3" Content="Botón 3" Height="25" Margin="0,70,0,21"/>
</Grid>

private void botones_Click(object sender, RoutedEventArgs e)


{
MessageBox.Show("Control '" + e.Source + "' pulsado!");
Console.Beep(1000, 100);
}
149

149

El proceso para manejar un evento adjunto y el código del controlador que


escribirá es básicamente el mismo que para un evento enrutado.
En general, un evento adjunto de WPF no es muy diferente de un evento
enrutado. Las diferencias son cómo se origina el evento y cómo lo expone una
clase como miembro (lo que también afecta a la sintaxis del controlador XAML).
Sin embargo, los eventos adjuntos de WPF existentes no están especialmente
pensados para su manejo en WPF. Más a menudo, el propósito del evento es
permitir que un elemento compuesto informe un estado a un elemento principal
en la composición, en cuyo caso el evento generalmente se genera en el código
y también se basa en el manejo de clases en la clase principal relevante. Por
ejemplo, se espera que los elementos dentro de un Selector generen el evento
Selected adjunto, que luego es la clase manejada por la clase Selector y luego
potencialmente convertida por la clase Selector en un evento enrutado
diferente, SelectionChanged.

150

150

75
1/9/2023

EVENTOS DE VIDA DE OBJETOS


Todos los objetos en el código administrado de Microsoft .NET Framework
pasan por un conjunto similar de etapas de vida, creación, uso y
destrucción. Muchos objetos también tienen una etapa de finalización de la vida
que ocurre como parte de la fase de destrucción. Los objetos de WPF, más
específicamente los objetos visuales que WPF identifica como elementos,
también tienen un conjunto de etapas comunes de la vida del objeto. Los
modelos de aplicación y programación de WPF exponen estas etapas como una
serie de eventos. Hay cuatro tipos principales de objetos en WPF con respecto
a los eventos de por vida; elementos en general, elementos de ventana, hosts
de navegación y objetos de aplicación. Las ventanas y los hosts de navegación
también se encuentran dentro de la agrupación más grande de objetos visuales
(elementos).
Cualquier elemento de nivel de marco de WPF (los objetos que se derivan de
FrameworkElement o FrameworkContentElement ) tiene tres eventos de
duración comunes: inicializado , cargado y descargado . 151

151

Initialized se genera primero y corresponde aproximadamente a la


inicialización del objeto mediante la llamada a su constructor. Debido a que
el evento ocurre en respuesta a la inicialización, se le garantiza que todas las
propiedades del objeto están configuradas. (Una excepción son los usos de
expresión como recursos dinámicos o enlace; estos serán expresiones no
evaluadas). Como consecuencia del requisito de que todas las propiedades
estén establecidas, la secuencia de Initialized al ser generada por elementos
anidados que están definidos en el marcado parece ocurrir primero en el
orden de los elementos más profundos en el árbol de elementos, luego los
elementos principales hacia la raíz. Este orden se debe a que las relaciones
padre-hijo y la contención son propiedades y, por lo tanto, el padre no puede
informar de la inicialización hasta que los elementos secundarios que llenan
la propiedad también estén completamente inicializados. Cuando escribe
controladores en respuesta al evento Initialized , se debe tener en cuenta
que no hay garantía de que se hayan creado todos los demás elementos del
árbol de elementos (árbol lógico o árbol visual) alrededor de donde se
adjunta el controlador, en particular los elementos principales. 152

152

76
1/9/2023

Loaded se eleva a continuación. El evento Loaded se genera antes del


renderizado final, pero después de que el sistema de diseño ha calculado
todos los valores necesarios para el renderizado. Loaded implica que el árbol
lógico en el que está contenido un elemento está completo y se conecta a
una fuente de presentación que proporciona el HWND y la superficie de
representación. El enlace de datos estándar (enlace a fuentes locales, como
otras propiedades o fuentes de datos definidas directamente) se habrá
producido antes de Loaded . Es posible que se haya producido un enlace de
datos asincrónico (fuentes externas o dinámicas), pero por definición de su
naturaleza asincrónica no se puede garantizar que haya ocurrido. El
mecanismo por el cual se genera el evento Loaded es diferente al de
Initialized . El evento Initialized se genera elemento por elemento, sin una
coordinación directa de un árbol de elementos completo. Por el contrario, el
evento Loaded se genera como un esfuerzo coordinado en todo el árbol de
elementos (específicamente, el árbol lógico). Cuando todos los elementos
del árbol están en un estado en el que se consideran cargados, el evento
Loaded se genera primero en el elemento raíz. A continuación, el evento
153
Loaded se genera sucesivamente en cada elemento secundario.
153

Unloaded se genera en último lugar y lo inicia la fuente de presentación o el


elemento principal visual que se elimina. Cuando se genera y maneja
Unloaded, el elemento que es el padre de origen del evento (según lo
determinado por la propiedad Parent) o cualquier elemento dado hacia
arriba en los árboles lógicos o visuales puede que ya se haya desarmado, lo
que significa que el enlace de datos, las referencias de recursos y los estilos
pueden no debe establecerse en su valor de tiempo de ejecución normal o
en el último conocido.

154

154

77
1/9/2023

EVENTOS DE VISTA PRELIMINAR


Los eventos de vista previa, también conocidos como eventos de túnel, son
eventos enrutados donde la dirección de la ruta viaja desde la raíz de la
aplicación hacia el elemento que generó el evento y se informa como la fuente
en los datos del evento. No todos los escenarios de eventos admiten o
requieren eventos de vista previa.
Cuando se manejan eventos de Vista previa en general, se debe tener cuidado al
marcar los eventos manejados en los datos de eventos. Manejar un evento de
vista previa en cualquier elemento que no sea el elemento que lo generó (el
elemento que se informa como el origen en los datos del evento) tiene el efecto
de no brindarle al elemento la oportunidad de manejar el evento que se originó.
A veces, este es el resultado deseado, especialmente si los elementos en
cuestión existen en relaciones dentro de la composición de un control.

155

155

Específicamente para eventos de entrada, los eventos Preview también


comparten instancias de datos de eventos con el evento burbujeante
equivalente. Si se usa un controlador de clase de evento Preview para marcar el
evento de entrada manejado, no se invocará el controlador de clase de evento de
entrada burbujeante. O, si usa un controlador de instancia de evento Preview
para marcar el evento manejado, normalmente no se invocarán controladores
para el evento de propagación. Los manejadores de clase o de instancia se
pueden registrar o adjuntar con una opción para ser invocada incluso si el evento
está marcado como manejado, pero esa técnica no se usa comúnmente.

156

156

78
1/9/2023

EVENTOS DE CAMBIOS DE PROPIEDADES


Windows Presentation Foundation (WPF) define varios eventos que se generan
en respuesta a un cambio en el valor de una propiedad. A menudo, la propiedad
es una propiedad de dependencia. El evento en sí es a veces un evento
enrutado y, a veces, un evento estándar de Common Language Runtime (CLR).
La definición del evento varía según el escenario, porque algunos cambios de
propiedad se enrutan más apropiadamente a través de un árbol de elementos,
mientras que otros cambios de propiedad generalmente solo afectan al objeto
donde cambió la propiedad.

157

157

1.4. ESTILOS Y PLANTILLAS

158

79
1/9/2023

Estilos WPF
En WPF, un estilo define los valores de una o más propiedades de dependencia
para un elemento visual dado. Los estilos se utilizan en toda la aplicación para
hacer que la interfaz de usuario sea más consistente (por ejemplo, para que todos
los botones de diálogo tengan un tamaño uniforme) y para hacer que los cambios
masivos sean más fáciles (por ejemplo, para cambiar el ancho de todos los
botones).
Normalmente, los estilos se definen en un ResourceDictionary a un alto nivel en la
aplicación (por ejemplo, en App.xaml o en un tema), por lo que está disponible
para toda la aplicación, pero también pueden definirse para un solo elemento y
sus elementos secundarios , por ejemplo, aplicar un Estilo a todos los elementos
TextBlock dentro de un StackPanel .

159

159

<StackPanel Margin="10">
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Gray" />
<Setter Property="FontSize" Value="24" />
</Style>
</StackPanel.Resources>
<TextBlock>Texto 1</TextBlock>
<TextBlock>Texto 2</TextBlock>
<TextBlock Foreground="Blue">Texto 3</TextBlock>
</StackPanel>

La ubicación donde se define el estilo afecta a donde está disponible.


Las referencias StaticResource no pueden ser resueltas por StaticResource . En otras
palabras, si está definiendo un estilo que depende de otro estilo o recurso en un
diccionario de recursos, debe definirse después / debajo del recurso del cual depende.
StaticResource es la forma recomendada de hacer referencia a estilos y otros recursos
(por razones de rendimiento y comportamiento) a menos que requiera específicamente el
uso de DynamicResource , por ejemplo, para temas que se pueden cambiar en tiempo de
ejecución.
160

160

80
1/9/2023

TIPOS DE ESTILOS WPF


➢ ESTILO CON NOMBRE
Un estilo con nombre requiere que se establezca la propiedad x:Key y se aplica
solo a los elementos que hacen referencia explícita por nombre:
<StackPanel>
<StackPanel.Resources>
<Style x:Key="EstiloTB" TargetType="TextBlock">
<Setter Property="Background" Value="Yellow"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
</StackPanel.Resources>

<TextBlock Text="Fondo amarillo y en negrita!" Style="{StaticResource EstiloTB}" />


<TextBlock Text="Fondo amarillo y en negrita!" Style="{DynamicResource EstiloTB}" />
<TextBlock Text="Texto plano sin estilo" />
</StackPanel>

161

161

➢ ESTILO IMPLÍCITO
Un estilo implícito se aplica a todos los elementos de un tipo dado dentro del
alcance. Un estilo implícito puede omitir x:Key ya que es implícitamente lo
mismo que la propiedad TargetType del estilo.
<StackPanel>
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Background" Value="Yellow"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
</StackPanel.Resources>

<TextBlock Text="Fondo amarillo y negrita!" />


<TextBlock Text="Fondo amarillo y negrita!" />
<TextBlock Style="{x:Null}" Text="TB con estilo por defecto!" />
</StackPanel>

162

162

81
1/9/2023

APLICACIONES DE ESTILOS WPF


➢ ESTILO ESPECÍFICO DE CONTROL LOCAL
Es posible definir estilos directamente en un control, de esta manera:
<TextBlock Text="Prueba de estilo">
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.FontSize" Value="36" />
</Style>
</TextBlock.Style>
</TextBlock>

163

163

➢ ESTILO LOCAL DE CONTROL HIJO


<StackPanel Margin="10">
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Gray" />
<Setter Property="FontSize" Value="24" />
</Style>
</StackPanel.Resources>
<TextBlock>Texto 1</TextBlock>
<TextBlock>Texto 2</TextBlock>
<TextBlock Foreground="Blue">Texto 3</TextBlock>
</StackPanel>

164

164

82
1/9/2023

➢ ESTILO DE VENTANA
<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Gray" />
<Setter Property="FontSize" Value="24" />
</Style>
</Window.Resources>
<StackPanel Margin="10">
<TextBlock>Texto 1</TextBlock>
<TextBlock>Texto 2</TextBlock>
<TextBlock Foreground="Blue">Texto 3</TextBlock>
</StackPanel>

165

165

➢ ESTILO ANCHO
Es posible aplicar los estilos en toda la aplicación, en diferentes ventanas. Esto
se hace en el archivo App.xaml que Visual Studio ha creado, y se hace como en
el ejemplo de toda la ventana:

<Application x:Class="Aula1WPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Aula1WPF"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Gray" />
<Setter Property="FontSize" Value="24" />
</Style>
</Application.Resources>
</Application>

166

166

83
1/9/2023

<StackPanel Margin="10">
<TextBlock>Texto 1</TextBlock>
<TextBlock>Texto 2</TextBlock>
<TextBlock Foreground="Blue">Texto 3</TextBlock>
</StackPanel>

167

167

➢ ESTILO EXPLÍCITO

<Window.Resources>
<Style x:Key="EstiloTexto" TargetType="TextBlock">
<Setter Property="Foreground" Value="Gray" />
<Setter Property="FontSize" Value="24" />
</Style>
</Window.Resources>
<StackPanel Margin="10">
<TextBlock>Texto 1</TextBlock>
<TextBlock Style="{StaticResource EstiloTexto}">Texto 2</TextBlock>
<TextBlock>Texto 3</TextBlock>
</StackPanel>

168

168

84
1/9/2023

TRIGGERS (DISPARADORES)
Hasta el momento, se ha trabajado con estilos estableciendo valores estáticos
en una propiedad determinada. Sin embargo, mediante el uso de disparadores
(Triggers), se puede modificar los valores de las propiedades ante cierta
condición. Existe varios tipos de triggers: de propiedad, de evento, y de datos.
Los triggers permiten realizar tareas que normalmente se realizarían en code
behind, como parte del proceso constante de separar la presentación de la
lógica de negocios.

169

169

➢ TRIGGERS DE PROPIEDAD
El tipo más común de trigger es el de propiedad, el cual, simplemente se define
con un elemento <Trigger>. Este tipo de trigger observa a una propiedad dada
en el control contenedor, y cuando esa propiedad toma un valor determinado, la
propiedad cambia.
<Grid>
<TextBlock Text="Hola, mundo con estilo!" FontSize="28"
HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Blue"></Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red" />
<Setter Property="TextDecorations" Value="Underline" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
170
</Grid>

170

85
1/9/2023

➢ TRIGGERS DE DATOS
Los triggers de datos, que se representan mediante el elemento <DataTrigger>,
se usan para propiedades que no necesariamente son propiedades de
dependencia. Funcionan mediante la creación de un binding a una propiedad
corriente, la cual se monitorea después en espera de cambios. Esto también
abre la posibilidad de vincular el trigger a una propiedad de otro control distinto.

171

171

✓ BINDING

Data binding es un mecanismo mediante el cual es posible enlazar los elementos de la


interfaz de usuario con los objetos que contienen la información a mostrar. El caso más
típico de data binding es el enlazar un control de la interfaz de usuario con un valor o registro
de una base de datos.
Las posibilidades que brinda el data binding en WPF para conseguir interfaces dinámicas y
mucho más ricas en contenido son mucho mayores que las posibilidades en Windows
Forms. Ahora es posible modificar el contenido de un control al modificar otro sin tener que
escribir código extra en eventos, todo esto y mucho más lo hará el binding por nosotros.
WPF permite de manera sencilla realizar binding a una propiedad de un control utilizando
propiedades de otros controles, objetos, colecciones, etc.
172

172

86
1/9/2023

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">


<CheckBox Name="cbPrueba" Content="Saludo?" />
<TextBlock HorizontalAlignment="Center" Margin="0,20,0,0"
FontSize="48">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="No" />
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=cbPrueba,
Path=IsChecked}" Value="True">
<Setter Property="Text" Value="Si!" />
<Setter Property="Foreground" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>

173

173

➢ TRIGGERS DE EVENTOS
Los triggers de evento, que se representan mediante el elemento
<EventTrigger> se usan mayormente para disparar una animación en respuesta
a un evento.

174

174

87
1/9/2023

<Grid> </EventTrigger>
<TextBlock Name="lblEstilo" Text="Hola, <EventTrigger
mundo con estilos!" FontSize="18" RoutedEvent="MouseLeave">
HorizontalAlignment="Center"
VerticalAlignment="Center"> <EventTrigger.Actions>
<TextBlock.Style>
<Style TargetType="TextBlock"> <BeginStoryboard>
<Style.Triggers>
<EventTrigger <Storyboard>
RoutedEvent="MouseEnter">
<DoubleAnimation Duration="0:0:0.800"
<EventTrigger.Actions> Storyboard.TargetProperty="FontSize" To="18" />

<BeginStoryboard> </Storyboard>

<Storyboard> </BeginStoryboard>

<DoubleAnimation Duration="0:0:0.300" </EventTrigger.Actions>


Storyboard.TargetProperty="FontSize" To="28" /> </EventTrigger>
</Style.Triggers>
</Storyboard> </Style>
</TextBlock.Style>
</BeginStoryboard> </TextBlock>
</Grid>
</EventTrigger.Actions>
175

175

➢ DISPARADORES MÚLTIPLES
Se pudo ver disparadores basados en una sola propiedad, pero WPF también es
compatible con múltiples disparadores, que pueden monitorear dos o más
condiciones de propiedad y solo desencadenarse una vez que todas se
cumplan.
Hay dos tipos de disparadores múltiples: el MultiTrigger , que al igual que el
activador normal funciona en las propiedades de dependencia, y luego
el MultiDataTrigger, que se vincula a cualquier tipo de propiedad.

176

176

88
1/9/2023

➢ MULTITRIGGER
<Grid>
<TextBox VerticalAlignment="Center" HorizontalAlignment="Center" Text="Haz clic aqui"
Width="150">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsKeyboardFocused" Value="True" />
<Condition Property="IsMouseOver" Value="True" />
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Background" Value="LightGreen" />
</MultiTrigger.Setters>
</MultiTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>

177

177

➢ MULTIDATATRIGGER
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<CheckBox Name="cbSi" Content="Si" />
<CheckBox Name="cbSeguro" Content="Estoy seguro" />
<TextBlock HorizontalAlignment="Center" Margin="0,20,0,0" FontSize="28">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="Sin verificar" />
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=cbSi, Path=IsChecked}"
Value="True" />
<Condition Binding="{Binding ElementName=cbSeguro,
Path=IsChecked}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="Text" Value="Verificado" />
<Setter Property="Foreground" Value="Green" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock> 178
</StackPanel>

178

89
1/9/2023

Plantillas WPF
Una plantilla describe el aspecto general y el aspecto visual de un control. Para cada
control, hay una plantilla predeterminada asociada que le da al control su apariencia.
En las aplicaciones de WPF, es posible crear fácilmente plantillas propias cuando se
desee personalizar el comportamiento visual y la apariencia visual de un control. La
conectividad entre la lógica y la plantilla se puede lograr mediante el enlace de datos.
Las principales diferencias entre estilos y plantillas son:
Los estilos solo pueden cambiar la apariencia de su control con las propiedades
predeterminadas de ese control.
Con las plantillas, puede acceder a más partes de un control que en estilos.
También puede especificar el comportamiento nuevo y existente de un control.
Hay dos tipos de plantillas que se utilizan con más frecuencia:
Plantilla de control
Plantilla de datos
179

179

➢ PLANTILLA DE CONTROL
La plantilla de control define la apariencia visual de un control. Todos los
elementos de la interfaz de usuario tienen algún tipo de apariencia y
comportamiento, por ejemplo, Button tiene apariencia y comportamiento. El
evento de clic o el evento de desplazamiento del mouse son los
comportamientos que se activan en respuesta a un clic y al pasar el mouse, y
también hay una apariencia predeterminada del botón que se puede cambiar
mediante la plantilla de Control.

180

180

90
1/9/2023

<Window.Resources>
<ControlTemplate x:Key = "PlantillaBoton" TargetType = "Button">
<Grid>
<Ellipse x:Name = "Elipse" Height = "100" Width = "150" >
<Ellipse.Fill>
<LinearGradientBrush StartPoint = "0,0.2" EndPoint = "0.2,1.4">
<GradientStop Offset = "0" Color = "Red" />
<GradientStop Offset = "1" Color = "Orange" />
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<ContentPresenter Content = "{TemplateBinding Content}" HorizontalAlignment = "Center" VerticalAlignment = "Center" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property = "IsMouseOver" Value = "True">
<Setter TargetName = "Elipse" Property = "Fill" >
<Setter.Value>
<LinearGradientBrush StartPoint = "0,0.2" EndPoint = "0.2,1.4">
<GradientStop Offset = "0" Color = "YellowGreen" />
<GradientStop Offset = "1" Color = "Gold" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property = "IsPressed" Value = "True">
<Setter Property = "RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX = "0.8" ScaleY = "0.8" CenterX = "0" CenterY = "0" />
</Setter.Value>
</Setter>
<Setter Property = "RenderTransformOrigin" Value = "0.5,0.5" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources> 181

181

<StackPanel>
<Button Content = "Boton circular!“ Template = "{StaticResource PlantillaBoton}"
Width = "150" Margin = "50" />
<Button Content = "Boton por defecto!" Height = "40 Width = "150" Margin = "5" />
</StackPanel>

182

182

91
1/9/2023

➢ PLANTILLA DE DATOS
Una plantilla de datos define y especifica la apariencia y estructura de una
colección de datos. Proporciona la flexibilidad de formatear y definir la
presentación de los datos en cualquier elemento de la interfaz de usuario. Se
utiliza principalmente en controles de elementos relacionados con datos, como
ComboBox, ListBox, etc.

183

183

<Window.Resources> Color = "Gold" />


<ControlTemplate x:Key = "PlantillaBoton" </LinearGradientBrush>
TargetType = "Button"> </Setter.Value>
<Grid> </Setter>
<Ellipse x:Name = "Elipse" Height = "100" </Trigger>
Width = "150" > <Trigger Property = "IsPressed" Value =
<Ellipse.Fill> "True">
<LinearGradientBrush StartPoint = <Setter Property = "RenderTransform">
"0,0.2" EndPoint = "0.2,1.4"> <Setter.Value>
<GradientStop Offset = "0" <ScaleTransform ScaleX = "0.8"
Color = "Red" /> ScaleY = "0.8" CenterX = "0" CenterY = "0" />
<GradientStop Offset = "1" </Setter.Value>
Color = "Orange" /> </Setter>
</LinearGradientBrush> <Setter Property =
</Ellipse.Fill> "RenderTransformOrigin" Value = "0.5,0.5" />
</Ellipse> </Trigger>
<ContentPresenter Content = </ControlTemplate.Triggers>
"{TemplateBinding Content}" HorizontalAlignment = "Center" </ControlTemplate>
VerticalAlignment = "Center" /> </Window.Resources>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property = "IsMouseOver" Value =
"True">
<Setter TargetName = "Elipse" Property
= "Fill" >
<Setter.Value>
<LinearGradientBrush
StartPoint = "0,0.2" EndPoint = "0.2,1.4">
<GradientStop Offset = "0"
Color = "YellowGreen" /> 184
<GradientStop Offset = "1"

184

92
1/9/2023

public partial class MainWindow : Window


{
Person src = new Person { Name = "Ali", Age = 27 };
List<Person> people = new List<Person>();
public MainWindow()
{
InitializeComponent();
people.Add(src);
people.Add(new Person { Name = "Mike", Age = 62 });
people.Add(new Person { Name = "Brian", Age = 12 });
this.DataContext = people;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
string message = src.Name + " is " + src.Age;
MessageBox.Show(message);
}
}
public class Person
{
private string nameValue;

public string Name


{
get { return nameValue; }
set { nameValue = value; }
}
private double ageValue;

public double Age


{
get { return ageValue; }
set
{
if (value != ageValue)
{
ageValue = value;
}
}
} 185
}

185

186

186

93
1/9/2023

➢ DICCIONARIO DE RECURSOS
Los recursos pueden establecerse como definiciones relacionadas con algún objeto
que se usarán más de una vez. ”Es la capacidad de almacenar datos localmente para
controles o para la ventana actual o globalmente para todas las aplicaciones”.
Definir un objeto como recurso permite acceder a él desde otro lugar. Lo que
significa que el objeto se puede reutilizar. Los recursos se definen en diccionarios de
recursos y cualquier objeto se puede definir como un recurso, lo que lo convierte en
un activo que se puede compartir. Se especifica una clave única para un recurso
XAML y, con esa clave, se puede hacer referencia a ella mediante una extensión de
marcado StaticResource. Los recursos pueden ser de dos tipos:
StaticResource
DynamicResource
Un StaticResource es una búsqueda única, mientras que un DynamicResource
funciona más como un enlace de datos. Se debe recordar que una propiedad está
asociada con una clave de recurso en particular. Si el objeto asociado con esa clave
cambia, el recurso dinámico actualizará la propiedad de destino.
187

187

<Window.Resources>
<SolidColorBrush x:Key = "Recurso1" Color = "Bisque" />
</Window.Resources>

<StackPanel>
<Rectangle Height = "50" Margin = "20" Fill = "{StaticResource Recurso1}" />
<Rectangle Height = "50" Margin = "20" Fill = "{DynamicResource Recurso1}" />

</StackPanel>

188

188

94
1/9/2023

✓ ALCANCE DE LOS RECURSOS


Los recursos se definen en diccionarios de recursos , pero hay numerosos
lugares donde se puede definir un diccionario de recursos.

189

189

✓ DICCIONARIO DE RECURSOS
Los diccionarios de recursos en aplicaciones XAML implican que los
diccionarios de recursos se guardan en archivos separados. Se sigue en casi
todas las aplicaciones XAML. La definición de recursos en archivos separados
puede tener las siguientes ventajas:
Separación entre la definición de recursos en el diccionario de recursos y el
código relacionado con la interfaz de usuario.
La definición de todos los recursos en un archivo separado, como App.xaml,
los haría disponibles en toda la aplicación.

190

190

95
1/9/2023

<SolidColorBrush x:Key = “Recurso1" Color = "Blue" />

<Application.Resources> <ResourceDictionary Source =


"Recursos\DictionarioPrueba.xaml"/>
</Application.Resources>

<StackPanel>
<Rectangle Height = "50" Margin = "20" Fill="{StaticResource Recurso1}"/>
<Rectangle Height = "50" Margin = "20" Fill="{DynamicResource Recurso1}"/>

</StackPanel>

191

191

1.6. CONTROLES MENÚ Y


BARRAS DE
HERRAMIENTAS

192

96
1/9/2023

Enlazar Comandos
Realmente, los comandos no hacen nada por sí mismos. En esencia consisten en
la interfaz ICommand, que únicamente define un evento y dos métodos: Execute()
y CanExecute(). El primero se encarga de realizar la acción, mientras que el
segundo determina si la acción está disponible. Para lograr realizar la acción
asociada al comando, se necesita enlazar el comando con el código que define la
acción, y aquí es donde entra en juego el CommandBinding.
Un CommandBinding se define típicamente en Window o en un UserControl, y
contiene una referencia al comando que controla, así como los controladores
para tratar los eventos Execute() y CanExcute() del comando.

193

193

➢ COMANDOS PREDEFINIDOS
Obviamente es posible implementar comandos propios, pero por facilidad, WPF
trae predefinidos más de 100 comandos típicos que son necesarios. Éstos se
dividen en 5 categorías: ApplicationCommands, NavigationCommands,
MediaCommands, EditingCommands y ComponentCommands. En especial
ApplicationCommands contiene comandos para multitud de acciones muy
comunes como Nuevo, Abrir, Guardar, Cortar, Copiar o Pegar.

194

194

97
1/9/2023

<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New"
Executed="NewCommand_Executed" CanExecute="NewCommand_CanExecute" />
</Window.CommandBindings>

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">


<Button Command="ApplicationCommands.New">_Nuevo</Button>
</StackPanel>

public partial class MainWindow : Window


{
private void NewCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("El comand New fue invocado");
}
public MainWindow()
{
InitializeComponent();
}
195
}

195

Se enlaza el comando en Window, añadiéndolo a su colección


CommandBindings. Se especifica el comando que se desea usar (el
comando New contenido en ApplicationCommands), así como dos
controladores de evento. La interfaz visual se compone de un único botón, al
que se asocia el comando mediante la propiedad Command.
En el código subyacente se controla los dos eventos. El controlador de
CanExecute, al que WPF llamará cuando la aplicación quiera comprobar si el
comando está disponible, es muy simple en este ejemplo, ya que se quiere
que siempre esté disponible. Esto se consigue igualando la propiedad
CanExecute del evento a true.
El controlador de Executed simplemente muestra un mensaje cuando se
invoca el comando. Si se ejecuta la aplicación y se pulsa el botón se verá un
mensaje. Un detalle a tener en cuenta es que el comando lleva asociado un
atajo de teclado por defecto Ctrl+N en el teclado y verás que el resultado es
el mismo.
196

196

98
1/9/2023

➢ COMANDO CANEXECUTE
Al implementar un evento CanExecute que sólo devuelve verdadero, el botón
estaría disponible todo el tiempo. Sin embargo, esto, por supuesto, no es cierto
para todos los botones; en muchos casos, se desea que el botón se habilite o
deshabilite dependiendo de algún tipo de estado de la aplicación.
Un ejemplo muy común de esto es alternar botones para usar el Portapapeles de
Windows, donde se desea que los botones Cortar y Copiar estén habilitados solo
cuando se selecciona texto, y el botón Pegar solo se habilita cuando el texto
está presente en el portapapeles.

197

197

<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Cut"
CanExecute="CutCommand_CanExecute" Executed="CutCommand_Executed" />
<CommandBinding Command="ApplicationCommands.Paste"
CanExecute="PasteCommand_CanExecute" Executed="PasteCommand_Executed" />
</Window.CommandBindings>
<DockPanel>
<WrapPanel DockPanel.Dock="Top" Margin="3">
<Button Command="ApplicationCommands.Cut"
Width="60">_Cortar</Button>
<Button Command="ApplicationCommands.Paste" Width="60"
Margin="3,0">_Pegar</Button>
</WrapPanel>
<TextBox AcceptsReturn="True" Name="txtEditor" />
</DockPanel>

198

198

99
1/9/2023

private void CutCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)


{
e.CanExecute = (txtEditor != null) && (txtEditor.SelectionLength > 0);
}

private void CutCommand_Executed(object sender, ExecutedRoutedEventArgs e)


{
txtEditor.Cut();
}

private void PasteCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)


{
e.CanExecute = Clipboard.ContainsText();
}

private void PasteCommand_Executed(object sender, ExecutedRoutedEventArgs e)


{
txtEditor.Paste();
}

199

199

Se tiene esta interfaz muy simple con un par de botones y un control TextBox. El
primer botón se cortará en el portapapeles y el segundo pegará desde allí.

En el Código adyacente (Code-behind), se tienen dos eventos por cada botón:


uno que realiza la acción real, cuyo nombre termina con _Executed, y luego el
evento CanExecute. En cada uno de ellos, se verá que se aplica alguna lógica
para decidir cuando una acción puede ser ejecutada o no y se asigna el valor
return al CanExecute en los EventArgs

La parte interesante de esto es que no se tiene que llamar a esos métodos para
tener los botones actualizados, WPF hace esto automáticamente cuando la
aplicación tiene un momento libre, asegurándose que la interfaz se mantiene
actualizada todo el tiempo.

200

200

100
1/9/2023

Control Menú
Una de las partes más comunes de una Aplicación de Windows es el menú,
algunas veces se refiere a él como el Menú Principal porque usualmente solo
existe uno en la aplicación. El menú es práctico porque ofrece muchas opciones,
usando solo un poco de espacio, aunque Windows está tratando de fomentar el
uso de la Cinta de Opciones (the Ribbon) como reemplazo, viejos menús y barras
de herramientas, definitivamente aún tienen su lugar en cada Caja de
Herramientas de un buen Desarrollador.
WPF viene con un control preciso para crear menús llamado... Menu.
Agregaritems, es muy simple – solo se agrega los elementos del MenuItem a el, y
cada MenuItem puede tener una gama de Sub-Items, permitiendo crear menús
jerárquicos como en muchas aplicaciones de Windows.

201

201

<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_Archivo">
<MenuItem Header="_Nuevo" />
<MenuItem Header="A_brir" />
<MenuItem Header="_Guardar" />
<Separator />
<MenuItem Header="_Salir" />
</MenuItem>
</Menu>
<TextBox AcceptsReturn="True" />
</DockPanel>

202

202

101
1/9/2023

➢ ICONOS Y CAJAS DE VERIFICACIÓN


Dos características comunes de un Menú son: el icono, usado para identificar
más fácilmente el Menú y lo que hace, y la habilidad de tener elementos de Menú
seleccionables, que puede activar y desactivar una característica especifica. El
MenuItem de WPF soporta ambos.

203

203

<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_Archivo">

<MenuItem Header="_Salir">
<MenuItem.Icon>
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE
Programacion II\Iconos\salir.png" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="_Herramientas">
<MenuItem Header="_Administracion Usuarios">
<MenuItem.Icon>
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE
Programacion II\Iconos\user.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Mostrar Grupos" IsCheckable="True" IsChecked="True" />
</MenuItem>
</Menu>
<TextBox AcceptsReturn="True" />
</DockPanel>

204

204

102
1/9/2023

➢ ACCIONES DEL MENÚ


Cuando el usuario hace clic en un elemento de menú, generalmente querrá que
pase algo. La forma más fácil es simplemente agregar un controlador de evento
clic al MenuItem.
Si se toma en cuenta un cambio en el anterior menú:
<MenuItem Header="_Salir" Click="MenuItem_Click" >

private void MenuItem_Click(object sender, RoutedEventArgs e)


{
MessageBox.Show("Usted salió...");
}

205

205

➢ ATAJOS DE COMANDOS Y TECLADOS


Se puede manejar fácilmente el evento Clic de un elemento de menú, pero el
enfoque más común es usar comandos WPF.
El uso de comandos en primer lugar, aseguran que puede tener la misma acción
en una barra de herramientas, un menú e incluso un menú contextual, sin tener
que implementar el mismo código en múltiples lugares, y segundo también
hacen que el manejo de los atajos de teclado sea mucho más fácil, porque a
diferencia de WinForms, WPF no está escuchando el teclado por accesos
directos de forma automática si los asigna por Ej. un elemento del menú -
tendrá que hacer eso manualmente.
Sin embargo, al usar comandos, WPF es todo oídos y responderá a los atajos de
teclado automáticamente. El texto (encabezado) del elemento del menú
también se establece de forma automática (aunque puede sobrescribirlo si es
necesario), y también lo es InputGestureText, que muestra al usuario qué atajo
de teclado se puede usar para invocar el elemento de menú específico.
206

206

103
1/9/2023

<Window.CommandBindings>
<CommandBinding Command="New" CanExecute="NewCommand_CanExecute"
Executed="NewCommand_Executed" />
</Window.CommandBindings>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_Archivo">
<MenuItem Command="New" />
<Separator />
<MenuItem Header="_Salir" />
</MenuItem>
<MenuItem Header="_Editar">
<MenuItem Command="Cut" />
<MenuItem Command="Copy" />
<MenuItem Command="Paste" />
</MenuItem>
</Menu>

<TextBox AcceptsReturn="True" Name="txtEditor" />


</DockPanel>

207

207

Puede que no sea completamente obvio, pero al usar comandos, se obtiene un


montón de cosas gratis: atajos de teclado, texto y InputGestureText en los
artículos y WPF automáticamente habilita / deshabilita los elementos
dependiendo del control activo y su estado. En este caso, Cortar y Copiar están
deshabilitados porque no hay texto seleccionado, pero Pegar está habilitado,
¡porque el portapapeles no está vacío!

Y porque WPF sabe cómo manejar ciertos comandos en combinación con


ciertos controles, en este caso los comandos Cortar / Copiar / Pegar en
combinación con un control de entrada de texto, ni siquiera se tiene que
manejar sus eventos de ejecución, ¡funcionan de inmediato! Sin embargo, se
tiene que manejarlo para el comando Nuevo , ya que WPF no tiene forma de
adivinar qué queremos que haga cuando el usuario lo active.

208

208

104
1/9/2023

Control Toolbar (Barra de Herramientas)


Una barra de herramientas es una fila de comandos, usualmente colocados
justo debajo del menú principal de una aplicación estándar de Windows. De
hecho, este puede ser un simple panel con botones en él, pero usando el
control ToolBar de WPF se obtiene unos beneficios extras como manejo
automático de desbordamiento y la posibilidad de que el usuario final pueda re-
ubicar las barras de herramientas.
Una ToolBar de WPF regularmente es ubicada dentro de un control
ToolBarTray. El ToolBarTray se encargará de manejar cosas como colocación y
dimensionamiento y se puede tener múltiples controles ToolBar dentro del
elemento ToolBarTray.

209

209

<Window.CommandBindings>
<CommandBinding Command="New" CanExecute="CommonCommandBinding_CanExecute" />
<CommandBinding Command="Open" CanExecute="CommonCommandBinding_CanExecute" />
<CommandBinding Command="Save" CanExecute="CommonCommandBinding_CanExecute" />
</Window.CommandBindings>
<DockPanel>
<ToolBarTray DockPanel.Dock="Top">
<ToolBar>
<Button Command="New" Content="Nuevo" />
<Button Command="Open" Content="Abrir" />
<Button Command="Save" Content="Guardar" />
</ToolBar>
<ToolBar>
<Button Command="Cut" Content="Cortar" />
<Button Command="Copy" Content="Copiar" />
<Button Command="Paste" Content="Pegar" />
</ToolBar>
</ToolBarTray>
<TextBox AcceptsReturn="True" />
</DockPanel>

private void CommonCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)


{
e.CanExecute = true;
} 210

210

105
1/9/2023

➢ MANEJO DE IMÁGENES
Mientras que el texto de los botones de la barra de herramientas está
perfectamente bien, el enfoque normal es tener iconos o al menos una
combinación de un icono y un trozo de texto. Dado que WPF utiliza controles de
botón regulares, agregar iconos a los elementos de la barra de herramientas es
muy fácil.

211

211

<DockPanel>
<ToolBarTray DockPanel.Dock="Top">
<ToolBar>
<Button Command="Cut" ToolTip="Corta y coloca una selección en el prtapapeles">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE Programacion
II\Iconos\cortar.png" />
</Button>
<Button Command="Copy" ToolTip="Copia una selección en el Portapapeles">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE Programacion
II\Iconos\copiar.png" />
</Button>
<Button Command="Paste" ToolTip="Pega contenido del Portapapeles">
<StackPanel Orientation="Horizontal">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE
Programacion II\Iconos\pegar.png" />
<TextBlock Margin="3,7,0,0">Pegar</TextBlock>
</StackPanel>
</Button>
</ToolBar>
</ToolBarTray>
<TextBox AcceptsReturn="True" />
</DockPanel>

212

212

106
1/9/2023

➢ DESBORDE
Una buena razón para utilizar el control ToolBar en lugar de un panel de botones,
es el manejo automático del desborde. Esto significa que si no hay lugar
suficiente para mostrar todos los botones de la barra de herramientas, WPF lo
pondrá en un menú accesible presionando en la flecha a la derecha de la barra.

<ToolBar>
<Button Command="Cut" Content="Cortar" ToolBar.OverflowMode="Always" />
<Button Command="Copy" Content="Copiar" ToolBar.OverflowMode="AsNeeded" />
<Button Command="Paste" Content="Pegar" ToolBar.OverflowMode="Never" />
</ToolBar>

213

213

➢ POSICIÓN
Si bien es cierto que la posición más común de una barra de herramientas es a
lo alto de la pantalla, las barras de herramientas también pueden encontrarse
en el fondo de la ventana de la aplicación o incluso a los lados. Por supuesto, la
barra de herramientas de WPF permite todo esto, y mientras que situarla al
fondo simplemente requiere cambiar la propiedad Dock del DockingPanel, una
barra vertical requiere también el uso de la propiedad Orientation (Orientación)
de la ToolbarTray.

214

214

107
1/9/2023

<DockPanel>
<ToolBarTray DockPanel.Dock="Top">
<ToolBar>
<Button Command="Cut" ToolTip="Corta una selección y pega al Portapapeles">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE Programacion II\Iconos\cortar.png" />
</Button>
<Button Command="Copy" ToolTip="Copia una selección en el Portapapeles">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE Programacion II\Iconos\copiar.png" />
</Button>
<Button Command="Paste" ToolTip="Pega contenido del portapapeles">
<StackPanel Orientation="Horizontal">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE Programacion II\Iconos\pegar.png" />
<TextBlock Margin="3,7,0,0">Pegar</TextBlock>
</StackPanel>
</Button>
</ToolBar>
</ToolBarTray>
<ToolBarTray DockPanel.Dock="Right" Orientation="Vertical">
<ToolBar>
<Button Command="Cut" ToolTip="Corta una selección y pega al Portapapeles">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE Programacion II\Iconos\cortar.png" />
</Button>
<Button Command="Copy" ToolTip="Copia una selección en el Portapapeles">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE Programacion II\Iconos\copiar.png" />
</Button>
<Button Command="Paste" ToolTip="Pega contenido del portapapeles">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE Programacion II\Iconos\pegar.png" />
</Button>
</ToolBar>
</ToolBarTray>
<TextBox AcceptsReturn="True" /> 215
</DockPanel>

215

➢ CONTROLES PERSONALIZADOS
<DockPanel>
<ToolBarTray DockPanel.Dock="Top">
<ToolBar>
<Button Command="Cut" ToolTip="Corta una selección y pega al Portapapeles">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE Programacion II\Iconos\cortar.png" />
</Button>
<Button Command="Copy" ToolTip="Copia una selección en el Portapapeles">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE Programacion II\Iconos\copiar.png" />
</Button>
<Button Command="Paste" ToolTip="Pega contenido del portapapeles">
<StackPanel Orientation="Horizontal">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE Programacion II\Iconos\pegar.png" />
<TextBlock Margin="3,7,0,0">Pegar</TextBlock>
</StackPanel>
</Button>
<Separator />
<Label Margin="0,7,0,0">Tamaño:</Label>
<ComboBox>
<ComboBoxItem>10</ComboBoxItem>
<ComboBoxItem IsSelected="True">12</ComboBoxItem>
<ComboBoxItem>14</ComboBoxItem>
<ComboBoxItem>16</ComboBoxItem>
</ComboBox>
</ToolBar>
</ToolBarTray>
<TextBox AcceptsReturn="True" />
</DockPanel>
216

216

108
1/9/2023

1.7. CONTROLES
AVANZADOS

217

Control Border
El control Border es un control Decorador que puede ser usado para dibujar un
borde, un fondo o inclusive ambos, alrededor de otro elemento. Ya que los
paneles WPF no tienen la capacidad de dibujar un borde alrededor de sus
esquinas, el control Border puede ayudar a lograr exactamente eso, simplemente
rodeando el mismo.
<Grid Margin="10">
<Border Background="GhostWhite" BorderBrush="Gainsboro"
BorderThickness="1">
<StackPanel Margin="10">
<Button>Boton 1</Button>
<Button Margin="0,10">Boton 2</Button>
<Button>Boton 3</Button>
</StackPanel>
</Border>
</Grid> 218

218

109
1/9/2023

<Grid Margin="10">
<Border Background="GhostWhite" BorderBrush="Silver" BorderThickness="1"
CornerRadius="8,8,3,3">
<StackPanel Margin="10">
<Button>Boton 1</Button>
<Button Margin="0,10">Boton 2</Button>
<Button>Boton 3</Button>
</StackPanel>
</Border>
</Grid>

<Grid Margin="10">
<Border Background="GhostWhite" BorderBrush="DodgerBlue"
BorderThickness="1,3,1,5">
<StackPanel Margin="10">
<Button>Boton 1</Button>
<Button Margin="0,10">Boton 2</Button>
<Button>Boton 3</Button>
</StackPanel>
</Border>
</Grid> 219

219

<Grid Margin="10">
<Border BorderBrush="Navy" BorderThickness="1,3,1,5">
<Border.Background>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="LightCyan" Offset="0.0" />
<GradientStop Color="LightBlue" Offset="0.5" />
<GradientStop Color="DarkTurquoise" Offset="1.0" />
</LinearGradientBrush>
</Border.Background>
<StackPanel Margin="10">
<Button>Boton 1</Button>
<Button Margin="0,10">Boton 2</Button>
<Button>Boton 3</Button>
</StackPanel>
</Border>
</Grid>

220

220

110
1/9/2023

Control Slider

<StackPanel VerticalAlignment="Center" Margin="10">


<Slider Maximum="100" />
</StackPanel>

<StackPanel VerticalAlignment="Center" Margin="10">


<Slider Maximum="100" TickPlacement="BottomRight" TickFrequency="5" />
</StackPanel>

221

<StackPanel VerticalAlignment="Center" Margin="10">


<Slider Maximum="100" TickPlacement="BottomRight" TickFrequency="10"
IsSnapToTickEnabled="True" />
</StackPanel>

<DockPanel VerticalAlignment="Center" Margin="10">


<TextBox Text="{Binding ElementName=slValue, Path=Value,
UpdateSourceTrigger=PropertyChanged}" DockPanel.Dock="Right" TextAlignment="Right"
Width="40" />
<Slider Maximum="255" TickPlacement="BottomRight" TickFrequency="5"
IsSnapToTickEnabled="True" Name="slValue" />
</DockPanel>

222

222

111
1/9/2023

<StackPanel Margin="10" VerticalAlignment="Center">


<DockPanel VerticalAlignment="Center" Margin="10">
<Label DockPanel.Dock="Left" FontWeight="Bold">R:</Label>
<TextBox Text="{Binding ElementName=slColorR, Path=Value,
UpdateSourceTrigger=PropertyChanged}" DockPanel.Dock="Right" TextAlignment="Right" Width="40"
/>
<Slider Maximum="255" TickPlacement="BottomRight" TickFrequency="5"
IsSnapToTickEnabled="True" Name="slColorR" ValueChanged="ColorSlider_ValueChanged" />
</DockPanel>
<DockPanel VerticalAlignment="Center" Margin="10">
<Label DockPanel.Dock="Left" FontWeight="Bold">G:</Label>
<TextBox Text="{Binding ElementName=slColorG, Path=Value,
UpdateSourceTrigger=PropertyChanged}" DockPanel.Dock="Right" TextAlignment="Right" Width="40"
/>
<Slider Maximum="255" TickPlacement="BottomRight" TickFrequency="5"
IsSnapToTickEnabled="True" Name="slColorG" ValueChanged="ColorSlider_ValueChanged" />
</DockPanel>
<DockPanel VerticalAlignment="Center" Margin="10">
<Label DockPanel.Dock="Left" FontWeight="Bold">B:</Label>
<TextBox Text="{Binding ElementName=slColorB, Path=Value,
UpdateSourceTrigger=PropertyChanged}" DockPanel.Dock="Right" TextAlignment="Right" Width="40"
/>
<Slider Maximum="255" TickPlacement="BottomRight" TickFrequency="5"
IsSnapToTickEnabled="True" Name="slColorB" ValueChanged="ColorSlider_ValueChanged" />
</DockPanel>
</StackPanel> 223

223

private void ColorSlider_ValueChanged(object sender,


RoutedPropertyChangedEventArgs<double> e)
{
Color color = Color.FromRgb((byte)slColorR.Value,
(byte)slColorG.Value, (byte)slColorB.Value);
this.Background = new SolidColorBrush(color);
}

224

224

112
1/9/2023

Control ProgressBar

<Grid Margin="20">
<ProgressBar Minimum="0" Maximum="100" Value="75" />
</Grid>

225

ContentRendered="Window_ContentRendered">

<Grid Margin="20">
<ProgressBar Minimum="0" Maximum="100" Name="pbStatus" />
</Grid>

using System.Threading;

private void Window_ContentRendered(object sender, EventArgs e)


{
for (int i = 0; i < 100; i++)
{
pbStatus.Value++;
Thread.Sleep(100);
}
}
226

226

113
1/9/2023

using System.Threading;
using System.ComponentModel;

private void Window_ContentRendered(object sender, EventArgs e)


{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += worker_DoWork;
worker.ProgressChanged += worker_ProgressChanged;

worker.RunWorkerAsync();
}

void worker_DoWork(object sender, DoWorkEventArgs e)


{
for (int i = 0; i < 100; i++)
{
(sender as BackgroundWorker).ReportProgress(i);
Thread.Sleep(100);
}
}

void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)


{
pbStatus.Value = e.ProgressPercentage;
} 227

227

<Grid Margin="20">
<ProgressBar Minimum="0" Maximum="100" Name="pbStatus" IsIndeterminate="True" />
</Grid>

<Grid Margin="20">
<ProgressBar Minimum="0" Maximum="100" Value="75" Name="pbStatus" />
<TextBlock Text="{Binding ElementName=pbStatus, Path=Value, StringFormat={}{0:0}%}"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>

228

228

114
1/9/2023

Control GroupBox
El control GroupBox permitirá agrupar de forma visual un grupo de controles.
Obviamente esto también podría realizarse usando uno de los tantos paneles que
existen, pero GroupBox agrega un tipo especial de encabezado y borde que
históricamente se ha usado mucho en el sistema operativo Windows.
El GroupBox solo puede contener un elemento hijo, un descendiente, pero esto
no es un problema.
<Grid>
<GroupBox Header="Datos Usuario" Margin="10" Padding="10">
<StackPanel>
<TextBlock>Nombres:</TextBlock>
<TextBox />
<TextBlock>Apellidos:</TextBlock>
<TextBox />
<Button Margin="0,20">Añadir usuario</Button>
</StackPanel>
</GroupBox>
</Grid>

229

<Grid>
<GroupBox Margin="10" Padding="10">
<GroupBox.Header>
<StackPanel Orientation="Horizontal">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE
Programacion II\Iconos\user.png" Margin="3,0" />
<TextBlock FontWeight="Bold"
Margin="0,7,0,0">Usuarios</TextBlock>
</StackPanel>
</GroupBox.Header>
<StackPanel>
<TextBlock>Nombres:</TextBlock>
<TextBox />
<TextBlock>Apellidos:</TextBlock>
<TextBox />
<Button Margin="0,20">Añadir usuario</Button>
</StackPanel>
</GroupBox>
</Grid>

230

230

115
1/9/2023

Control Calendar
<Grid>
<Calendar />
</Grid>

El Calendario no ocupa todo el espacio disponible. De hecho, incluso si se le da un


ancho y un alto grandes, la parte del calendario real solo ocupará la cantidad de
espacio observado, en tanto que si establece cualquiera de los valores muy
bajos, el calendario solo será parcialmente visible. Este comportamiento de
tamaño fijo no es muy típico de WPF, donde las cosas generalmente se estiran
para llenar el espacio disponible, y puede ser un poco molesto trabajar con él si
tiene una cantidad designada de espacio disponible para el calendario que desea
que complete. Afortunadamente, todo en WPF es escalable, pero en el caso del
control Calendar, necesita un poco de ayuda. El control Viewbox sirve para este
propósito:

231

<Viewbox>
<Calendar />
</Viewbox>

<Viewbox Stretch="Fill" StretchDirection="UpOnly">


<Calendar />
</Viewbox>

<Viewbox>
<Calendar DisplayDate="01.01.2009" />
</Viewbox>

<Viewbox>
<Calendar SelectionMode="MultipleRange" />
</Viewbox>

232

232

116
1/9/2023

<StackPanel Margin="10">
<Calendar Name="cldSample" SelectionMode="MultipleRange"
SelectedDate="10.22.2019" />
<Label>Fecha Seleccionada:</Label>
<TextBox Text="{Binding ElementName=cldSample, Path=SelectedDate,
StringFormat=d, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>

public MainWindow()
{
InitializeComponent();
cldSample.SelectedDate = DateTime.Now.AddDays(1);
}

233

233

<StackPanel Margin="10">
<Calendar Name="cldSample" SelectionMode="MultipleRange" />
<Label>Fechas seleccionadas:</Label>
<ListBox ItemsSource="{Binding ElementName=cldSample,
Path=SelectedDates}" MinHeight="150" />
</StackPanel>

<Viewbox>
<Calendar Name="cldSample" SelectionMode="MultipleRange">
<Calendar.BlackoutDates>
<CalendarDateRange Start="10.13.2019" End="10.19.2019" />
<CalendarDateRange Start="10.27.2019" End="10.31.2019" />
</Calendar.BlackoutDates>
</Calendar>
</Viewbox>
234

234

117
1/9/2023

Control DatePicker
El control DatePicker se mostrará más o menos como un TextBox normal, pero
con un pequeño botón que mostrará una vista de calendario al hacer clic, lo que le
permitirá al usuario seleccionar la fecha.
<StackPanel Margin="20">
<Label>Nombre:</Label>
<TextBox />
<Label>Cumpleaños:</Label> <DatePicker SelectedDate="2019-10-23"
<DatePicker></DatePicker> SelectedDateFormat="Long"></DatePicker>
<Label>Genero:</Label>
<ComboBox>
<ComboBoxItem>Femenino</ComboBoxItem>
<ComboBoxItem>Masculino</ComboBoxItem>
</ComboBox>
<Button Margin="20">Registrar</Button>
</StackPanel>

235

Control TabControl
El control TabControl de WPF permite distribuir la interfaz en diferentes áreas,
cada una de ellas accesibles haciendo clic en la pestaña de la cabecera,
usualmente situada en la parte superior del control.
<Grid>
<TabControl>
<TabItem Header="General">
<Label Content="El contenido va aqui..." />
</TabItem>
<TabItem Header="Seguridad" />
<TabItem Header="Detalles" />
</TabControl>
</Grid>

236

118
1/9/2023

Control ItemsControl
WPF cuenta con una amplia gama de controles para mostrar una lista de datos.
Tales controles vienen en distintos aspectos y formas, y varían en su complejidad
y cuánto trabajo realizan. La variante más simple es ItemsControl, que es más o
menos un bucle basado en una plantilla.
xmlns:system="clr-namespace:System;assembly=mscorlib"

<Grid Margin="10">
<ItemsControl>
<system:String>ItemsControl Item #1</system:String>
<system:String>ItemsControl Item #2</system:String>
<system:String>ItemsControl Item #3</system:String>
<system:String>ItemsControl Item #4</system:String>
<system:String>ItemsControl Item #5</system:String>
</ItemsControl>
</Grid>

237

<Grid Margin="10">
<ItemsControl Name="icTodoList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,0,0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}" />
<ProgressBar Grid.Column="1" Minimum="0" Maximum="100"
Value="{Binding Completion}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>

238

238

119
1/9/2023

public MainWindow()
{
InitializeComponent();

List<TodoItem> items = new List<TodoItem>();


items.Add(new TodoItem() { Title = "Completar WPF", Completion = 45 });
items.Add(new TodoItem() { Title = “Aprender C#", Completion = 80 });
items.Add(new TodoItem() { Title = “Armar la PC", Completion = 0 });

icTodoList.ItemsSource = items;
}
public class TodoItem
{
public string Title { get; set; }
public int Completion { get; set; }
}

239

239

<Grid Margin="10">
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" Margin="0,0,5,5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<system:String>Item #1</system:String>
<system:String>Item #2</system:String>
<system:String>Item #3</system:String>
<system:String>Item #4</system:String>
<system:String>Item #5</system:String>
</ItemsControl>
</Grid>
240

240

120
1/9/2023

<Grid Margin="10">
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" Margin="0,0,5,5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<system:String>Item #1</system:String>
<system:String>Item #2</system:String>
<system:String>Item #3</system:String>
<system:String>Item #4</system:String>
<system:String>Item #5</system:String>
</ItemsControl>
</Grid>

241

241

Control ListBox
El control ListBox es el siguiente componente encargado de generar listas, el cual
agrega un poco más de funcionalidad. Una de las principales diferencias es el
hecho de que el control ListBox trabaja con selecciones, permitiendo al usuario
final seleccionar uno o varios elementos de la lista y automáticamente da
retroalimentación visual.

<Grid Margin="10">
<ListBox>
<ListBoxItem>ListBox Item #1</ListBoxItem>
<ListBoxItem>ListBox Item #2</ListBoxItem>
<ListBoxItem>ListBox Item #3</ListBoxItem>
</ListBox>
</Grid>

242

121
1/9/2023

Para cada uno de los ListBoxItem es posible agregar un StackPanel, en el cual


se agrega una imagen y un TextBlock. Esto otorga un control total del
contenido, así como del renderizado
<Grid Margin="10">
<ListBox>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="D:\UNIVALLE catedras II-2020\Programacion II\Iconos\circulo.png" />
<TextBlock>ListBox Item #1</TextBlock>
</StackPanel>
</ListBoxItem>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="D:\UNIVALLE catedras II-2020\Programacion II\Iconos\circulo.png" />
<TextBlock>ListBox Item #2</TextBlock>
</StackPanel>
</ListBoxItem>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="D:\UNIVALLE catedras II-2020\Programacion II\Iconos\circulo.png" />
<TextBlock>ListBox Item #3</TextBlock>
</StackPanel>
</ListBoxItem>
</ListBox> 243
</Grid>

243

➢ VINCULANDO DATOS AL LISTBOX


Una diferencia clave entre ItemsControl y ListBox es que ListBox maneja y muestra la
selección de usuario. Por lo tanto, muchas preguntas de ListBox giran en torno a trabajar
de alguna manera con la selección.

244

244

122
1/9/2023

<DockPanel Margin="10">
<StackPanel DockPanel.Dock="Right" Margin="10,0">
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Margin" Value="0,0,0,5" />
</Style>
</StackPanel.Resources>
<TextBlock FontWeight="Bold" Margin="0,0,0,10">Selección del ListBox</TextBlock>
<Button Name="btnShowSelectedItem" Click="btnShowSelectedItem_Click">Ver Selección</Button>
<Button Name="btnSelectLast" Click="btnSelectLast_Click">Último</Button>
<Button Name="btnSelectNext" Click="btnSelectNext_Click">Siguiente</Button>
<Button Name="btnSelectCSharp" Click="btnSelectCSharp_Click">C#</Button>
<Button Name="btnSelectAll" Click="btnSelectAll_Click">Todo</Button>
</StackPanel>
<ListBox Name="lbTodoList" HorizontalContentAlignment="Stretch" SelectionMode="Extended"
SelectionChanged="lbTodoList_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}" />
<ProgressBar Grid.Column="1" Minimum="0" Maximum="100" Value="{Binding Completion}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel> 245

245

public partial class MainWindow : Window int nextIndex = 0;


{ if ((lbTodoList.SelectedIndex >= 0) &&
public MainWindow() (lbTodoList.SelectedIndex < (lbTodoList.Items.Count - 1)))
{ nextIndex = lbTodoList.SelectedIndex + 1;
InitializeComponent(); lbTodoList.SelectedIndex = nextIndex;
List<TodoItem> items = new List<TodoItem>(); }
items.Add(new TodoItem() { Title = "Aprender WPF", Completion
= 45 }); private void btnSelectCSharp_Click(object sender, RoutedEventArgs
items.Add(new TodoItem() { Title = "Estudiar C#", Completion e)
= 80 }); {
items.Add(new TodoItem() { Title = "Lavar el auto", foreach (object o in lbTodoList.Items)
Completion = 0 }); {
if ((o is TodoItem) && ((o as
lbTodoList.ItemsSource = items; TodoItem).Title.Contains("C#")))
} {
lbTodoList.SelectedItem = o;
private void lbTodoList_SelectionChanged(object sender, break;
System.Windows.Controls.SelectionChangedEventArgs e) }
{ }
if (lbTodoList.SelectedItem != null) }
this.Title = (lbTodoList.SelectedItem as TodoItem).Title;
} private void btnSelectAll_Click(object sender, RoutedEventArgs e)
{
private void btnShowSelectedItem_Click(object sender, foreach (object o in lbTodoList.Items)
RoutedEventArgs e) lbTodoList.SelectedItems.Add(o);
{ }
foreach (object o in lbTodoList.SelectedItems)
MessageBox.Show((o as TodoItem).Title); }
}
public class TodoItem
private void btnSelectLast_Click(object sender, RoutedEventArgs {
e) public string Title { get; set; }
{ public int Completion { get; set; }
lbTodoList.SelectedIndex = lbTodoList.Items.Count - 1; }
}

private void btnSelectNext_Click(object sender, RoutedEventArgs


e)
{
246

246

123
1/9/2023

Control ComboBox
El control ComboBox es parecido al control ListBox en muchos sentidos, pero usa
mucho menos espacio, ya que la lista de ítems se encuentra oculta cuando no se
necesita.

<StackPanel Margin="10">
<ComboBox>
<ComboBoxItem>ComboBox Item #1</ComboBoxItem>
<ComboBoxItem IsSelected="True">ComboBox Item #2</ComboBoxItem>
<ComboBoxItem>ComboBox Item #3</ComboBoxItem>
</ComboBox>
</StackPanel>

247

<StackPanel Margin="10">
<ComboBox>
<ComboBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="D:\UNIVALLE catedras II-2020\Programacion
II\Iconos\circulo.png" />
<TextBlock Foreground="Red">Red</TextBlock>
</StackPanel>
</ComboBoxItem>
<ComboBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="D:\UNIVALLE catedras II-2020\Programacion
II\Iconos\circulo.png" />
<TextBlock Foreground="Green">Green</TextBlock>
</StackPanel>
</ComboBoxItem>
<ComboBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="D:\UNIVALLE catedras II-2020\Programacion
II\Iconos\circulo.png" />
<TextBlock Foreground="Blue">Blue</TextBlock>
</StackPanel>
</ComboBoxItem>
</ComboBox>
</StackPanel>
248

248

124
1/9/2023

➢ VINCULANDO DATOS AL COMBOBOX


A veces es necesario que los ítems provengan de algún tipo de fuente de datos,
como una base de datos o simplemente una lista en memoria.
<StackPanel Margin="10">
<ComboBox Name="cmbColors">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding Name}" Width="16" Height="16"
Margin="0,2,5,2" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>

cmbColors.ItemsSource = typeof(Colors).GetProperties();
249

249

➢ ISEDITABLE
El ComboBox admite la posibilidad de permitir que el usuario seleccione de una
lista de elementos o ingrese su propio valor. Esto es extremadamente útil en
situaciones en las que se desea ayudar al usuario dándoles un conjunto
predefinido de opciones, mientras tiene la opción de ingresar manualmente el
valor deseado.

<StackPanel Margin="10">
<ComboBox IsEditable="True">
<ComboBoxItem>ComboBox Item #1</ComboBoxItem>
<ComboBoxItem>ComboBox Item #2</ComboBoxItem>
<ComboBoxItem>ComboBox Item #3</ComboBoxItem>
</ComboBox>
</StackPanel>

250

250

125
1/9/2023

➢ TRABAJANDO CON LA SELECCIÓN DEL COMBOBOX


Una parte clave del uso del control ComboBox es poder leer la selección del
usuario e incluso controlarla con código.
<StackPanel Margin="10">
<ComboBox Name="cmbColors" SelectionChanged="cmbColors_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding Name}" Width="16" Height="16"
Margin="0,2,5,2" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<WrapPanel Margin="15" HorizontalAlignment="Center">
<Button Name="btnPrevious" Click="btnPrevious_Click" Width="55">Previous</Button>
<Button Name="btnNext" Click="btnNext_Click" Margin="5,0" Width="55">Next</Button>
<Button Name="btnBlue" Click="btnBlue_Click" Width="55">Blue</Button>
</WrapPanel>
</StackPanel>
251

251

public partial class MainWindow : Window


{
public MainWindow()
{
InitializeComponent();
cmbColors.ItemsSource = typeof(Colors).GetProperties();
}

private void btnPrevious_Click(object sender, RoutedEventArgs e)


{
if (cmbColors.SelectedIndex > 0)
cmbColors.SelectedIndex = cmbColors.SelectedIndex - 1;
}

private void btnNext_Click(object sender, RoutedEventArgs e)


{
if (cmbColors.SelectedIndex < cmbColors.Items.Count - 1)
cmbColors.SelectedIndex = cmbColors.SelectedIndex + 1;
}

private void btnBlue_Click(object sender, RoutedEventArgs e)


{
cmbColors.SelectedItem = typeof(Colors).GetProperty("Blue");
}

private void cmbColors_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)


{
Color selectedColor = (Color)(cmbColors.SelectedItem as PropertyInfo).GetValue(null, null);
this.Background = new SolidColorBrush(selectedColor);
}

}
252

252

126
1/9/2023

Control ListView
El control ListView (Lista de datos) es muy comúnmente usado en aplicaciones
de Windows para representar una lista de datos. Un excelente ejemplo de esto es
el archivo "list" (lista) en el Explorador de Windows, donde cada archivo puede
verse por su nombre y si es deseado, con columnas que contienen información
acerca del tamaño, última fecha de modificación y otras más.
El control ListView de WPF es muy minimalista en su forma más simple. De
hecho, se parecerá bastante al control ListBox, a menos que se le empiecen a
añadir vistas especializadas.

253

<Grid>
<ListView Margin="10">
<ListViewItem>Un ListView</ListViewItem>
<ListViewItem IsSelected="True">con muchos</ListViewItem>
<ListViewItem>items</ListViewItem>
</ListView>
</Grid>

254

254

127
1/9/2023

<Grid>
<ListView Margin="10">
<ListViewItem>
<StackPanel Orientation="Horizontal">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE Programacion
II\Iconos\circulo.png" Margin="0,0,5,0" />
<TextBlock>Green</TextBlock>
</StackPanel>
</ListViewItem>
<ListViewItem>
<StackPanel Orientation="Horizontal">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE Programacion
II\Iconos\circulo.png" Margin="0,0,5,0" />
<TextBlock>Blue</TextBlock>
</StackPanel>
</ListViewItem>
<ListViewItem IsSelected="True">
<StackPanel Orientation="Horizontal">
<Image Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE Programacion
II\Iconos\circulo.png" Margin="0,0,5,0" />
<TextBlock>Red</TextBlock>
</StackPanel>
</ListViewItem>
</ListView>
</Grid>
255

255

public MainWindow()
{
InitializeComponent();

List<Usuario> items = new List<Usuario>();


items.Add(new Usuario()
{
Nombre = "Juan Juarez",
Edad = 42
});
items.Add(new Usuario()
{
Nombre = "Pedro Perez",
<Grid>
Edad = 39 <ListView Margin="10"
}); Name="lvDataBinding"></ListView>
items.Add(new Usuario() </Grid>
{
Nombre = "Fernando Fuentes",
Edad = 13
});
lvDataBinding.ItemsSource = items;
}

public class Usuario


{
public string Nombre { get; set; }

public int Edad { get; set; } 256


}

256

128
1/9/2023

Si se cambia la clase Usuario:

public class Usuario


{
public string Nombre { get; set; }
public int Edad { get; set; }
public override string ToString()
{
return this.Nombre + ", " + this.Edad + " años";
}
}

257

257

➢ LISTVIEW CON ITEMTEMPLATE


WPF tiene que ver con la creación de plantillas, por lo que especificar una
plantilla de datos para ListView es muy fácil.
<Grid>
<ListView Margin="10" Name="lvDataBinding">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Nombre: " />
<TextBlock Text="{Binding Nombre}" FontWeight="Bold" />
<TextBlock Text=", " />
<TextBlock Text="Edad: " />
<TextBlock Text="{Binding Edad}" FontWeight="Bold" />
<TextBlock Text=" (" />
<TextBlock Text="{Binding Mail}" TextDecorations="Underline"
Foreground="Blue" Cursor="Hand" />
<TextBlock Text=")" />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid> 258

258

129
1/9/2023

public MainWindow()
{
InitializeComponent();

List<Usuario> items = new List<Usuario>();


items.Add(new Usuario() { Nombre = "Juan Juarez", Edad = 42, Mail =
"juan@familia.com" });
items.Add(new Usuario() { Nombre = "Pedro Perez", Edad = 39, Mail =
"pedro@familia.com" });
items.Add(new Usuario() { Nombre = "Fernando Fuentes", Edad = 13,
Mail = "fernando.fer@gmail.com" });
lvDataBinding.ItemsSource = items;

public class Usuario


{
public string Nombre { get; set; }
public int Edad { get; set; }
public string Mail { get; set; }
} 259

259

➢ LISTVIEW CON GRIDVIEW


<Grid>
<ListView Margin="10" Name="lvUsers">
<ListView.View>
<GridView>
<GridViewColumn Header="Nombre" Width="120"
DisplayMemberBinding="{Binding Nombre}" />
<GridViewColumn Header="Edad" Width="50"
DisplayMemberBinding="{Binding Edad}" />
<GridViewColumn Header="Mail" Width="150"
DisplayMemberBinding="{Binding Mail}" />
</GridView>
</ListView.View>
</ListView>
</Grid>

260

260

130
1/9/2023

public MainWindow()
{
InitializeComponent();

List<Usuario> items = new List<Usuario>();


items.Add(new Usuario() { Nombre = "Juan Jupi", Edad = 42, Mail =
"juan@jupi-familia.com" });
items.Add(new Usuario() { Nombre = "Pedro Perez", Edad = 39, Mail =
"pedro@perez-familia.com" });
items.Add(new Usuario() { Nombre = "Fernando Fuentes", Edad = 13,
Mail = "fernando.fer@gmail.com" });
lvUsers.ItemsSource = items;

public class Usuario


{
public string Nombre { get; set; }
public int Edad { get; set; }
public string Mail { get; set; }
}
261

261

➢ ALINEACIÓN DE LAS COLUMNAS DE UN LISTVIEW


<Grid>
<ListView Margin="10" Name="lvUsers">
<ListView.Resources>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Left" />
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="Nombre" Width="120"
DisplayMemberBinding="{Binding Nombre}" />
<GridViewColumn Header="Edad" Width="50"
DisplayMemberBinding="{Binding Edad}" />
<GridViewColumn Header="Mail" Width="150"
DisplayMemberBinding="{Binding Mail}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
262

262

131
1/9/2023

➢ AGRUPANDO LOS DATOS DE UN LISTVIEW


La agrupación de datos es otro elemento más que soporta el ListView de
manera predeterminada, y es fácil de usar y extremadamente personalizable.

263

263

<Grid Margin="10">
<ListView Name="lvUsers">
<ListView.View>
<GridView>
<GridViewColumn Header="Nombre" Width="120"
DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Edad" Width="50"
DisplayMemberBinding="{Binding Age}" />
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock FontWeight="Bold" FontSize="14"
Text="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid> 264

264

132
1/9/2023

public MainWindow()
{
InitializeComponent();

List<User> items = new List<User>();


items.Add(new User() { Name = "John Doe", Age = 42, Sex = SexType.Masculino });
items.Add(new User() { Name = "Jane Doe", Age = 39, Sex = SexType.Femenino });
items.Add(new User() { Name = "Sammy Doe", Age = 13, Sex = SexType.Masculino });
lvUsers.ItemsSource = items;

CollectionView view =
(CollectionView)CollectionViewSource.GetDefaultView(lvUsers.ItemsSource);
PropertyGroupDescription groupDescription = new PropertyGroupDescription("Sex");
view.GroupDescriptions.Add(groupDescription);
}

public enum SexType { Masculino, Femenino };

public class User


{
public string Name { get; set; }
public int Age { get; set; }
public string Mail { get; set; }
public SexType Sex { get; set; }
}
265

265

Control ThreeView
La función del control TreeView (vista de árbol), permite mostrar en pantalla datos
de jerarquías, en donde cada pieza de datos es representada por un nodo en el
árbol. Cada nodo puede tener hijos y cada hijo puede tener otro hijo y así
sucesivamente.

266

133
1/9/2023

<Grid Margin="10">
<TreeView>
<TreeViewItem Header="Nivel 1" IsExpanded="True">
<TreeViewItem Header="Nivel 2.1" />
<TreeViewItem Header="Nivel 2.2" IsExpanded="True">
<TreeViewItem Header="Nivel 3.1" />
<TreeViewItem Header="Nivel 3.2" />
</TreeViewItem>
<TreeViewItem Header="Nivel 2.3" />
</TreeViewItem>
</TreeView>
</Grid>

267

267

<Grid Margin="10"> Programacion II\Iconos\puntorojo.png" Height="10"


<TreeView> Width="10" />
<TreeViewItem IsExpanded="True"> <TextBlock Text="Nivel
<TreeViewItem.Header> 2.2 (Red)" Foreground="Gray" />
<StackPanel </StackPanel>
Orientation="Horizontal"> </TreeViewItem.Header>
<Image Source="D:\UNIVALLE <TreeViewItem>
catedras II-2020\Equipo2\UNIVALLE Programacion <TreeViewItem.Header>
II\Iconos\puntoplomo.png" Height="10" Width="10" /> <TextBlock Text="Nivel
<TextBlock Text="Nivel 1 3.1" Foreground="Red" />
(Gray)" /> </TreeViewItem.Header>
</StackPanel> </TreeViewItem>
</TreeViewItem.Header> <TreeViewItem>
<TreeViewItem> <TreeViewItem.Header>
<TreeViewItem.Header> <TextBlock Text="Nivel
<StackPanel 3.2" Foreground="Red" />
Orientation="Horizontal"> </TreeViewItem.Header>
<TextBlock Text="Nivel </TreeViewItem>
2.1" Foreground="Gray" /> </TreeViewItem>
</StackPanel> <TreeViewItem>
</TreeViewItem.Header> <TreeViewItem.Header>
</TreeViewItem> <TextBlock Text="Nivel 2.3"
<TreeViewItem IsExpanded="True"> Foreground="Gray" />
<TreeViewItem.Header> </TreeViewItem.Header>
<StackPanel </TreeViewItem>
Orientation="Horizontal"> </TreeViewItem>
<Image </TreeView>
Source="D:\UNIVALLE catedras II-2020\Equipo2\UNIVALLE </Grid> 268

268

134
1/9/2023

Control DataGrid
El control DataGrid es parecido al ListView cuando se usa un GridView (es decir,
una tabla), pero ofrece una funcionalidad adicional. Por Ejemplo, el DataGrid
puede generar columnas automáticamente, dependiendo de los datos que se le
proporciona. El DataGrid también se puede editar de forma predeterminada, lo
que permite al usuario final cambiar los valores sobre el origen de los datos.

<Grid Margin="10">
<DataGrid Name="dgSimple"></DataGrid>
</Grid>

269

public partial class MainWindow : Window


{
public MainWindow()
{
InitializeComponent();
List<User> users = new List<User>();
users.Add(new User() { Id = 1, Name = "John Doe", Birthday = new DateTime(1971, 7,
23) });
users.Add(new User() { Id = 2, Name = "Jane Doe", Birthday = new DateTime(1974, 1,
17) });
users.Add(new User() { Id = 3, Name = "Sammy Doe", Birthday = new DateTime(1991, 9,
2) });

dgSimple.ItemsSource = users;
}
public class User
{
public int Id { get; set; }

public string Name { get; set; }

public DateTime Birthday { get; set; }


}

270

270

135
1/9/2023

➢ COLUMNAS DEL DATAGRID


<Grid Margin="10">
<DataGrid Name="dgUsers" AutoGenerateColumns="False">
<DataGrid.Columns>

<DataGridTextColumn Header="Nombre" Binding="{Binding Name}" />

<DataGridTemplateColumn Header="Fecha Nacimiento">


<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding Birthday}"
BorderThickness="0" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

</DataGrid.Columns>
</DataGrid>
</Grid>

271

271

public partial class MainWindow : Window


{
public MainWindow()
{
InitializeComponent();
List<User> users = new List<User>();
users.Add(new User() { Id = 1, Name = "John Doe", Birthday = new
DateTime(1971, 7, 23) });
users.Add(new User() { Id = 2, Name = "Jane Doe", Birthday = new
DateTime(1974, 1, 17) });
users.Add(new User() { Id = 3, Name = "Sammy Doe", Birthday = new
DateTime(1991, 9, 2) });

dgUsers.ItemsSource = users;

}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Birthday { get; set; }
}
} 272

272

136
1/9/2023

➢ FILAS DEL DATAGRID


<Grid Margin="10">
<DataGrid Name="dgUsers" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Nombre" Binding="{Binding Name}" />
<DataGridTextColumn Header="Fecha Nacimiento" Binding="{Binding
Birthday}" />
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<TextBlock Text="{Binding Details}" Margin="10" />
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
</Grid>

273

273

public partial class MainWindow : Window


{
public MainWindow()
{
InitializeComponent();
List<User> users = new List<User>();
users.Add(new User() { Id = 1, Name = "John Doe", Birthday = new DateTime(1971, 7, 23) });
users.Add(new User() { Id = 2, Name = "Jane Doe", Birthday = new DateTime(1974, 1, 17) });
users.Add(new User() { Id = 3, Name = "Sammy Doe", Birthday = new DateTime(1991, 9, 2) });

dgUsers.ItemsSource = users;

}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Birthday { get; set; }
public string Details
{
get
{
return String.Format("{0} nació el {1} y esta es una larga descripción de la
persona.", this.Name, this.Birthday.ToLongDateString());
}
}
}

} 274

274

137
1/9/2023

1.8. TEMPORIZADORES

275

La Clase DispatcherTimer
En Windows Forms existe un control llamado Timer que puede ejecutar una acción
repetidas veces dentro de un intervalo de tiempo. WPF tiene esta posibilidad
también, pero en vez de un control invisible, se tiene el control DispatcherTimer.
Este control hace casi lo mismo, pero en lugar de colocar solamente el elemento
en el formulario, debe ser creado y usado exclusivamente desde el código del
mismo.
La clase DispatcherTimer trabaja especificando un intervalo y suscribiéndolo al
evento Tick que ocurrirá cada vez que se cumpla el intervalo. El DispatcherTimer
no comienza hasta que se llama al método Start() o se establece la propiedad
IsEnabled a verdadero.

276

276

138
1/9/2023

<Grid>
<Label Name="lblTime" FontSize="48" HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>

public partial class MainWindow : Window using System.Windows.Threading;


{
public MainWindow()
{
InitializeComponent();

DispatcherTimer timer = new DispatcherTimer();


timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += timer_Tick;
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
lblTime.Content = DateTime.Now.ToLongTimeString();
}
} 277

277

<Grid>
<TextBlock Name="tbTime" />
</Grid>
public partial class MainWindow : Window
{
DispatcherTimer _timer;
TimeSpan _time;
public MainWindow()
{
InitializeComponent();
_time = TimeSpan.FromSeconds(10);
_timer = new DispatcherTimer(new TimeSpan(0, 0, 1),
DispatcherPriority.Normal, delegate
{
tbTime.Text = _time.ToString("c");
if (_time == TimeSpan.Zero) _timer.Stop();
_time = _time.Add(TimeSpan.FromSeconds(-1));
}, Application.Current.Dispatcher);

_timer.Start();
}
} 278

278

139
1/9/2023

1.9. CONTROLES DE
DIÁLOGO

279

MESSAGE BOX
WPF ofrece varios diálogos para que su uso en aplicaciones, pero MessageBox es
definitivamente el más sencillo. Su único propósito es mostrar un mensaje al
usuario y luego darle múltiples formas de responder al mismo.
MessageBox se utiliza llamando al método estático Show(), que puede recibir una
gama de diferentes parámetros para ser capaz de verse y comportarse de la forma
que se requiera.
public MainWindow()
{
InitializeComponent();
MessageBox.Show("Bienvenido a WPF!");

280

280

140
1/9/2023

✓ TÍTULO
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MessageBox.Show("Bienvenido a WPF", "Sistema v1.1");
}
}

281

281

✓ BOTONES ADICIONALES
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MessageBox.Show("Deseas ingresar a la aplicación.\n\nBienvenido a
WPF?", "Sistema v1.1", MessageBoxButton.YesNoCancel);
}
}

OK
OKCancel
YesNoCancel
YesNo

282

282

141
1/9/2023

Es posible detectar la opción seleccionada por el usuario, ya que, el método


MessageBox.Show () siempre devuelve un valor de el
enumerador MessageBoxResult.
public MainWindow()
{
InitializeComponent();
MessageBoxResult result = MessageBox.Show("Deseas ingresar a la
aplicación.\n\nBienvenido a WPF?", "Sistema v1.1", MessageBoxButton.YesNoCancel);
switch (result)
{
case MessageBoxResult.Yes:
MessageBox.Show("Bienvenido!", "Sistema v1.1");
break;
case MessageBoxResult.No:
MessageBox.Show("Hasta pronto!", "Sistema v1.1");
break;
case MessageBoxResult.Cancel:
MessageBox.Show("Vuleve a intentarlo", "Sistema v1.1");
break;
}
}
283

283

✓ ICONOS
public MainWindow()
{
InitializeComponent();
MessageBox.Show("Deseas ingresar a la aplicación.\n\nBienvenido a
WPF?", "Sistema v1.1", MessageBoxButton.OK, MessageBoxImage.Information);
}

Asterisk (Asterisco)
Error
Exclamation (Exclamación)
Hand (Mano)
Information (información)
None (Ninguno)
Question (Pregunta)
Stop (Alto)
Warning (Advertencia)
284

284

142
1/9/2023

✓ OPCIÓN POR DEFECTO


MessageBox seleccionará un botón como la elección predeterminada, que luego
será el botón invocado en caso de que el usuario simplemente presione Enter
una vez que se muestra el cuadro de diálogo.

public MainWindow()
{
InitializeComponent();
MessageBox.Show("Deseas ingresar a la aplicación.\n\nBienvenido a
WPF?", "Sistema v1.1", MessageBoxButton.YesNo, MessageBoxImage.Question,
MessageBoxResult.No);
}

285

285

Ejercicio

Realice un programa en WPF, que muestre los botones para ejecutar los
diferentes diálogos establecidos anteriormente.

286

286

143
1/9/2023

OpenFileDialog
Cada vez que se abre o guarda un archivo en casi cualquier aplicación de Windows,
se puede observar aproximadamente los mismos cuadros de diálogo para hacerlo.
La razón es, por supuesto, que estos cuadros de diálogos forman parte de la API de
Windows y, por lo tanto, también son accesibles para los desarrolladores en la
plataforma de Windows.
En WPF, se encontrarán cuadros de diálogo estándares para abrir y guardar
archivos en el espacio de nombres Microsoft.Win32. Uno de ellos es la
clase OpenFileDialog, que hace que sea muy fácil mostrar un cuadro de diálogo
para abrir uno o varios archivos.

287

<DockPanel Margin="10">
<WrapPanel HorizontalAlignment="Center" DockPanel.Dock="Top" Margin="0,0,0,10">
<Button Name="btnOpenFile" Click="btnOpenFile_Click">Open file</Button>
</WrapPanel>
<TextBox Name="txtEditor" />
</DockPanel>
La función ShowDialog() devolverá un valor
using Microsoft.Win32; booleano que se puede ser nulo, lo que
using System.IO; significa que puede ser falso, verdadero o
nulo. Si el usuario selecciona un archivo y
public partial class MainWindow : Window presiona "Abrir", el resultado es True y, en ese
{ caso, intentamos cargar el archivo en el
public MainWindow()
control TextBox. Obtenemos la ruta completa
{ del archivo seleccionado utilizando la
InitializeComponent(); propiedad FileName de OpenFileDialog.
}

private void btnOpenFile_Click(object sender, RoutedEventArgs e)


{
OpenFileDialog openFileDialog = new OpenFileDialog();
if (openFileDialog.ShowDialog() == true)
txtEditor.Text = File.ReadAllText(openFileDialog.FileName);
}
} 288

288

144
1/9/2023

✓ FILTRAR
Normalmente, cuando se desea que el usuario abra un archivo en su aplicación,
desea limitarlo a uno o dos tipos de archivos. Por ejemplo, Word casi siempre
abre archivos de Word (con la extensión .doc o .docx) y el Bloc de notas casi
siempre abre archivos de texto (con la extensión .txt), por ejemplo:
openFileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
openFileDialog.Filter = "Image files (*.png;*.jpeg)|*.png;*.jpeg|All files (*.*)|*.*";

private void btnOpenFile_Click(object sender, RoutedEventArgs e)


{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
if (openFileDialog.ShowDialog() == true)
txtEditor.Text = File.ReadAllText(openFileDialog.FileName);

}
289

289

✓ DIRECTORIO INICIAL
El directorio inicial utilizado por el diálogo OpenFileDialog es decidido por
Windows, pero utilizando la propiedad InitialDirectory es posible sobreescribirla.
openFileDialog.InitialDirectory = @"c:\temp\";

Si se desea utilizar una de las carpetas especiales de Windows, p.e. Escritorio, Mis
Documentos o Archivos de Programa, se debe tener un especial cuidado, pues
estas pueden cambiar entre versiones de Windows y también pueden depender
del usuario que esté conectado. El framework .NET puede ayudar, simplemente
utilizando la clase Environment y sus miembros para tratar con carpetas
especiales:
openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

290

290

145
1/9/2023

✓ MÚLTIPLES ARCHIVOS
Si una aplicación requiere varios archivos abiertos, o simplemente se desea
utilizar OpenFileDialog para seleccionar mas de un archivo, es necesario activar
la propiedad Multiselect.
<DockPanel Margin="10">
<WrapPanel HorizontalAlignment="Center" DockPanel.Dock="Top" Margin="0,0,0,10">
<Button Name="btnOpenFile" Click="btnOpenFiles_Click">Open files</Button>
</WrapPanel>
<ListBox Name="lbFiles" />
</DockPanel>

291

291

public partial class MainWindow : Window


{
public MainWindow()
{
InitializeComponent();
}

private void btnOpenFiles_Click(object sender, RoutedEventArgs e)


{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Multiselect = true;
openFileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
openFileDialog.InitialDirectory =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
if (openFileDialog.ShowDialog() == true)
{
foreach (string filename in openFileDialog.FileNames)
lbFiles.Items.Add(System.IO.Path.GetFileName(filename));
}
}
}

292

292

146
1/9/2023

SaveFileDialog
El SaveFileDialog permitirá seleccionar una ubicación y asignar nombre cuando se
desee guardar un archivo. Funciona y luce como el OpenFileDialog.
<DockPanel Margin="10">
<WrapPanel HorizontalAlignment="Center" DockPanel.Dock="Top" Margin="0,0,0,10">
<Button Name="btnSaveFile" Click="btnSaveFile_Click">Save file</Button>
</WrapPanel>
<TextBox Name="txtEditor" TextWrapping="Wrap" AcceptsReturn="True"
ScrollViewer.VerticalScrollBarVisibility="Auto" />
</DockPanel>

private void btnSaveFile_Click(object sender, RoutedEventArgs e)


{
SaveFileDialog saveFileDialog = new SaveFileDialog();
if (saveFileDialog.ShowDialog() == true)
File.WriteAllText(saveFileDialog.FileName, txtEditor.Text);
}

293

✓ FILTRAR
saveFileDialog.Filter = "Text file (*.txt)|*.txt|C# file (*.cs)|*.cs";

public partial class MainWindow : Window


{
public MainWindow()
{
InitializeComponent();
}

private void btnSaveFile_Click(object sender, RoutedEventArgs e)


{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.Filter = "Text file (*.txt)|*.txt|C# file (*.cs)|*.cs";
if (saveFileDialog.ShowDialog() == true)
File.WriteAllText(saveFileDialog.FileName, txtEditor.Text);
}

294

294

147
1/9/2023

✓ DIRECTORIO INICIAL
La dirección inicial usada por el SaveFileDialog es decidida por Windows, pero
usando la propiedad InitialDirectory, es posible sobrescribirla. Usualmente se
asignará este valor apuntando a una dirección específica del usuario, a la
carpeta de la aplicación o quizá la última dirección usada.

saveFileDialog.InitialDirectory = @"c:\temp\";

saveFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

295

295

✓ OTRAS OPCIONES
AddExtension- El valor por defecto es verdadero y determina si el SaveFileDialog debería
automáticamente agregar una extensión al nombre del archivo, si el usuario la omite.- La
extensión se basará en la opción seleccionada en el filtro, a menos que ese no sea el caso,
se le asignará la propiedad DefaultExt (si está especificado). Si se desea que la aplicación
sea capaz de guardar archivos sin extensión, se debe deshabilitar esta opción.

OverwritePrompt - el valor por defecto es verdadero y determina si el SaveFileDialog


debería hacer una confirmación si el usuario ingresa un nombre para su archivo y
guardarlo, provocaría sobrescribir algún otro archivo. Usualmente se requiere mantener
esta opción habilitada excepto en situaciones muy especiales.

Title – Permite colocar un título personalizado en el diálogo. Por defecto el título es


"Guardar como" o el equivalente localizado. Esto es también válido para OpenFileDialog.

ValidateNames - Por defecto es true y salvo que este deshabilitado, garantiza que el
usuario solo introduzca nombres validos de archivos de Windows antes de permitirle
continuar.
296

296

148
1/9/2023

✓ OTROS DIÁLOGOS
Windows Forms viene con una serie adicionales de diálogos pero que no existen
en WPF. El más importante de ellos es definitivamente el FolderBrowserDialog,
el cual permite al usuario seleccionar una carpeta del sistema de archivos, pero
otros diálogos faltantes en WPF incluyen el ColorDialog, el FontDialog, el
PrintPreviewDialogy el PageSetupDialog.
Esto puede ser un verdadero problema para los desarrolladores de WPF, ya que
la reimplementación de estos diálogos sería una tarea enorme.
Afortunadamente, WPF y WinForms pueden ser mezclados, simplemente
haciendo referencia al ensamblado System.Windows.Forms, pero siendo que
WPF utiliza tipos básicos para ambos, colores y diálogos, esto no siempre es una
solución viable. Esto es sin embargo una fácil solución si sólo necesitas el
FolderBrowserDialog, dado que éste solamente trata con rutas de carpeta como
simples strings, aunque algunos profesionales argumentarían que mezclar WPF
y WinForms jamás es la manera de proceder.
297

297

Una mejor forma de proceder, es usar algo del trabajo creado por otros
desarrolladores. Aquí hay un par de links de un artículo que ofrece una solución a
algunos de los diálogos faltantes:

https://www.codeproject.com/Articles/368070/A-WPF-Font-Picker-with-Color

https://www.codeproject.com/Articles/33001/WPF-A-Simple-Color-Picker-With-Preview

298

298

149
1/9/2023

CONSULTAS Y
OBSERVACIONES

299

299

150

También podría gustarte