Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
WPF
Microsoft IT Academy
La plataforma .NET tiene todo un mecanismo estndar definido para el trabajo con eventos. WPF hace uso de este estndar y lo enriquece con un nuevo mecanismo de disparo de eventos (utilizado sobre todo para los eventos de teclado y ratn). Los eventos en WPF siguen estrategias de propagacin asociadas a que los elementos en WPF pueden estar contenidos en otros elementos.
Listado 5-1 Cdigo XAML de Ventana con Botn con Texto e Imagen como contenido.
<Window x:Class="BotnTextoImagen.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Botn caja de texto e imagen" Height="300" Width="300" > <Button Grid.Column="0" Height="100" Width="200"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions>
Pgina 2 de 17
Figura 5 - 1 Ventana con botn (cursor sobre el texto y cursor sobre la imagen)
En el modelo estndar de .NET el manejo de los eventos consiste en que cada elemento contenido en el control responda al evento al que queremos reaccione el control. Esto implicara en este ejemplo que tanto el bloque de texto como la imagen tendra que registrarse como manejador de dicho evento. Imagine lo tedioso que esto podra resultar ahora en WPF registrar los manejadores de los eventos si tenemos en cuenta que prcticamente no hay restricciones sobre lo que puede conformar el contenido de un control (todo un rbol de elementos anidados dentro de otros). Afortunadamente esto no es necesario. WPF hace uso de los llamados Eventos Enrutados (RoutedEvent). Un Evento Enrutado en lugar de tener en cuenta solo a aquellos manejadores que se han registrado en el elemento sobre el que directamente se produce el evento, podr llamar a todos los manejadores especificados en los elementos que estn en la "ruta" del rbol en el que est incluido el elemento. Los Eventos enrutados pueden ser hacia afuera (Bubbling), hacia adentro (Tunneling) o directos (Direct). Eventos Bubbling: Primero busca por manejadores en el elemento donde se ha originado el evento, luego hace lo mismo en el elemento que lo contiene (padre) y as sucesivamente hasta la raz del rbol de elementos (que como ya sabemos del Captulo I Introduccin es una ventana o una pgina). El evento se dispara en un elemento y se propaga hacia su contenedor ms inmediato que a su vez lo dispara a su contenedor y as hasta llegar al elemento raz.
Pgina 3 de 17
Un evento como clic de ratn se considera que se origina en un elemento cuya rea de layout es la menor que contiene al punto donde estaba el cursor al hacer clic. As en la Figura 5-1 izquierda el evento se origina sobre el TextBlock y en la figura de la derecha el evento se origina sobre el Image Eventos Tunneling: El proceso ocurre al inverso que con los Bubbling, busca primero manejadores en el elemento raz para luego descender buscando en cada hijo (elemento contenido) hasta llegar al elemento en que se origin. Tenga presente que por la forma arbrea en que estn contenidos los elementos solo hay un camino para llegar de la raz al elemento en el que se origin el evento. Eventos directos: El mecanismo es semejante al que se ha tratado hasta ahora en .NET (por ejemplo en las Windows Forms). Solo se tendr en cuenta el manejador en el elemento en que se ha originado el evento. Por ejemplo un evento clic cuando el cursor est sobre el rea que ocupa una imagen (Figura 5-1 a la derecha) solo se tendr en cuenta en el manejador del elemento Image. En este caso el evento no se propaga en ningn sentido. Para un mismo suceso, por ejemplo un clic a un botn, el orden de llamadas comienza por los eventos Tunneling, luego el directo y finalmente los eventos de Bubbling. En WPF la mayora de los eventos con ruta (exceptuando los directos) se disparan en pares: uno Tunneling y otro Bubbling. Por convenio en WPF el nombre de los Tunneling que conforman el par comienza siempre con "Preview" seguido por el nombre del correspondiente Bubbling (por ejemplo PreviewMouseLeftButtonDown y MouseLeftButtonDown). Los eventos Tunneling son disparados primero, seguidos directamente del Bubbling correspondiente. Se pueden registrar manejadores para cada uno de los eventos tanto Tunneling como Bubbling. Veamos un ejemplo de aplicacin donde queremos conocer las coordenadas del cursor del ratn as como el tipo del elemento de la interfaz sobre el que se encuentra situado. El Listado 5-2 muestra el cdigo de una ventana con varios elementos hijos y cmo en XAML se asocia un manejador a un evento. En este caso se ha asociado al evento MouseMove. Este manejador asociado al MouseMove en el elemento Window se ejecutar independientemente del en el que se origin el evento MouseMove pues, ser "burbujeado" hacia arriba siguiendo la ruta a la raz del rbol que forman los elementos de la interfaz.
Pgina 4 de 17
Gracias al mecanismo de los Eventos con Ruta, para lograr esta funcionalidad solo tenemos que registrar un manejador para este evento en el elemento padre de la jerarqua (en este caso Window). Por ser MouseMove un evento Bubbling el mecanismo de eventos de WPF se encargar de que el evento "suba" siguiendo una ruta de dnde se origin hacia su padre (su elemento contenedor) hasta llegar a la raz (Window o Page) donde ser manejado. Note que no es necesario registrarse al evento en ningn elemento intermedio.
MouseMove="WindowMouseMove"
> <StackPanel> <TextBlock Margin="10" HorizontalAlignment="Right" Name="TB" FontSize="16" FontWeight="Bold" Foreground="Red"> </TextBlock> <StackPanel HorizontalAlignment="Left"> <TextBlock Margin="10" FontFamily="Arial" FontSize="20" FontWeight="Bold" Foreground="Orange"> Desarrolla con </TextBlock> <Image Source="MSDN.gif" Height="50"/> </StackPanel> <StackPanel Orientation="Horizontal"> <Image Source="Windito.gif" Height="100"/> <TextBlock Margin="10" VerticalAlignment="Center" FontFamily="Arial" FontSize="20" FontWeight="Bold" Foreground="Navy"> y aprende WPF con nosotros </TextBlock> </StackPanel> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Button FontFamily="Arial" FontSize="20" FontWeight="Bold"> Aceptar </Button> <Button FontFamily="Arial" FontSize="20" FontWeight="Bold"> Cancelar </Button> </StackPanel> </StackPanel> </Window>
Pgina 5 de 17
Para cada manejador definido en el XAML hay que dar una implementacin en el Codebehind como muestra el Listado 5-3. El nombre dado al manejador en el XAML tiene que coincidir con el nombre del mtodo en la implementacin en el Codebehind. De manera general un manejador de evento tiene la siguiente sintaxis:
El parmetro sender expresa quin enva el evento directamente. Para obtener el elemento donde se origin un evento se tiene la propiedad Source del parmetro args de tipo RoutedEventsArgs. Los manejadores para los eventos relacionados con el ratn (MouseMove, MouseEnter o MouseLeave por ejemplo) son de tipo MouseEventHandler y su segundo parmetro es de tipo MouseEventArgs. Para conocer las coordenadas del ratn el parmetro args brinda el mtodo GetPosition que recibe como parmetros el elemento cuya esquina superior izquierda se tomar como origen de coordenadas para dar la ubicacin relativa a sta del puntero del ratn.
Lo que hace este manejador (Listado 5-3) es mostrar la posicin (coordenadas x,y) del cursor del ratn relativas a la ventana y el tipo del elemento sobre el que se encuentra (Figura 5-2) .
Pgina 6 de 17
Pgina 7 de 17
WPF brinda soporte para definir en XAML comandos que ejecuten un conjunto de acciones. La manera ms sencilla de usar un comando en WPF se basa en un conjunto de RoutedCommand predefinidos, usar controles WPF que ya vengan con soporte para manejar el comando, y controles que tengan soporte para invocar el comando. Evitando de esta forma la necesidad de escribir cdigo en el code-behind. Por ejemplo el control TextBox, tiene soporte para manejar los comandos Cut, Copy y Paste, y el control Button tiene soporte para invocar comandos a travs de la propiedad Command. Asociadas a la propiedad Command hay a su vez dos propiedades:
CommandParameter y CommandTarget, que permiten definir sobre quin se desea aplicar la accin del comando, as como el parmetro para la ejecucin del comando (en el caso en que el comando necesite parmetro). Por ejemplo, el comando NavigationCommands.GoToPage requiere como parmetro la pgina hacia la cual navegar. Cuando se invoca un comando WPF dispara dos eventos: PreviewExecuted y Executed (que son Tunneling y Bubbling respectivamente). Los eventos PreviewExecuted y Executed buscarn en el rbol de elementos contenedores por un objeto que tenga definido como manejar el evento (un CommandBinding para el comando especfico que lanz el evento). CommandBinding es el mecanismo (se ve con ms detalle en la Leccin Enlace a Datos) mediante el cual se asocia el evento con una lgica que lleva a cabo. Existen en WPF un conjunto de clases que nos brindan comandos comnmente utilizados, esto implica que no necesitamos crear nuestro propio RoutedCommand para operaciones como nuevo, abrir, cerrar, cortar, copiar, pegar, etc. Existe una amplia gama de comandos definidos en WPF para ejecutar acciones en diversos escenarios. Estos comandos de tipo RoutedCommand estn agrupados como propiedades estticas en cinco clases diferentes. ApplicationCommands: Comandos estndar para cualquier aplicacin, por ejemplo Copy, Paste, Undo, Redo, Find, Open, SaveAs, PrintPreview, etc. ComponentCommands: Comandos frecuentemente usados por la interfaz de usuario que brindan algunos controles. MoveLeft, MoveToEnd, ScrollPageDown, Textselection, etc.
Pgina 8 de 17
MediaCommands: Comandos usados para multimedia, por ejemplo Play, Pause, NextTrack, IncreaseVolume, ToggleMicrophoneOnOff, etc. NavigationCommands: Comandos utilizados para navegar por la informacin de las pginas por ejemplo BrowseBack, GoToPage, NextPage, Refresh, Zoom, etc. EditingCommands Comandos usados para editar documentos, por ejemplo
AlignCenter, ApplyFontSize, EnterPageBreak, ToggleBold, Bold, Italic, Alignment, etc. Cada uno de estos comandos es un "singleton" (existe una nica instancia del comando). Esto implica que si registramos un manejador para el evento de invocar un comando, ste se ejecutar cada vez que el comando sea invocado en cualquier lugar de nuestra aplicacin. Si lo que queremos es que este manejador se ejecute solo cuando el comando es invocado en un contexto determinado tenemos entonces que establecer una relacin entre comando, manejador y mbito del elemento que ser el destino de la accin definida por el comando dentro de la interfaz de usuario, para ello se hace uso de un objeto CommandBinding como veremos ms adelante en esta misma leccin. Toda la lgica que se sigue en WPF para el trabajo con comandos podra separarse en cuatro conceptos fundamentales: La accin a ejecutar El objeto que invoca al comando El objeto sobre el cual se ejecuta el comando El objeto que relaciona el comando con una lgica. Lo ms interesante en la arquitectura de los comandos de WPF, es las distintas formas que hay de asociar un "destino" y una funcionalidad al comando. Vamos a dividir ahora nuestra explicacin en dos escenarios.
Asociiando un comando a un ellemento con soporte para Asoc ando un comando a un e emento con soporte para manejjarllo.. mane ar o
Si Ud. desea comenzar a trabajar con los comandos de WPF, quizs lo primero que le venga a la mente probar es una caja de texto donde poder Cortar, Copiar y Pegar contenido. Cmo ya mencionamos anteriormente el control TextBox brinda soporte intrnseco para estos comandos, veamos el cdigo XAML del Listado 5-4.
Pgina 9 de 17
El resultado aparece en la Figura 5-3. Note que la opcin de Pegar (Paste) en el men de contexto que despliega el TextBox, como respuesta a la accin de clic derecho del ratn, aparece deshabilitada pues en el clipboard no hay nada "a pegar" (en este caso el mtodo CanExecute para el comando Paste devuelve false).
Figura 5 - 3 Soporte para manejar los comandos Cut, Copy y Paste en el TextBox
Ahora queremos que estas acciones de Cortar, Copiar y Pegar sobre la caja de texto se puedan lograr tambin a travs de botones. Para ello consideremos el cdigo del Listado 5-5
Pgina 10 de 17
Pgina 11 de 17
CommandTarget="{Binding ElementName=TextBox1}">
Cortar </Button> <Button Command="Copy" FontSize="18"
CommandTarget="{Binding ElementName=TextBox1}">
Copiar </Button> <Button Command="Paste" FontSize="18"
CommandTarget="{Binding ElementName=TextBox1}">
Pegar </Button> </StackPanel> </StackPanel>
Pgina 12 de 17
resolver
este
problema
usaremos
la
propiedad
IsFocusScope
de
la
clase
FocusManager. Cuando no se especifica explcitamente un valor a la propiedad CommandTarget, esta tomar como valor el elemento que tenga el foco. Pero en este caso no es posible mantener el foco sobre la caja de texto del cual se quiere Cortar, Copiar o Pegar y a la vez dar clic sobre uno de los botones (pues para dar clic el foco lo toma el botn). Sin embargo con la propiedad IsFocusScope de tipo bool podemos especificar mbitos de foco para un control. El cdigo del Listado 5-7 nos ilustra cmo lograr esto. Se le ha dado un mbito de foco al StackPanel que contiene a los tres botones de modo que el StackPanel y los TextBox estarn ahora en mbitos de foco distintos. Note ahora en el Listado 5-7 como prescindimos de la propiedad CommandTarget pues automticamente esta tomar como valor el elemento que contenga el foco y si adems este tiene la capacidad de manejar el comando (como es el caso de los TextBox para los comandos Cut, Copy y Paste) pues ya todo estar listo.
FocusManager.IsFocusScope="True"
Orientation="Horizontal" HorizontalAlignment="Center" Background="Red" Margin="10"> <Button Command="Cut" FontSize="18"> Cortar </Button> <Button Command="Copy" FontSize="18"> Copiar </Button> <Button Command="Paste" FontSize="18"> Pegar </Button> </StackPanel> </StackPanel>
Pgina 13 de 17
Note que en la Figura 5-6 Los tres botones bordeados de rojo (color de fondo del StackPanel que los contiene) tienen su propio mbito de foco (definido por su contenedor StackPanel), lo cual quiere decir que los comandos definidos en estos botones son aplicables en cualquier elemento fuera de su mbito de foco, entiendase en este caso a ambos controles TexBox pues al no asignar valor a la propiedad CommandTarget esta tomar como valor el elemento que tenga el foco.
Asociiando un comando a un ellemento que no tiiene soporte Asoc ando un comando a un e emento que no t ene soporte para manejjarllo.. para mane ar o
Hasta ahora hemos visto el desarrollo de un ejemplo en el cual el elemento sobre el que se aplica el comando conoce como manejarlo. Pero sera incorrecto terminar esta leccin si plantearnos la siguiente interrogante Qu pasa entonces si el elemento "destino" del comando no cuenta con soporte para el mismo? Para entender esto mejor veamos por ejemplo como asociar el comando Close a un botn para cerrar una ventana cuando se haga clic sobre ste. La ventana (Window)
Pgina 14 de 17
no tiene definido como manejar el comando Close, por lo que habr que asociar una lgica a la ejecucin del comando mediante CommandBinding (Listado 5-8).
Note como hemos definido en la ventana un objeto CommandBinding que relaciona el comando con una lgica determinada. Ahora hay que ir al code-behind y dar implementacin a CloseCommandHandler y CloseCommandCanExecuteHandler manejadores que se han asociado al Executed y CanExecuted de CommandBinding. Este es un ejemplo en que es necesario escribir cdigo en el code-behind (Listado 5-9) pues de otra manera no habr un elemento en la jerarqua que de funcionalidad al comando Close.
Pgina 15 de 17
La Figura 5-7 muestra el resultado de la ejecucin. Note como se deshabilita el botn (Figura 5-8 derecha) si el mtodo CloseCommandCanExecuteHandler retorna false debido a que no se ha seleccionado el CheckBox. Ahora Ud. puede "jugar" un poco con este cdigo agregando condiciones para la ejecucin del comando.
Pgina 16 de 17
Pgina 17 de 17