This article has been localized into Spanish by the community.
Utilizando comandos en WPF
En el artículo anterior, hemos resumido la teoría acerca de qué son los comandos y como funcionan. En éste, veremos como se utilizan realmente, asignando comandos a elementos de la interfaz de usuario y creando los enlaces necesarios entre ellos.
Empezaremos con un ejemplo sencillo:
<Window x:Class="WpfTutorialSamples.Commands.UsingCommandsSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="UsingCommandsSample" Height="100" Width="200">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" Executed="NewCommand_Executed" CanExecute="NewCommand_CanExecute" />
</Window.CommandBindings>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Command="ApplicationCommands.New">New</Button>
</StackPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
namespace WpfTutorialSamples.Commands
{
public partial class UsingCommandsSample : Window
{
public UsingCommandsSample()
{
InitializeComponent();
}
private void NewCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("The New command was invoked");
}
}
}
Enlazamos el comando en la Window, añadiéndolo a su colección CommandBindings. Especificamos el comando que queremos 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 asociamos el comando mediante la propiedad Command.
En el código subyacente controlamos 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 queremos 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 ejecutas la aplicacion y pulsas el botón verás este mensaje. Un detalle a tener en cuenta es que el comando lleva asociado un atajo de teclado por defecto. Puedes probar a pulsar Ctrl+N en el teclado y verás que el resultado es el mismo.
Utilizando el método CanExecute
En el primer ejemplo, implementamos un evento CanExecute que sólo devuelve verdadero, de modo que el botón estaría disponible todo el tiempo. Sin embargo, esto, por supuesto, no es cierto para todos los botones; en muchos casos, usted desea que el botón se habilite o deshabilite dependiendo de algún tipo de estado en su aplicación.
Un ejemplo muy común de esto es alternar botones para usar el Portapapeles de Windows, donde 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. Esto es exactamente lo que lograremos en este ejemplo:
<Window x:Class="WpfTutorialSamples.Commands.CommandCanExecuteSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CommandCanExecuteSample" Height="200" Width="250">
<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">_Cut</Button>
<Button Command="ApplicationCommands.Paste" Width="60" Margin="3,0">_Paste</Button>
</WrapPanel>
<TextBox AcceptsReturn="True" Name="txtEditor" />
</DockPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
namespace WpfTutorialSamples.Commands
{
public partial class CommandCanExecuteSample : Window
{
public CommandCanExecuteSample()
{
InitializeComponent();
}
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();
}
}
}
Entonces, tenemos 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-detrás (Code-behind), tenemos 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, verás que aplico alguna lógica para decidir cuando una acción puede ser ser ejecutada o no y entonces asigno el valor "return" al CanExecute en los EventArgs
La parte interesante de esto es que no tienes que llamar a esos métodos para tener tus botones actualizados, WPF hace esto automáticamente cuando la aplicación tiene un momento libre, asegurándose que tu interfaz se mantiene actualizada todo el tiempo.
Comportamiento Predeterminado de un comando y CommandTarget
Como vimos en el ejemplo anterior, manejar un conjunto de comandos puede conducir a un poco de código, siendo un montón de declaraciones de metodos y una lógica muy estándar. Esto es por lo que probablemente el equipo de WPF decidió manejar algunas por ti. De hecho, podríamos haber evitado todo el Code-behind en el ejemplo anterior, porque un TextBox de WPF puede manejar automáticamente los comandos comunes como cortar, copiar, pegar, deshacer y rehacer.
WPF hace esto manejando los eventos Executed y CanExecute por ti, cuando un control de entrada de texto como el Textbox tiene el foco. Eres libre de saltarte estos eventos, los cual es básicamente lo que hicimos en el ejemplo anterior, pero si solo quieres el comportamiento básico, puedes dejar que WPF conecte los commandos y el control TextBox para que hagan el trabajo por ti. Solo mira que tan simple es este ejemplo:
<Window x:Class="WpfTutorialSamples.Commands.CommandsWithCommandTargetSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CommandsWithCommandTargetSample" Height="200" Width="250">
<DockPanel>
<WrapPanel DockPanel.Dock="Top" Margin="3">
<Button Command="ApplicationCommands.Cut" CommandTarget="{Binding ElementName=txtEditor}" Width="60">_Cut</Button>
<Button Command="ApplicationCommands.Paste" CommandTarget="{Binding ElementName=txtEditor}" Width="60" Margin="3,0">_Paste</Button>
</WrapPanel>
<TextBox AcceptsReturn="True" Name="txtEditor" />
</DockPanel>
</Window>
No es necesario Code-behinid para este ejemplo - WPF trata con todo esto por nosotros, pero sólo porque nosotros queremos usar estos comandos específicos para este control específico. El TextBox hace el trabajo por nosotros.
Fijate como uso las propiedades CommandTarget en los botones, para enlazar los comandos a nuestro control TextBox. Esto se requiere en este ejemplo particular, por que el WrapPanel no maneja el foco a como lo haría por ejemplo un Toolbar o un Menú, pero también tiene bastante sentido darle un target a los comandos.
Resumen
Tratar con los comandos es bastante sencillo, pero implica un poco de código y marcado extra. La recompensa es especialmente obvia cuando necesitas invocar la misma acción desde múltiples sitios, o cuando usas comandos pre-definidos que WPF puede manejar completamente por ti, como vimos en el último ejemplo.