Información general sobre comandos

Los comandos constituyen un mecanismo de entrada en Windows Presentation Foundation (WPF) que ofrece la administración de acciones del usuario en un nivel más semántico que la entrada de dispositivo. Los ejemplos de comandos son las operaciones Copiar, Cortary Pegar que se encuentran en muchas aplicaciones.

Esta información general define qué comandos hay en WPF, qué clases forman parte del modelo de comandos, y cómo utilizar y crear comandos en las aplicaciones.

Este tema contiene las siguientes secciones:

  • ¿Qué son los comandos?

  • Ejemplo de comando simple en WPF

  • Cuatro conceptos básicos en los comandos WPF

  • Biblioteca de comandos

  • Crear comandos personalizados

¿Qué son los comandos?

Los comandos tienen varios propósitos. El primer propósito es separar la semántica y el objeto que invoca un comando de la lógica que lo ejecuta. Esto permite que varios orígenes dispares invoquen la misma lógica de comando y permite personalizar la lógica de comando para diferentes destinos. Por ejemplo, las operaciones de edición Copiar, Cortar y Pegar, que se encuentran en muchas aplicaciones, se pueden invocar mediante distintas acciones de usuario si se implementan con comandos. Una aplicación podría permitir a un usuario cortar texto u objetos seleccionados haciendo clic en un botón, eligiendo un elemento en un menú o utilizando una combinación de teclas, como CTRL+X. Mediante el uso de comandos, puede enlazar cada tipo de acción del usuario a la misma lógica.

Otro propósito de los comandos es indicar si una acción está disponible. Para continuar el ejemplo de cortar un objeto o texto, la acción tiene sentido sólo cuando algo está seleccionado. Si un usuario intenta cortar un objeto o texto sin tener algo seleccionado, no pasaría nada. Para indicar esto al usuario, muchas aplicaciones deshabilitan los botones y elementos de menú para que sepa si es posible realizar una acción. Un comando puede indicar si una acción es posible implementando el método CanExecute. Un botón puede suscribirse al evento CanExecuteChanged y estar deshabilitado si CanExecute devuelve false o estar habilitado si CanExecute devuelve true.

La semántica de un comando puede ser coherente entre aplicaciones y clases, pero la lógica de la acción es específica del objeto sobre el que se actúa. La combinación de teclas CTRL+X invoca el comando Cortar en clases de texto, clases de imagen y exploradores web, pero la lógica real para realizar la operación Cortar la define la aplicación que la realiza. RoutedCommand permite a los clientes implementar la lógica. Un objeto de texto puede cortar el texto seleccionado y pegarlo en el portapapeles, mientras que un objeto de imagen puede cortar la imagen seleccionada. Cuando una aplicación controla el evento Executed, tiene acceso al destino del comando y puede realizar la acción correspondiente en función del tipo del destino.

Ejemplo de comando simple en WPF

La manera más simple de utilizar un comando en WPF es utilizar un objeto RoutedCommand predefinido de una de las clases de biblioteca de comandos; utilice un control con compatibilidad nativa para administrar el comando; y utilice un control con compatibilidad nativa para invocar un comando. El comando Paste es uno de los comandos predefinidos de la clase ApplicationCommands. El control TextBox tiene lógica integrada para administrar el comando Paste. La clase MenuItem tiene compatibilidad nativa para invocar comandos.

En el ejemplo siguiente se muestra cómo configurar un elemento MenuItem para que, cuando se haga clic en él, invoque el comando Paste en un objeto TextBox, suponiendo que TextBox tenga el foco de teclado.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
            ' Creating the UI objects
            Dim mainStackPanel As New StackPanel()
            Dim pasteTextBox As New TextBox()
            Dim stackPanelMenu As New Menu()
            Dim pasteMenuItem As New MenuItem()

            ' Adding objects to the panel and the menu
            stackPanelMenu.Items.Add(pasteMenuItem)
            mainStackPanel.Children.Add(stackPanelMenu)
            mainStackPanel.Children.Add(pasteTextBox)

            ' Setting the command to the Paste command
            pasteMenuItem.Command = ApplicationCommands.Paste
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;

Cuatro conceptos básicos en los comandos WPF

El modelo de comando enrutado de WPF se puede descomponer en cuatro conceptos básicos: el comando, el origen del comando, el destino del comando y el enlace del comando:

  • El comando es la acción que se va a ejecutar.

  • El origen del comando es el objeto que invoca el comando.

  • El destino del comando es el objeto en el que se ejecuta el comando.

  • El enlace del comando es el objeto que asigna la lógica de comando al comando.

En el ejemplo anterior, el comando Paste es el comando, el objeto MenuItem es el origen del comando, el objeto TextBox es el destino del comando y en enlace del comando lo proporciona el control TextBox. Hay que indicar que no siempre sucede que el objeto CommandBinding lo suministre el control que es la clase de destino del comando. Con frecuencia, el objeto CommandBinding debe ser creado por el desarrollador de la aplicación; también puede ocurrir que el objeto CommandBinding esté asociado a un antecesor del destino del comando.

Comandos

Los comandos de WPF se crean mediante la implementación de la interfaz ICommand. ICommand expone dos métodos, Execute y CanExecute, y un evento, CanExecuteChanged. Execute realiza las acciones que están asociadas al comando. CanExecute determina si el comando se puede ejecutar en el destino del comando actual. El evento CanExecuteChanged se produce si el administrador de comandos que centraliza las operaciones de comandos detecta un cambio en el origen del comando que podría invalidar un comando que se ha iniciado pero que el enlace de comando aún no ha ejecutado. La implementación de WPF de ICommand es la clase RoutedCommand y el centro de interés de esta información general.

Los principales orígenes de entrada en WPF son el mouse, el teclado, la entrada de lápiz y los comandos enrutados. Las entradas más orientadas a dispositivo utilizan un objeto RoutedEvent para notificar a los objetos de una página de aplicación que se ha producido un evento de entrada. Un RoutedCommand no es diferente. Los métodos Execute y CanExecute de una objeto RoutedCommand no contienen la lógica de aplicación para el comando, sino que provocan eventos enrutados que se tunelizan y se propagar a través del elemento hasta que encuentran un objeto con CommandBinding. El objeto CommandBinding contiene los controladores para estos eventos y son los controladores los que realizan el comando. Para obtener más información sobre el enrutamiento de eventos en WPF, vea Información general sobre eventos enrutados.

El método Execute de un objeto RoutedCommand provoca los eventos PreviewExecuted y Executed en el destino del comando. El método CanExecute de un objeto RoutedCommand provoca los eventos CanExecute y PreviewCanExecute en el destino del comando. Estos eventos se tunelizan y se propagan a través del árbol de elementos hasta que encuentran un objeto que tiene un objeto CommandBinding para ese comando en particular.

WPF proporciona un conjunto de comandos enrutados comunes distribuido entre varias clases: MediaCommands, ApplicationCommands, NavigationCommands, ComponentCommands y EditingCommands. Estas clases constan solamente de los objetos RoutedCommand y no de la lógica de implementación del comando. La lógica de implementación es responsabilidad del objeto en el que se ejecuta el comando.

Orígenes de comando

Un origen de comando es el objeto que invoca el comando. Ejemplos de orígenes de comando son MenuItem, Button y KeyGesture.

Los orígenes de comando en WPF implementan generalmente la interfaz ICommandSource.

ICommandSource expone tres propiedades: Command, CommandTarget y CommandParameter:

  • Command es el comando a ejecutar cuando se invoca el origen de comando.

  • CommandTarget es el objeto en el que se ejecuta el comando. Hay que destacar que en WPF la propiedad CommandTarget de ICommandSource solamente es aplicable cuando la interfaz ICommand es un objeto RoutedCommand. Si se establece la propiedad CommandTarget en un objeto ICommandSource y el comando correspondiente no es de tipo RoutedCommand, se omite el destino del comando. Si no se establece CommandTarget, el destino del comando será el elemento que tenga el foco del teclado.

  • CommandParameter es un tipo de datos definido por el usuario utilizado para pasar información a los controladores que implementan el comando.

Las clases WPF que implementan ICommandSource son ButtonBase, MenuItem, Hyperlink y InputBinding. ButtonBase, MenuItemy Hyperlink invocan un comando cuando se hace clic en ellos y InputBinding invoca un comando cuando se realiza el InputGesture asociado.

En el ejemplo siguientes se muestra cómo usar un objeto MenuItem en un objeto ContextMenu como origen de comando para el comando Properties.

<StackPanel>
  <StackPanel.ContextMenu>
    <ContextMenu>
      <MenuItem Command="ApplicationCommands.Properties" />
    </ContextMenu>
  </StackPanel.ContextMenu>
</StackPanel>
            Dim cmdSourcePanel As New StackPanel()
            Dim cmdSourceContextMenu As New ContextMenu()
            Dim cmdSourceMenuItem As New MenuItem()

            ' Add ContextMenu to the StackPanel.
            cmdSourcePanel.ContextMenu = cmdSourceContextMenu
            cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem)

            ' Associate Command with MenuItem.
            cmdSourceMenuItem.Command = ApplicationCommands.Properties
StackPanel cmdSourcePanel = new StackPanel();
ContextMenu cmdSourceContextMenu = new ContextMenu();
MenuItem cmdSourceMenuItem = new MenuItem();

// Add ContextMenu to the StackPanel.
cmdSourcePanel.ContextMenu = cmdSourceContextMenu;
cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem);

// Associate Command with MenuItem.
cmdSourceMenuItem.Command = ApplicationCommands.Properties;

Habitualmente, un origen de comando escuchará el evento CanExecuteChanged. Este evento informa al origen de comando que es posible que haya cambiado la capacidad del comando para ejecutarse en el destino de comando actual. El origen de comando puede consultar el estado actual del objeto RoutedCommand utilizando el método CanExecute. El origen de comando se puede deshabilitar a sí mismo si no se puede ejecutar el comando. Un ejemplo de esto es un objeto MenuItem que se atenúa cuando un comando no se puede ejecutar.

Un objeto InputGesture se puede utilizar como origen de comando. Dos tipos de movimientos de entrada en WPF son KeyGesture y MouseGesture. Puede pensar en un objeto KeyGesture como en un método abreviado de teclado, tal como CTRL+C. Un objeto KeyGesture consta de un objeto Key y un conjunto de ModifierKeys. Un objeto MouseGesture consta de un objeto MouseAction y un conjunto opcional de ModifierKeys.

Para que un InputGesture actúe como un origen de comando, debe estar asociado a un comando. Hay varias maneras de lograrlo. Una manera es utilizar un objeto InputBinding.

En el ejemplo siguiente se muestra cómo crear un objeto KeyBinding entre un objeto KeyGesture y un objeto RoutedCommand.

<Window.InputBindings>
  <KeyBinding Key="B"
              Modifiers="Control" 
              Command="ApplicationCommands.Open" />
</Window.InputBindings>
            Dim OpenKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)

            Dim OpenCmdKeybinding As New KeyBinding(ApplicationCommands.Open, OpenKeyGesture)

            Me.InputBindings.Add(OpenCmdKeybinding)
KeyGesture OpenKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

KeyBinding OpenCmdKeybinding = new KeyBinding(
    ApplicationCommands.Open,
    OpenKeyGesture);

this.InputBindings.Add(OpenCmdKeybinding);

Otra manera de asociar un objeto InputGesture a un objeto RoutedCommand es agregar el objeto InputGesture a la colección InputGestureCollection del objeto RoutedCommand.

En el ejemplo siguiente se muestra cómo agregar un objeto KeyGesture a la colección InputGestureCollection de un objeto RoutedCommand.

            Dim OpenCmdKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)

            ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture)
KeyGesture OpenCmdKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture);

CommandBinding

Un objeto CommandBinding asocia un comando con los controladores de eventos que implementan el comando.

La clase CommandBinding contiene una propiedad Command, y eventos PreviewExecuted, Executed, PreviewCanExecute y CanExecute.

Command es el comando al que se está asociando CommandBinding. Los controladores de eventos asociados a los eventos PreviewExecuted y Executed implementan la lógica del comando. Los controladores de eventos asociados a los eventos PreviewCanExecute y CanExecute determinan si el comando puede ejecutarse en el destino de comando actual.

En el ejemplo siguiente se muestra cómo crear un objeto CommandBinding en el objeto Window raíz de una aplicación. El comando CommandBinding asocia el comando Open con los controladores Executed y CanExecute.

<Window.CommandBindings>
  <CommandBinding Command="ApplicationCommands.Open"
                  Executed="OpenCmdExecuted"
                  CanExecute="OpenCmdCanExecute"/>
</Window.CommandBindings>
            ' Creating CommandBinding and attaching an Executed and CanExecute handler
            Dim OpenCmdBinding As New CommandBinding(ApplicationCommands.Open, AddressOf OpenCmdExecuted, AddressOf OpenCmdCanExecute)

            Me.CommandBindings.Add(OpenCmdBinding)
// Creating CommandBinding and attaching an Executed and CanExecute handler
CommandBinding OpenCmdBinding = new CommandBinding(
    ApplicationCommands.Open,
    OpenCmdExecuted,
    OpenCmdCanExecute);

this.CommandBindings.Add(OpenCmdBinding);

A continuación, se crea el objeto ExecutedRoutedEventHandler y un objeto CanExecuteRoutedEventHandler. El objeto ExecutedRoutedEventHandler abre un control MessageBox que muestra una cadena que indica que se ha ejecutado el comando. CanExecuteRoutedEventHandler establece la propiedad CanExecute en true.

Private Sub OpenCmdExecuted(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
    Dim command, targetobj As String
    command = CType(e.Command, RoutedCommand).Name
    targetobj = CType(sender, FrameworkElement).Name
    MessageBox.Show("The " + command + " command has been invoked on target object " + targetobj)
End Sub
void OpenCmdExecuted(object target, ExecutedRoutedEventArgs e)
{
    String command, targetobj;
    command = ((RoutedCommand)e.Command).Name;
    targetobj = ((FrameworkElement)target).Name;
    MessageBox.Show("The " + command +  " command has been invoked on target object " + targetobj);
}
Private Sub OpenCmdCanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
    e.CanExecute = True
End Sub
void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}

Un objeto CommandBinding se asocia a un objeto específico, tal como el objeto Window raíz de la aplicación o un control. El objeto al que se asocia CommandBinding define el ámbito del enlace. Por ejemplo, un objeto CommandBinding asociado a un antecesor del destino de comando puede ser alcanzado por el evento Executed, pero un objeto CommandBinding asociado a un descendiente del destino de comando no se puede alcanzar. Ésta es una consecuencia directa de la manera en que los objetos RoutedEvent se tuneliza y propagan desde el objeto que provoca el evento.

En algunas situaciones, el objeto CommandBinding está asociado al propio destino de comando, como ocurre con la clase TextBox y los comandos Cut, Copy y Paste. Bastante a menudo, sin embargo, es más cómodo asociar el objeto CommandBinding a un antecesor del destino de comando, tal como el objeto Window principal o el objeto Application, sobre todo si el mismo objeto CommandBinding puede utilizarse para varios destinos de comando. Estas son decisiones de diseño que deberá considerar cuando cree la infraestructura de comandos.

Destino de comando

El destino de comando es el elemento en el que se ejecuta el comando. En lo referente a un objeto RoutedCommand, el destino de comando es el elemento en el que se inicia el enrutado de CanExecute y Executed. Como se indicó previamente, en WPF, la propiedad CommandTarget de ICommandSource solamente se aplica cuando ICommand es RoutedCommand. Si se establece la propiedad CommandTarget en un objeto ICommandSource y el comando correspondiente no es de tipo RoutedCommand, se omite el destino del comando.

El origen de comando puede establecer explícitamente el destino de comando. Si el destino de comando no está definido, se utilizará como destino de comando el elemento que tenga el foco de teclado. Una de las ventajas de utilizar el elemento con el foco de teclado como destino de comando es que permite al desarrollador de aplicaciones utilizar el mismo origen de comando para invocar un comando en varios destinos, sin tener que hacer un seguimiento del destino de comando. Por ejemplo, si un objeto MenuItem invoca el comando Pegar en una aplicación que tiene un control TextBox y un control PasswordBox, el destino puede ser el objeto TextBox o PasswordBox según qué control tenga el foco de teclado.

En el ejemplo siguiente se muestra cómo establecer explícitamente el destino de comando en marcado y en código subyacente.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste"
              CommandTarget="{Binding ElementName=mainTextBox}" />
  </Menu>
  <TextBox Name="mainTextBox"/>
</StackPanel>
            ' Creating the UI objects
            Dim mainStackPanel As New StackPanel()
            Dim pasteTextBox As New TextBox()
            Dim stackPanelMenu As New Menu()
            Dim pasteMenuItem As New MenuItem()

            ' Adding objects to the panel and the menu
            stackPanelMenu.Items.Add(pasteMenuItem)
            mainStackPanel.Children.Add(stackPanelMenu)
            mainStackPanel.Children.Add(pasteTextBox)

            ' Setting the command to the Paste command
            pasteMenuItem.Command = ApplicationCommands.Paste
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;

CommandManager

El objeto CommandManager desempeña varias funciones relacionadas con comandos. Proporciona un conjunto de métodos estáticos para agregar y quitar controladores de eventos PreviewExecuted, Executed, PreviewCanExecute y CanExecute de un elemento concreto. Proporciona un medio para registrar objetos CommandBinding y InputBinding en una clase concreta. El objeto CommandManager también proporciona un medio, a través del evento RequerySuggested, para notificar a un comando cuándo debe provocar el evento CanExecuteChanged.

El método InvalidateRequerySuggested fuerza al objeto CommandManager a provocar el evento RequerySuggested. Esto es útil para condiciones que deban deshabilitar o habilitar un comando pero que no conozca el objeto CommandManager.

Biblioteca de comandos

WPF proporciona un conjunto de comandos predefinidos. La biblioteca de comandos está compuesta de las clases siguientes: ApplicationCommands, NavigationCommands, MediaCommands, EditingCommandsy ComponentCommands. Estas clases proporcionan comandos como Cut, BrowseBack y BrowseForward, Play, Stop y Pause.

Muchos de estos comandos incluyen un conjunto de enlaces de entrada predeterminados. Por ejemplo, si especifica que la aplicación administra el comando de copia, obtendrá automáticamente el enlace de teclado "CTRL + C". También obtendrá los enlaces para otros dispositivos de entrada, como los movimientos de lápiz de Tablet PC e información de voz.

Cuando haga referencia a comandos de las diversas bibliotecas de comandos utilizando XAML, habitualmente podrá omitir el nombre de clase de la clase de biblioteca que exponga la propiedad de comando estática. Generalmente, los nombres de comando son inequívocos como cadenas y los tipos propietarios existen para proporcionar una agrupación lógica de comandos, pero no son necesarios para la anulación de ambigüedades. Por ejemplo, puede especificar Command="Cut" en lugar de Command="ApplicationCommands.Cut", más detallado. Éste es un mecanismo de conveniencia integrado en el procesador WPFXAML para comandos (más concretamente, es un comportamiento del convertidor de tipos de ICommand, al que el procesador de WPFXAML hace referencia en el momento de la carga).

Crear comandos personalizados

Si los comandos de las clases de la biblioteca de comandos no satisfacen sus necesidades, puede crear comandos propios. Hay dos formas de crear un comando personalizado. La primera es empezar desde la nada e implementar la interfaz ICommand. La otra manera, el enfoque más común consiste en crear un objeto RoutedCommand o RoutedUICommand.

Para obtener un ejemplo de la creación de un objeto RoutedCommand personalizado, vea Ejemplo Create a Custom RoutedCommand.

Vea también

Tareas

Cómo: Implementar ICommandSource

Cómo: Agregar un comando a un elemento de menú

Referencia

RoutedCommand

CommandBinding

InputBinding

CommandManager

Conceptos

Información general sobre acciones del usuario

Información general sobre eventos enrutados

Otros recursos

Ejemplo Create a Custom RoutedCommand