Atalhos de teclado no WPF


129

Eu sei sobre o uso em _vez de &, mas estou olhando para todos os Ctrlatalhos do tipo +.

Ctrl+ Zpara desfazer, Ctrl+ Spara salvar etc.

Existe uma maneira 'padrão' para implementá-los em aplicativos WPF? Ou é o caso de criar o seu próprio e conectá-los a qualquer comando / controle?

Respostas:


170

Uma maneira é adicionar as teclas de atalho aos próprios comandos, como InputGestures. Comandos são implementados como RoutedCommands.

Isso permite que as teclas de atalho funcionem mesmo se não estiverem conectadas a nenhum controle. E como os itens de menu entendem os gestos do teclado, eles exibirão automaticamente sua tecla de atalho no texto dos itens de menu, se você conectar esse comando ao item de menu.

  1. Crie um atributo estático para armazenar um comando (de preferência como uma propriedade em uma classe estática criada para comandos - mas, para um exemplo simples, basta usar um atributo estático em window.cs):

     public static RoutedCommand MyCommand = new RoutedCommand();
  2. Adicione as teclas de atalho que devem chamar o método:

     MyCommand.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control));
  3. Crie uma ligação de comando que aponte para o seu método para chamar na execução. Coloque-os nas ligações de comando do elemento da interface do usuário sob o qual ele deve trabalhar (por exemplo, a janela) e o método:

     <Window.CommandBindings>
         <CommandBinding Command="{x:Static local:MyWindow.MyCommand}" Executed="MyCommandExecuted"/>
     </Window.CommandBindings>
    
     private void MyCommandExecuted(object sender, ExecutedRoutedEventArgs e) { ... }

4
Como associo o comando a um item de menu? Certamente essa seria a informação mais importante a ser incluída nesta resposta, mas está faltando.
Timwi

8
@ Timwi: usei o código acima para adicionar atalhos de teclado a um evento existente: RoutedCommand cmndSettings = new RoutedCommand (); cmndSettings.InputGestures.Add (new KeyGesture (Key.S, ModifierKeys.Control)); CommandBindings.Add (novo CommandBinding (cmndSettings, mnuSettings_Click));
itsho 11/01

1
O comentário de itsho fez esse trabalho para mim, não conseguiu fazer o código xml acima funcionar.
gosr

1
Infelizmente, com essa abordagem, o Executedcódigo do comando terminará no code-behind (da janela ou no controle do usuário), e não no modelo de visualização, em vez de usar um comando usual ( ICommandimplementação personalizada ).
OR Mapper


97

Eu achei que isso era exatamente o que eu estava procurando relacionado à ligação de chaves no WPF:

<Window.InputBindings>
        <KeyBinding Modifiers="Control"
                    Key="N"
                    Command="{Binding CreateCustomerCommand}" />
</Window.InputBindings>

Ver postagem no blog MVVM CommandReference and KeyBinding


Muito agradável e fácil!
fusão

1
Você se importaria em elaborar o que é "CreateCustomerCommand" e como deve ser implementado?
Vinz

Essa ainda é uma resposta apenas para o link, pois o snippet de cópia e código colado é descrito com "O resultado será uma exceção" na postagem do blog vinculado. : P
Martin Schneider

Obras maravilha aqui. Tentei adicionar "_" antes da chave do conteúdo do botão, como o OP, mas não funcionou. Pior, ele foi ativado quando eu pressionei a tecla quando não estava focado em um objeto gravável da interface. Como "s" para salvar, em vez de ctrl-s.
Jay

14

Experimente este código ...

Primeiro, crie um objeto RoutedComand

  RoutedCommand newCmd = new RoutedCommand();
  newCmd.InputGestures.Add(new KeyGesture(Key.N, ModifierKeys.Control));
  CommandBindings.Add(new CommandBinding(newCmd, btnNew_Click));

9

Depende de onde você deseja usá-los.

TextBoxBasecontroles derivados já implementam esses atalhos. Se você quiser usar atalhos de teclado personalizados, consulte os gestos de comandos e entrada. Aqui está um pequeno tutorial do Switch on the Code : Tutorial do WPF - Ligações de comandos e comandos personalizados


8
Que tutorial horrível - não explica a coisa absolutamente mais importante de todas, que é como usar um comando que não é um dos conjuntos predefinidos de 20 comandos "comuns".
Timwi

6

Documentar essa resposta para outras pessoas, pois existe uma maneira muito mais simples de fazer isso, que raramente é referenciada e que não exige o toque no XAML.

Para vincular um atalho de teclado, no construtor Window, adicione um novo KeyBinding à coleção InputBindings. Como o comando, passe sua classe de comando arbitrária que implementa o ICommand. Para o método execute, simplesmente implemente a lógica que você precisa. No meu exemplo abaixo, minha classe WindowCommand leva um delegado que será executado sempre que chamado. Quando eu construo o novo WindowCommand para passar com minha ligação, eu simplesmente indico no meu inicializador, o método que eu quero que o WindowCommand execute.

Você pode usar esse padrão para criar seus próprios atalhos de teclado rápidos.

public YourWindow() //inside any WPF Window constructor
{
   ...
   //add this one statement to bind a new keyboard command shortcut
   InputBindings.Add(new KeyBinding( //add a new key-binding, and pass in your command object instance which contains the Execute method which WPF will execute
      new WindowCommand(this)
      {
         ExecuteDelegate = TogglePause //REPLACE TogglePause with your method delegate
      }, new KeyGesture(Key.P, ModifierKeys.Control)));
   ...
}

Crie uma classe WindowCommand simples, que leva um delegado de execução para disparar qualquer método definido nela.

public class WindowCommand : ICommand
{
    private MainWindow _window;

    //Set this delegate when you initialize a new object. This is the method the command will execute. You can also change this delegate type if you need to.
    public Action ExecuteDelegate { get; set; }

    //You don't have to add a parameter that takes a constructor. I've just added one in case I need access to the window directly.
    public WindowCommand(MainWindow window)
    {
        _window = window;
    }

    //always called before executing the command, mine just always returns true
    public bool CanExecute(object parameter)
    {
        return true; //mine always returns true, yours can use a new CanExecute delegate, or add custom logic to this method instead.
    }

    public event EventHandler CanExecuteChanged; //i'm not using this, but it's required by the interface

    //the important method that executes the actual command logic
    public void Execute(object parameter)
    {
        if (ExecuteDelegate != null)
        {
            ExecuteDelegate();
        }
        else
        {
            throw new InvalidOperationException();
        }
    }
}

5

Eu tive um problema semelhante e achei a resposta da @ aliwa a solução mais útil e elegante; no entanto, eu precisava de uma combinação de teclas específica, Ctrl+ 1. Infelizmente, recebi o seguinte erro:

'1' não pode ser usado como um valor para 'Chave'. Os números não são valores de enumeração válidos.

Com um pouco mais de pesquisa, modifiquei a resposta da @ aliwa para o seguinte:

<Window.InputBindings>
    <KeyBinding Gesture="Ctrl+1" Command="{Binding MyCommand}"/>
</Window.InputBindings>

Achei que isso funcionou muito bem para qualquer combinação que eu precisasse.


Isso funcionou para mim<UserControl.InputBindings> <KeyBinding Gesture="Enter" Command="{Binding someCommand}"/> </UserControl.InputBindings>
fs_tigre

3

VB.NET:

Public Shared SaveCommand_AltS As New RoutedCommand

Dentro do evento carregado :

SaveCommand_AltS.InputGestures.Add(New KeyGesture(Key.S, ModifierKeys.Control))

Me.CommandBindings.Add(New CommandBinding(SaveCommand_AltS, AddressOf Me.save))

Não é necessário XAML.


1

Embora as respostas principais estejam corretas, eu pessoalmente gosto de trabalhar com propriedades anexadas para permitir que a solução seja aplicada a qualquer uma UIElement, especialmente quando ela Windownão está ciente do elemento que deve ser focado. Na minha experiência, muitas vezes vejo uma composição de vários modelos de visualização e controles de usuário, em que a janela geralmente nada mais é que o contêiner raiz.

Snippet

public sealed class AttachedProperties
{
    // Define the key gesture type converter
    [System.ComponentModel.TypeConverter(typeof(System.Windows.Input.KeyGestureConverter))]
    public static KeyGesture GetFocusShortcut(DependencyObject dependencyObject)
    {
        return (KeyGesture)dependencyObject?.GetValue(FocusShortcutProperty);
    }

    public static void SetFocusShortcut(DependencyObject dependencyObject, KeyGesture value)
    {
        dependencyObject?.SetValue(FocusShortcutProperty, value);
    }

    /// <summary>
    /// Enables window-wide focus shortcut for an <see cref="UIElement"/>.
    /// </summary>
    // Using a DependencyProperty as the backing store for FocusShortcut.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty FocusShortcutProperty =
        DependencyProperty.RegisterAttached("FocusShortcut", typeof(KeyGesture), typeof(AttachedProperties), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnFocusShortcutChanged)));

    private static void OnFocusShortcutChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (!(d is UIElement element) || e.NewValue == e.OldValue)
            return;

        var window = FindParentWindow(d);
        if (window == null)
            return;

        var gesture = GetFocusShortcut(d);
        if (gesture == null)
        {
            // Remove previous added input binding.
            for (int i = 0; i < window.InputBindings.Count; i++)
            {
                if (window.InputBindings[i].Gesture == e.OldValue && window.InputBindings[i].Command is FocusElementCommand)
                    window.InputBindings.RemoveAt(i--);
            }
        }
        else
        {
            // Add new input binding with the dedicated FocusElementCommand.
            // see: https://gist.github.com/shuebner20/349d044ed5236a7f2568cb17f3ed713d
            var command = new FocusElementCommand(element);
            window.InputBindings.Add(new InputBinding(command, gesture));
        }
    }
}

Com esta propriedade anexada, você pode definir um atalho de foco para qualquer UIElement. Ele registrará automaticamente a ligação de entrada na janela que contém o elemento.

Uso (XAML)

<TextBox x:Name="SearchTextBox"
         Text={Binding Path=SearchText}
         local:AttachedProperties.FocusShortcutKey="Ctrl+Q"/>

Código fonte

A amostra completa, incluindo a implementação do FocusElementCommand, está disponível como gist: https://gist.github.com/shuebner20/c6a5191be23da549d5004ee56bcc352d

Isenção de responsabilidade: Você pode usar este código em qualquer lugar e gratuitamente. Lembre-se de que esta é uma amostra que não é adequada para uso pesado. Por exemplo, não há coleta de lixo de elementos removidos porque o Comando manterá uma forte referência ao elemento.


-2

Como associar o comando a MenuItem:

<MenuItem Header="My command" Command="{x:Static local:MyWindow.MyCommand}"/>
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.