Partilhar via


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. Exemplos de comandos são as operações Copy, Cute Paste 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. O primeiro objetivo é separar a semântica e o objeto que invoca um comando da lógica que executa o comando. Isso permite que várias fontes diferentes invoquem a mesma lógica de comando e permite que a lógica de comando seja personalizada para destinos diferentes. Por exemplo, as operações de edição Copiar, Recortare Colar, que são encontradas em muitos aplicativos, podem ser invocadas usando diferentes ações do usuário se forem implementadas usando comandos. Um aplicativo pode permitir que um usuário corte objetos ou texto selecionados clicando em um botão, escolhendo um item em um menu ou usando uma combinação de teclas, como CTRL+X. Usando comandos, você pode vincular 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 cortar um objeto ou texto, a ação só faz sentido quando algo é selecionado. Se um usuário tentar cortar um objeto ou texto sem ter nada selecionado, nada acontecerá. Para indicar isso ao usuário, muitos aplicativos desativam botões e itens de menu para que o usuário saiba se é possível executar uma ação. Um comando pode indicar se uma ação é possível implementando o método CanExecute. Um botão pode se inscrever no evento CanExecuteChanged e ser desativado se CanExecute retornar false ou ser habilitado se CanExecute retornar true.

A semântica de um comando pode ser consistente entre aplicativos e classes, mas a lógica da ação é específica para o objeto específico atuado. A combinação de teclas CTRL+X invoca o comando Cut em classes de texto, classes de imagem e navegadores da Web, mas a lógica real para executar a operação Cut é definida pelo aplicativo que executa o corte. Um RoutedCommand permite que os clientes implementem a lógica. Um objeto de texto pode cortar o texto selecionado na área de transferência, enquanto um objeto de imagem pode cortar a imagem selecionada. Quando um aplicativo manipula o evento Executed, ele tem acesso ao destino do comando e pode tomar as medidas apropriadas dependendo do tipo de destino.

Exemplo de comando simples no WPF

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

O exemplo a seguir mostra como configurar um MenuItem para que, quando clicado, ele invoque o comando Paste em um TextBox, supondo que o TextBox tenha foco no 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 no comando 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 ligação do comando:

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

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

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

  • A ligação de comando , associada ao, é o objeto que mapeia a lógica do comando para o comando.

No exemplo anterior, o comando Paste é o comando, o MenuItem é a origem do comando, o TextBox é o destino do comando e a ligação de comando é fornecida pelo controle TextBox. Vale a pena notar que nem sempre o CommandBinding é fornecido pelo controle que é a classe de destino de comando. Muitas vezes, o CommandBinding deve ser criado pelo criador da aplicação, ou o CommandBinding pode ser anexado a um antecessor do alvo do comando.

Comandos

Os comandos no WPF são criados implementando a interface ICommand. ICommand expõe dois métodos, Execute, e CanExecute, e 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 é gerado se o gerenciador de comandos que centraliza as operações de comando detetar uma alteração na fonte de comando que pode invalidar um comando que foi gerado, mas ainda não executado pela associação de comando. A implementação do WPF de ICommand é a classe RoutedCommand 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 para o dispositivo usam um RoutedEvent para notificar objetos em uma página de aplicativo de 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 da aplicação para o comando, mas geram eventos encaminhados que circulam e propagam-se pela árvore de elementos até chegarem a um objeto com um CommandBinding. O CommandBinding contém os manipuladores para esses eventos e são os manipuladores que executam o comando. Para obter mais informações sobre o encaminhamento de eventos no WPF, consulte a Visão Geral de Eventos Roteados .

O método Execute em um RoutedCommand gera os eventos PreviewExecuted e Executed no destino de comando. O método CanExecute num RoutedCommand dispara os eventos CanExecute e PreviewCanExecute no alvo do comando. Esses eventos tunnelizam e borbulham através da árvore de elementos até encontrarem um objeto que possui um CommandBinding para esse comando específico.

O WPF fornece um conjunto de comandos roteados comuns distribuídos em várias classes: MediaCommands, ApplicationCommands, NavigationCommands, 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 é a responsabilidade do objeto no qual o comando está sendo executado.

Fontes de comando

Uma fonte de comando é o objeto que invoca o comando. Exemplos de fontes de comando são MenuItem, Buttone KeyGesture.

Os códigos-fonte de comando no WPF geralmente implementam a interface ICommandSource.

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

As classes WPF que implementam ICommandSource são ButtonBase, MenuItem, Hyperlinke InputBinding. ButtonBase, MenuIteme Hyperlink invocam um comando quando são clicados e um InputBinding invoca um comando quando o InputGesture associado a ele é executado.

O exemplo a seguir mostra como usar um MenuItem em um ContextMenu como uma fonte 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 fonte de comando escutará o evento CanExecuteChanged. Esse evento informa à fonte de comando que a capacidade do comando de executar no destino de comando atual pode ter sido alterada. A fonte de comando pode consultar o status atual do RoutedCommand usando o método CanExecute. A origem do comando pode então desativar-se se o comando não puder ser executado. Um exemplo disso é quando um MenuItem fica acinzentado porque um comando não pode ser executado.

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

Para que um InputGesture atue como uma fonte de comando, ele deve ser associado a um comando. Existem algumas maneiras de conseguir isso. Uma maneira é usar um 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 ao 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

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

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

Command é o comando ao qual o CommandBinding está sendo associado. Os manipuladores de eventos que são 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 do comando atual.

O exemplo a seguir mostra como criar um CommandBinding na raiz Window de uma aplicação. O 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 um MessageBox que exibe uma cadeia de caracteres dizendo 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

Um CommandBinding é anexado a um objeto específico, como a Window raiz do aplicativo ou um controle. O objeto ao qual o CommandBinding está anexado define o escopo da vinculação. Por exemplo, um CommandBinding anexado a um ancestral do destino de comando pode ser alcançado pelo evento Executed, mas um CommandBinding anexado a um descendente do destino de comando não pode ser alcançado. Esta é uma consequência direta da forma como um RoutedEvent túneliza e bolha a partir do objeto que gera o evento.

Em algumas situações, o CommandBinding está ligado diretamente ao destino do comando, como no caso da classe TextBox e dos comandos Cut, Copye Paste. Muitas vezes, porém, é mais conveniente anexar o CommandBinding a um ancestral do destino de comando, como o Window principal ou o objeto Application, especialmente se o mesmo CommandBinding puder ser usado para vários destinos de comando. Estas são decisões de design que você vai querer considerar ao criar sua infraestrutura de comando.

Comando de destino

O destino do comando é o elemento no qual o comando é executado. No que diz respeito a um RoutedCommand, o alvo do comando é o elemento no qual o roteamento do Executed e do CanExecute começa. Como observado anteriormente, no WPF a propriedade CommandTarget em ICommandSource só é aplicável quando o ICommand é um RoutedCommand. Se o CommandTarget estiver definido em um ICommandSource e o comando correspondente não for um RoutedCommand, o destino do comando será ignorado.

A origem do comando pode definir explicitamente o destino do comando. Se o destino do comando não estiver definido, o elemento com foco do teclado será usado como o destino do comando. Um dos benefícios de usar o elemento com foco no teclado como o 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 ter que acompanhar o destino do comando. Por exemplo, se um MenuItem invoca o comando Colar em um aplicativo que tem um controle TextBox e um controle PasswordBox, o destino pode ser o TextBox ou PasswordBox dependendo de qual controle tem foco no 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 serve uma série de funções relacionadas ao comando. Ele fornece um conjunto de métodos estáticos para adicionar e remover manipuladores de eventos PreviewExecuted, Executed, PreviewCanExecutee CanExecute de e para um elemento específico. Ele fornece um meio de registrar CommandBinding e InputBinding objetos em uma classe específica. O CommandManager também fornece um método, através do evento RequerySuggested, para notificar um comando quando ele deve acionar o evento CanExecuteChanged.

O método InvalidateRequerySuggested força o CommandManager a desencadear o evento RequerySuggested. Isso é útil para condições que devem desabilitar/habilitar um comando, mas não são condições das quais o CommandManager está ciente.

Biblioteca de Comandos

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

Muitos desses comandos incluem um conjunto de ligações de entrada padrão. Por exemplo, se você especificar que seu aplicativo manipula o comando copy, 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 de comando estático. Geralmente, os nomes de comando são inequívocos como cadeias de caracteres, e os tipos proprietários existem para fornecer um agrupamento lógico de comandos, mas não são necessários para a desambiguação. Por exemplo, pode especificar Command="Cut" em vez de Command="ApplicationCommands.Cut"mais verboso. Este é um mecanismo de conveniência que é incorporado ao processador WPF XAML para comandos (mais precisamente, é um comportamento de conversor de tipo de ICommand, que o processador WPF XAML faz referência no momento do carregamento).

Criando comandos personalizados

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

Para obter um exemplo de criação de um RoutedCommandpersonalizado, consulte Criar um exemplo de RoutedCommand personalizado.

Ver também