Atajos de teclado en WPF

129

Sé usar en _lugar de &, pero estoy mirando todos los Ctrlaccesos directos de tipo +.

Ctrl+ Zpara deshacer, Ctrl+ Spara guardar, etc.

¿Existe una forma 'estándar' para implementar esto en aplicaciones WPF? ¿O es un caso de rodar los suyos y conectarlos a cualquier comando / control?

Benjol
fuente

Respuestas:

170

Una forma es agregar sus teclas de acceso directo a los comandos en sí mismos InputGestures. Los comandos se implementan comoRoutedCommands .

Esto permite que las teclas de acceso directo funcionen incluso si no están conectadas a ningún control. Y dado que los elementos del menú comprenden los gestos del teclado, mostrarán automáticamente su tecla de acceso directo en el texto de los elementos del menú, si conecta ese comando a su elemento del menú.

  1. Cree un atributo estático para contener un comando (preferiblemente como una propiedad en una clase estática que cree para los comandos, pero para un ejemplo simple, simplemente usando un atributo estático en window.cs):

     public static RoutedCommand MyCommand = new RoutedCommand();
  2. Agregue las teclas de acceso directo que deberían invocar el método:

     MyCommand.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control));
  3. Cree un enlace de comando que apunte a su método para ejecutar. Ponga esto en los enlaces de comando para el elemento de la interfaz de usuario bajo el cual debería funcionar (por ejemplo, la ventana) y el método:

     <Window.CommandBindings>
         <CommandBinding Command="{x:Static local:MyWindow.MyCommand}" Executed="MyCommandExecuted"/>
     </Window.CommandBindings>
    
     private void MyCommandExecuted(object sender, ExecutedRoutedEventArgs e) { ... }
Abby Fichtner
fuente
44
¿Cómo asocio el comando con un elemento del menú? Seguramente esa sería la información más importante para incluir en esta respuesta, pero falta.
Timwi
8
@Timwi: he utilizado el código anterior de esta manera para agregar un atajo de teclado a un evento existente: RoutedCommand cmndSettings = new RoutedCommand (); cmndSettings.InputGestures.Add (nuevo KeyGesture (Key.S, ModifierKeys.Control)); CommandBindings.Add (nuevo CommandBinding (cmndSettings, mnuSettings_Click));
itsho
1
El comentario de itsho hizo que esto funcionara para mí, no podía hacer que el código xml anterior funcionara.
gosr
1
Desafortunadamente, con este enfoque, el Executedcódigo para el comando terminará en el código subyacente (de la ventana o el control del usuario) en lugar del modelo de vista, en lugar de usar un comando habitual ( ICommandimplementación personalizada ).
O Mapper
98

Encontré que esto era exactamente lo que estaba buscando en relación con la vinculación de teclas en WPF:

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

Ver publicación de blog MVVM CommandReference y KeyBinding

oliwa
fuente
Muy bonito y fácil!
fusión
1
¿Le importaría explicar qué es "CreateCustomerCommand" y cómo se implementará?
Vinz
Esta sigue siendo una respuesta de solo enlace, ya que el fragmento de código copiado y pegado se describe con "El resultado será una excepción" en la publicación del blog vinculado. : P
Martin Schneider
Las obras se maravillan aquí. Primero intenté agregar "_" antes de la clave del contenido del botón, como el OP, pero no funcionó. Lo peor, se activó cuando presioné la tecla cuando no estaba enfocado en un objeto grabable de la interfaz ... como "s" para guardar, en lugar de ctrl-s.
Jay
14

Prueba este código ...

Primero cree un objeto RoutedComand

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

Depende de dónde quieras usarlos.

TextBoxBasecontroles controlados ya implementan esos atajos. Si desea utilizar métodos abreviados de teclado personalizados, debe echar un vistazo a los comandos y los gestos de entrada. Aquí hay un pequeño tutorial de Switch on the Code : Tutorial de WPF : enlaces de comandos y comandos personalizados

Anvaka
fuente
8
Qué tutorial de mierda: no explica lo más importante de todo, que es cómo usar un comando que no es uno de su conjunto predefinido de 20 comandos "comunes".
Timwi
6

Documentar esta respuesta para otros, ya que hay una manera mucho más simple de hacer esto que rara vez se menciona y que no requiere tocar el XAML.

Para vincular un atajo de teclado, en el constructor de Windows simplemente agregue un nuevo KeyBinding a la colección InputBindings. Como comando, pase su clase de comando arbitrario que implementa ICommand. Para el método de ejecución, simplemente implemente la lógica que necesite. En mi ejemplo a continuación, mi clase WindowCommand toma un delegado que se ejecutará cada vez que se invoque. Cuando construyo el nuevo WindowCommand para pasar con mi enlace, simplemente indico en mi inicializador, el método que quiero que ejecute el WindowCommand.

Puede usar este patrón para crear sus propios atajos rápidos de teclado.

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)));
   ...
}

Cree una clase WindowCommand simple que tome un delegado de ejecución para disparar cualquier método establecido en ella.

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();
        }
    }
}
Ayo I
fuente
5

Tuve un problema similar y encontré que la respuesta de @ aliwa era la solución más útil y más elegante; sin embargo, necesitaba una combinación de teclas específica, Ctrl+ 1. Lamentablemente recibí el siguiente error:

'1' no se puede usar como valor para 'Clave'. Los números no son valores de enumeración válidos.

Con un poco de búsqueda adicional, modifiqué la respuesta de @ aliwa a lo siguiente:

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

Encontré que esto funciona muy bien para cualquier combinación que necesitaba.

Nik
fuente
Esto funcionó para mí<UserControl.InputBindings> <KeyBinding Gesture="Enter" Command="{Binding someCommand}"/> </UserControl.InputBindings>
fs_tigre
3

VB.NET:

Public Shared SaveCommand_AltS As New RoutedCommand

Dentro del evento cargado :

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

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

No se necesita XAML.

plaasmeisie
fuente
1

Aunque las respuestas principales son correctas, personalmente me gusta trabajar con propiedades adjuntas para permitir que la solución se aplique a cualquiera UIElement, especialmente cuando Windowno conoce el elemento que debe enfocarse. En mi experiencia, a menudo veo una composición de varios modelos de vista y controles de usuario, donde la ventana a menudo no es más que el contenedor raíz.

Retazo

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));
        }
    }
}

Con esta propiedad adjunta puede definir un acceso directo de foco para cualquier UIElement. Registrará automáticamente el enlace de entrada en la ventana que contiene el elemento.

Uso (XAML)

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

Código fuente

La muestra completa, incluida la implementación de FocusElementCommand, está disponible como gist: https://gist.github.com/shuebner20/c6a5191be23da549d5004ee56bcc352d

Descargo de responsabilidad: puede usar este código en todas partes y de forma gratuita. Tenga en cuenta que esta es una muestra que no es adecuada para un uso intensivo. Por ejemplo, no hay recolección de basura de elementos eliminados porque el comando tendrá una fuerte referencia al elemento.

Sebastian Hübner
fuente
-2

Cómo asociar el comando con un MenuItem:

<MenuItem Header="My command" Command="{x:Static local:MyWindow.MyCommand}"/>
Jiří Skála
fuente