Raccourcis clavier dans WPF

129

Je sais utiliser _au lieu de &, mais je regarde tous les Ctrlraccourcis de type +.

Ctrl+ Zpour annuler, Ctrl+ Spour enregistrer, etc.

Existe-t-il un moyen «standard» de les implémenter dans les applications WPF? Ou est-ce un cas de rouler les vôtres et de les câbler à n'importe quel commandement / contrôle?

Benjol
la source

Réponses:

170

Une façon consiste à ajouter vos touches de raccourci aux commandes elles-mêmes sous forme de InputGestures. Les commandes sont implémentées comme RoutedCommands.

Cela permet aux touches de raccourci de fonctionner même si elles ne sont connectées à aucun contrôle. Et comme les éléments de menu comprennent les gestes du clavier, ils afficheront automatiquement votre touche de raccourci dans le texte des éléments de menu, si vous accrochez cette commande à votre élément de menu.

  1. Créez un attribut statique pour contenir une commande (de préférence en tant que propriété dans une classe statique que vous créez pour les commandes - mais pour un exemple simple, en utilisant simplement un attribut statique dans window.cs):

     public static RoutedCommand MyCommand = new RoutedCommand();
  2. Ajoutez la ou les touches de raccourci qui doivent appeler la méthode:

     MyCommand.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control));
  3. Créez une liaison de commande qui pointe vers votre méthode à appeler lors de l'exécution. Mettez-les dans les liaisons de commande pour l'élément d'interface utilisateur sous lequel il devrait fonctionner (par exemple, la fenêtre) et la méthode:

     <Window.CommandBindings>
         <CommandBinding Command="{x:Static local:MyWindow.MyCommand}" Executed="MyCommandExecuted"/>
     </Window.CommandBindings>
    
     private void MyCommandExecuted(object sender, ExecutedRoutedEventArgs e) { ... }
Abby Fichtner
la source
4
Comment associer la commande à un élément de menu? Ce serait sûrement l'information la plus importante à inclure dans cette réponse, mais elle manque.
Timwi
8
@Timwi: j'ai utilisé le code ci-dessus de cette manière pour ajouter un raccourci clavier à un événement existant: RoutedCommand cmndSettings = new RoutedCommand (); cmndSettings.InputGestures.Add (nouveau KeyGesture (Key.S, ModifierKeys.Control)); CommandBindings.Add (nouveau CommandBinding (cmndSettings, mnuSettings_Click));
itsho
1
Le commentaire d'itsho a fait ce travail pour moi, n'a pas pu faire fonctionner le code xml ci-dessus.
gosr
1
Malheureusement, avec cette approche, le Executedcode de la commande se retrouvera dans le code-behind (de la fenêtre ou du contrôle utilisateur) plutôt que dans le modèle de vue, par opposition à l'utilisation d'une commande habituelle ( ICommandimplémentation personnalisée ).
OR Mapper
2
Exemple similaire wpf-tutorial.com/commands/implementing-custom-commands
Narottam Goyal
98

J'ai trouvé que c'était exactement ce que je recherchais en ce qui concerne la liaison de clé dans WPF:

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

Voir l'article de blog MVVM CommandReference and KeyBinding

Oliwa
la source
Très agréable et facile!
fusion
1
Pourriez-vous préciser ce qu'est "CreateCustomerCommand" et comment il doit être implémenté?
Vinz
Il s'agit toujours d'une réponse de lien uniquement puisque l'extrait de code copier-coller est décrit par "Le résultat sera une exception" sur le billet de blog lié. : P
Martin Schneider
Les œuvres à merveille ici. J'ai d'abord essayé d'ajouter "_" avant la clé du contenu du bouton, comme l'OP, mais cela n'a pas fonctionné. Pire encore, il s'activait lorsque j'appuyais sur la touche elle-même alors que je n'étais pas sur le focus dans un objet inscriptible de l'interface .. comme "s" pour enregistrer, au lieu de ctrl-s.
Jay le
14

Essayez ce code ...

Créez d'abord un objet RoutedComand

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

Cela dépend de l'endroit où vous souhaitez les utiliser.

TextBoxBaseLes contrôles dérivés implémentent déjà ces raccourcis. Si vous souhaitez utiliser des raccourcis clavier personnalisés, vous devez jeter un œil aux commandes et aux gestes de saisie. Voici un petit tutoriel de Switch on the Code : Tutoriel WPF - Liaisons de commandes et commandes personnalisées

Anvaka
la source
8
Quel tutoriel de merde - n'explique pas la chose absolument la plus importante de toutes, qui est de savoir comment utiliser une commande qui ne se trouve pas être l'une de leur ensemble prédéfini de 20 commandes «communes».
Timwi le
6

Documenter cette réponse pour les autres, car il existe un moyen beaucoup plus simple de le faire qui est rarement référencé et qui ne nécessite pas du tout de toucher au XAML.

Pour lier un raccourci clavier, dans le constructeur Window, ajoutez simplement un nouveau KeyBinding à la collection InputBindings. En tant que commande, passez votre classe de commande arbitraire qui implémente ICommand. Pour la méthode d'exécution, implémentez simplement la logique dont vous avez besoin. Dans mon exemple ci-dessous, ma classe WindowCommand prend un délégué qu'elle exécutera chaque fois qu'elle sera appelée. Lorsque je construis le nouveau WindowCommand à transmettre avec ma liaison, j'indique simplement dans mon initialiseur, la méthode que je veux que WindowCommand exécute.

Vous pouvez utiliser ce modèle pour créer vos propres raccourcis clavier rapides.

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

Créez une classe WindowCommand simple qui prend un délégué d'exécution pour déclencher toute méthode définie dessus.

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 je
la source
5

J'ai eu un problème similaire et j'ai trouvé que la réponse de @ aliwa était la solution la plus utile et la plus élégante; cependant, j'avais besoin d'une combinaison de touches spécifique, Ctrl+ 1. Malheureusement, j'ai eu l'erreur suivante:

'1' ne peut pas être utilisé comme valeur pour 'Key'. Les nombres ne sont pas des valeurs d'énumération valides.

Avec un peu plus de recherche, j'ai modifié la réponse de @ aliwa à ce qui suit:

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

J'ai trouvé que cela fonctionnait très bien pour toutes les combinaisons dont j'avais besoin.

Nik
la source
Cela a fonctionné pour moi<UserControl.InputBindings> <KeyBinding Gesture="Enter" Command="{Binding someCommand}"/> </UserControl.InputBindings>
fs_tigre
3

VB.NET:

Public Shared SaveCommand_AltS As New RoutedCommand

À l'intérieur de l' événement chargé :

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

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

Aucun XAML n'est nécessaire.

plaasmeisie
la source
1

Bien que les principales réponses soient correctes, j'aime personnellement travailler avec les propriétés attachées pour permettre à la solution d'être appliquée à tout UIElement , en particulier lorsque le Windown'est pas conscient de l'élément qui doit être ciblé. D'après mon expérience, je vois souvent une composition de plusieurs modèles de vue et de contrôles utilisateur, où la fenêtre n'est souvent rien de plus que le conteneur racine.

Fragment

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

Avec cette propriété jointe, vous pouvez définir un raccourci de focus pour n'importe quel UIElement. Il enregistrera automatiquement la liaison d'entrée dans la fenêtre contenant l'élément.

Utilisation (XAML)

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

Code source

L'exemple complet, y compris l'implémentation de FocusElementCommand, est disponible sous forme de fichier essentiel: https://gist.github.com/shuebner20/c6a5191be23da549d5004ee56bcc352d

Avertissement: Vous pouvez utiliser ce code partout et gratuitement. Veuillez garder à l'esprit qu'il s'agit d'un échantillon qui ne convient pas à un usage intensif. Par exemple, il n'y a pas de garbage collection des éléments supprimés car la commande contiendra une référence forte à l'élément.

Sebastian Hübner
la source
-2

Comment associer la commande à un MenuItem:

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