Visão geral de comandos

Commanding é um mecanismo de entrada no Windows Presentation Foundation (WPF) que fornece manipulação de entrada em um nível mais semântico do que a entrada de dispositivo. Os exemplos de comandos são as operações Copiar, Recortar e Colar, encontradas em muitos aplicativos.

Esta visão geral define quais comandos estão no WPF, quais classes fazem parte do modelo de comando e como usar e criar comandos em seus aplicativos.

Este tópico contém as seguintes seções:

O que são comandos

Os comandos têm várias finalidades. A primeira finalidade é separar a semântica e o objeto que invoca um comando da lógica que o executa. Isso permite que fontes múltiplas e distintas invoquem a mesma lógica de comando, além de permitir que a lógica de comando seja personalizada para destinos diferentes. Por exemplo, as operações de edição Copiar, Recortar e Colar, que são encontradas em muitos aplicativos, podem ser invocadas por meio de ações do usuário diferentes se forem implementadas por meio do uso de comandos. Um aplicativo pode permitir que um usuário recorte texto ou objetos selecionados ao clicar em um botão, escolher um item em um menu ou usar uma combinação de teclas, como CTRL+X. Usando comandos, é possível associar cada tipo de ação do usuário à mesma lógica.

Outra finalidade dos comandos é indicar se uma ação está disponível. Para continuar o exemplo de recortar um objeto ou texto, a ação só faz sentido quando algo está selecionado. Se um usuário tentar recortar um objeto ou texto sem ter selecionado alguma coisa, nada acontece. Para indicar isso para o usuário, muitos aplicativos desabilitam botões e itens de menu para indicar se é possível executar uma ação. Um comando pode indicar se uma ação é possível pela implementação do método CanExecute. Um botão pode assinar o evento CanExecuteChanged e ser desabilitado se CanExecute retorna false ou ser habilitado se CanExecute retorna true.

A semântica de um comando pode ser consistente entre aplicativos e classes; porém, a lógica da ação é específica para o objeto específico da ação. A combinação de teclas CTRL+X invoca o comando Recortar em classes de texto, classes de imagens e navegadores da Web, enquanto a lógica real para executar a operação Recortar é definida pelo aplicativo que faz o recorte. Um RoutedCommand permite que os clientes implementem a lógica. Um objeto de texto poderá recortar o texto selecionado na área de transferência, enquanto um objeto de imagem poderá recortar a imagem selecionada. Quando um aplicativo manipula o evento Executed, ele tem acesso ao destino do comando e pode executar a ação adequada, dependendo do tipo de destino.

Exemplo de comando simples no WPF

A maneira mais simples de usar um comando no WPF é usar um predefinido RoutedCommand de uma das classes da biblioteca de comandos, usar um controle que tenha suporte nativo para manipular o comando e usar um controle que tenha suporte nativo para invocar um comando. O comando Paste é um dos comandos predefinidos na classe ApplicationCommands. O controle TextBox tem uma lógica interna para manipular o comando Paste. Além disso, a classe MenuItem tem suporte nativo para invocar comandos.

O exemplo a seguir mostra como configurar um MenuItem para que, quando ele receber um clique, ele invoque o comando Paste em uma TextBox, supondo que a TextBox tenha o foco do teclado.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
// 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;
' 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

Quatro conceitos principais em comandos do WPF

O modelo de comando roteado no WPF pode ser dividido em quatro conceitos principais: o comando, a origem do comando, o destino do comando e a associação de comando:

  • O comando é a ação que será executada.

  • A fonte do comando é o objeto que invoca o comando.

  • O destino do comando é o objeto no qual o comando está sendo executado.

  • A associação do comando é o objeto que mapeia a lógica de comando até o comando.

No exemplo anterior, o comando Paste é o comando, o MenuItem é a origem de comando, a TextBox é o destino de comando, e a associação de comando é fornecida pelo controle TextBox. Vale a pena observar que nem sempre a CommandBinding é fornecida pelo controle que é a classe de destino de comando. Com muita frequência, a CommandBinding precisa ser criada pelo desenvolvedor de aplicativos, ou a CommandBinding pode ser anexada a um ancestral do destino de comando.

Comandos

Os comandos no WPF são criados implementando a ICommand interface. ICommand expõe dois métodos, Execute, e CanExecutee um evento CanExecuteChanged. Execute executa as ações associadas ao comando. CanExecute determina se o comando pode ser executado no destino de comando atual. CanExecuteChanged é acionado quando o gerenciador de comandos que centraliza as operações de comandos detecta uma alteração na origem de comando que pode invalidar um comando que foi acionado, mas que ainda não foi executado pela associação de comando. A implementação do WPF de ICommand é a RoutedCommand classe e é o foco desta visão geral.

As principais fontes de entrada no WPF são o mouse, o teclado, a tinta e os comandos roteados. As entradas mais orientadas ao dispositivo usam um RoutedEvent para notificar aos objetos em uma página de aplicativo que ocorreu um evento de entrada. Um RoutedCommand não é diferente. Os métodos Execute e CanExecute de um RoutedCommand não contêm a lógica do aplicativo para o comando, mas acionam eventos roteados que passam por um túnel e se propagam pela árvore de elementos até encontrarem um objeto com uma CommandBinding. A CommandBinding contém os manipuladores para esses eventos e são os manipuladores que executam o comando. Para obter mais informações sobre roteamento de eventos no WPF, consulte Visão geral de eventos roteados.

O método Execute em um RoutedCommand aciona os eventos PreviewExecuted e Executed no destino de comando. O método CanExecute em um RoutedCommand aciona os eventos CanExecute e PreviewCanExecute no destino de comando. Esses eventos passam por um túnel e se propagam pela árvore de elementos até encontrarem um objeto que tem uma CommandBinding para esse comando específico.

O WPF fornece um conjunto de comandos roteados comuns espalhados por várias classes: MediaCommands, , ApplicationCommandsNavigationCommands, ComponentCommandse EditingCommands. Essas classes consistem apenas nos objetos RoutedCommand e não na lógica de implementação do comando. A lógica de implementação é de responsabilidade do objeto no qual o comando está sendo executado.

Fontes de comando

Uma fonte do comando é o objeto que invoca o comando. Alguns exemplos de origem de comando são MenuItem, Button e KeyGesture.

As fontes de comando no WPF geralmente implementam a ICommandSource interface.

ICommandSource expõe três propriedades: Command, CommandTarget e CommandParameter:

As classes WPF que implementam ICommandSource são ButtonBase, , MenuItemHyperlinke InputBinding. ButtonBase, MenuItem e Hyperlink invocam um comando quando eles recebem um clique, e uma InputBinding invoca um comando quando o InputGesture associado a ela é executado.

O exemplo a seguir mostra como usar um MenuItem em um ContextMenu como uma origem de comando para o comando Properties.

<StackPanel>
  <StackPanel.ContextMenu>
    <ContextMenu>
      <MenuItem Command="ApplicationCommands.Properties" />
    </ContextMenu>
  </StackPanel.ContextMenu>
</StackPanel>
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;
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

Normalmente, uma origem de comando escutará o evento CanExecuteChanged. Esse evento informa à fonte de comando que a capacidade do comando de executar o destino do comando atual pode ter sido alterada. A origem de comando pode consultar o status atual do RoutedCommand usando o método CanExecute. Em seguida, ela poderá ser desabilitada se não for possível executar o comando. Um exemplo disso é um MenuItem esmaecendo quando um comando não pode ser executado.

Um InputGesture pode ser usado como uma fonte do comando. Dois tipos de gestos de entrada no WPF são o KeyGesture e MouseGesture. Considere um KeyGesture como um atalho de teclado, como CTRL+C. Um KeyGesture é composto por uma Key e um conjunto de ModifierKeys. Um MouseGesture é composto por uma MouseAction e um conjunto opcional de ModifierKeys.

Para que um InputGesture funcione como uma origem de comando, ele deve estar associado a um comando. Existem algumas maneiras de fazer isso. Uma maneira é usar uma InputBinding.

O exemplo a seguir mostra como criar uma KeyBinding entre um KeyGesture e um RoutedCommand.

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

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

this.InputBindings.Add(OpenCmdKeybinding);
Dim OpenKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)

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

Me.InputBindings.Add(OpenCmdKeybinding)

Outra maneira de associar um InputGesture a um RoutedCommand é adicionar o InputGesture ao InputGestureCollection no RoutedCommand.

O exemplo a seguir mostra como adicionar um KeyGesture à InputGestureCollection de um RoutedCommand.

KeyGesture OpenCmdKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture);
Dim OpenCmdKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture)

CommandBinding

Uma CommandBinding associa um comando aos manipuladores de eventos que implementam o comando.

A classe CommandBinding contém uma propriedade Command e PreviewExecuted, Executed, PreviewCanExecute e CanExecute eventos.

Command é o comando ao qual a CommandBinding está sendo associada. Os manipuladores de eventos anexados aos eventos PreviewExecuted e Executed implementam a lógica de comando. Os manipuladores de eventos anexados aos eventos PreviewCanExecute e CanExecute determinam se o comando pode ser executado no destino de comando atual.

O exemplo a seguir mostra como criar uma CommandBinding na Window raiz de um aplicativo. A CommandBinding associa o comando Open aos manipuladores Executed e CanExecute.

<Window.CommandBindings>
  <CommandBinding Command="ApplicationCommands.Open"
                  Executed="OpenCmdExecuted"
                  CanExecute="OpenCmdCanExecute"/>
</Window.CommandBindings>
// Creating CommandBinding and attaching an Executed and CanExecute handler
CommandBinding OpenCmdBinding = new CommandBinding(
    ApplicationCommands.Open,
    OpenCmdExecuted,
    OpenCmdCanExecute);

this.CommandBindings.Add(OpenCmdBinding);
' Creating CommandBinding and attaching an Executed and CanExecute handler
Dim OpenCmdBinding As New CommandBinding(ApplicationCommands.Open, AddressOf OpenCmdExecuted, AddressOf OpenCmdCanExecute)

Me.CommandBindings.Add(OpenCmdBinding)

Em seguida, o ExecutedRoutedEventHandler e um CanExecuteRoutedEventHandler são criados. O ExecutedRoutedEventHandler abre uma MessageBox que exibe uma cadeia de caracteres indicando que o comando foi executado. O CanExecuteRoutedEventHandler define a propriedade CanExecute como true.

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 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 OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}
Private Sub OpenCmdCanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
    e.CanExecute = True
End Sub

Uma CommandBinding está anexada a um objeto específico, como a Window raiz do aplicativo ou de um controle. O objeto ao qual a CommandBinding está anexada define o escopo da associação. Por exemplo, uma CommandBinding anexada a um ancestral do destino de comando pode ser acessada pelo evento Executed, mas uma CommandBinding anexada a um descendente do destino de comando não pode ser acessada. Essa é uma consequência direta da forma como um RoutedEvent passa por um túnel e se propaga do objeto que aciona o evento.

Em algumas situações, a CommandBinding está anexada ao próprio destino de comando, como ocorre com a classe TextBox e os comandos Cut, Copy e Paste. No entanto, muitas vezes, é mais conveniente anexar a CommandBinding a um ancestral do destino de comando, como a Window principal ou o objeto Application, especialmente se a mesma CommandBinding pode ser usada para vários destinos de comando. São decisões de design que você deverá considerar no momento de criar sua infraestrutura de comando.

Destino do comando

O destino do comando é o elemento no qual o comando é executado. Com relação a um RoutedCommand, o destino de comando é o elemento no qual o roteamento do Executed e do CanExecute é iniciado. Como observado anteriormente, no WPF a CommandTarget propriedade on ICommandSource só é aplicável quando o ICommand é um RoutedCommandarquivo . Se o CommandTarget estiver definido em uma ICommandSource e o comando correspondente não for um RoutedCommand, o destino de comando será ignorado.

A fonte do comando pode definir explicitamente o destino do comando. Se o destino do comando não for definido, o elemento com o foco do teclado será usado como destino do comando. Uma das vantagens de usar o elemento com o foco do teclado como destino do comando é que ele permite que o desenvolvedor do aplicativo use a mesma fonte de comando para invocar um comando em vários destinos, sem a necessidade de controlar o destino do comando. Por exemplo, se um MenuItem invoca o comando Paste em um aplicativo que tem um controle TextBox e um controle PasswordBox, o destino pode ser a TextBox ou a PasswordBox, dependendo de qual controle tem o foco do teclado.

O exemplo a seguir mostra como definir explicitamente o destino do comando na marcação e no code-behind.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste"
              CommandTarget="{Binding ElementName=mainTextBox}" />
  </Menu>
  <TextBox Name="mainTextBox"/>
</StackPanel>
// 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;
' 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

O CommandManager

O CommandManager fornece várias funções relacionadas a um comando. Ele fornece um conjunto de métodos estáticos para adicionar os manipuladores de eventos PreviewExecuted, Executed, PreviewCanExecute e CanExecute a um elemento específico e removê-los de um deles. Ele fornece um meio para registrar objetos CommandBinding e InputBinding em uma classe específica. O CommandManager também fornece um meio, por meio do evento RequerySuggested, para notificar um comando quando ele deve acionar o evento CanExecuteChanged.

O método InvalidateRequerySuggested força o CommandManager a acionar o evento RequerySuggested. Isso é útil em condições que devem desabilitar/habilitar um comando, mas que não são condições reconhecidas pelo CommandManager.

Biblioteca de comandos

WPF fornece um conjunto de comandos predefinidos. A biblioteca de comandos consiste nas seguintes classes: ApplicationCommands, NavigationCommands, MediaCommands, EditingCommands e o ComponentCommands. Essas classes fornecem comandos como Cut, BrowseBack e BrowseForward, Play, Stop e Pause.

Muitos desses comandos incluem um conjunto de associações de entrada padrão. Por exemplo, se você especificar que seu aplicativo manipula o comando copy, você obterá automaticamente a vinculação de teclado "CTRL+C" Você também obterá ligações para outros dispositivos de entrada, como gestos de caneta do Tablet PC e informações de fala.

Quando você faz referência a comandos nas várias bibliotecas de comandos usando XAML, geralmente pode omitir o nome da classe da biblioteca que expõe a propriedade static command. De modo geral, os nomes dos comandos são cadeias de caracteres não ambíguas; os tipos de propriedade existem para fornecer um agrupamento lógico de comandos, mas não são necessários para eliminar a ambiguidade. Por exemplo, é possível especificar o Command="Cut" em vez do Command="ApplicationCommands.Cut", mais detalhado. Esse é um mecanismo de conveniência interno ao processador XAML do WPF para comandos (mais precisamente, é um comportamento de conversor de tipo de ICommand, ao qual o processador XAML do WPF faz referência no momento do carregamento).

Criando comandos personalizados

Se os comandos nas classes de biblioteca de comandos não satisfizerem suas necessidades, você poderá criar seus próprios comandos. Há duas maneiras de criar um comando personalizado. A primeira é começar do início e implementar a interface ICommand. A outra forma, e a abordagem mais comum, é criar um RoutedCommand ou RoutedUICommand.

Para obter um exemplo de como criar um RoutedCommand personalizado, confira Criar uma amostra de um RoutedCommand personalizado.

Confira também