Scorciatoie da tastiera in WPF


129

So di usare _invece di &, ma sto guardando tutte le Ctrlscorciatoie di tipo +.

Ctrl+ Zper annullare, Ctrl+ Sper salvare, ecc.

Esiste un modo "standard" per implementarli in applicazioni WPF? O è un caso di rollare il tuo e collegarlo a qualunque comando / controllo?

Risposte:


170

Un modo è quello di aggiungere i tasti di scelta rapida ai comandi stessi come InputGestures. I comandi sono implementati come RoutedCommands.

Ciò consente ai tasti di scelta rapida di funzionare anche se non sono collegati a nessun controllo. E poiché le voci di menu comprendono i gesti della tastiera, visualizzeranno automaticamente il tasto di scelta rapida nel testo delle voci di menu, se si aggancia quel comando alla voce di menu.

  1. Crea un attributo statico per contenere un comando (preferibilmente come una proprietà in una classe statica che crei per i comandi, ma per un semplice esempio, usa un attributo statico in window.cs):

     public static RoutedCommand MyCommand = new RoutedCommand();
  2. Aggiungi i tasti di scelta rapida che dovrebbero invocare il metodo:

     MyCommand.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control));
  3. Crea un binding di comando che punti al tuo metodo da chiamare in esecuzione. Inseriscili nei collegamenti dei comandi per l'elemento UI in base al quale dovrebbe funzionare (ad esempio, la finestra) e il metodo:

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

4
Come associo il comando a una voce di menu? Sicuramente sarebbero le informazioni più importanti da includere in questa risposta, ma mancano.
Timwi,

8
@Timwi: ho usato il codice sopra in questo modo per aggiungere scorciatoie da tastiera a un evento esistente: RoutedCommand cmndSettings = new RoutedCommand (); cmndSettings.InputGestures.Add (new KeyGesture (Key.S, ModifierKeys.Control)); CommandBindings.Add (nuovo CommandBinding (cmndSettings, mnuSettings_Click));
itsho

1
Il commento di itsho ha reso questo lavoro per me, impossibile far funzionare il codice XML sopra.
gosr

1
Sfortunatamente, con questo approccio, il Executedcodice per il comando finirà nel code-behind (della finestra o del controllo utente) anziché nel modello di visualizzazione, anziché utilizzare un normale comando ( ICommandimplementazione personalizzata ).
OR Mapper,


98

Ho trovato che questo era esattamente quello che stavo cercando in relazione all'associazione dei tasti in WPF:

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

Vedi post sul blog MVVM CommandReference e KeyBinding


Molto bello e facile!
fusione

1
Ti dispiacerebbe approfondire cosa è "CreateCustomerCommand" e come deve essere implementato?
Vinz,

Questa è ancora una risposta solo al collegamento poiché lo snippet di codice copiato e incollato è descritto con "Il risultato sarà un'eccezione" sul post di blog collegato. : P
Martin Schneider,

Funziona a meraviglia qui. Ho prima provato ad aggiungere "_" prima della chiave del contenuto del pulsante, come l'OP, ma non ha funzionato. Peggio ancora, si attivava quando premevo il tasto stesso quando non ero a fuoco in un oggetto scrivibile dell'interfaccia .. come "s" per salvare, invece di ctrl-s.
Jay

14

Prova questo codice ...

Innanzitutto creare un oggetto RoutedComand

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

9

Dipende da dove vuoi usarli.

TextBoxBasecontrolli derivati ​​implementano già queste scorciatoie. Se si desidera utilizzare scorciatoie da tastiera personalizzate, è necessario dare un'occhiata ai comandi e ai gesti di input. Ecco un piccolo tutorial da Switch on the Code : WPF Tutorial - Command Bindings and Custom Commands


8
Che tutorial di merda - non spiega la cosa assolutamente più importante di tutte, che è come usare un comando che non è uno dei loro set predefiniti di 20 comandi "comuni".
Timwi,

6

Documentare questa risposta per gli altri, in quanto esiste un modo molto più semplice di fare ciò a cui raramente si fa riferimento e che non richiede affatto di toccare XAML.

Per collegare una scorciatoia da tastiera, nel costruttore di Window è sufficiente aggiungere un nuovo KeyBinding alla raccolta InputBindings. Come comando, passa la tua classe di comando arbitraria che implementa ICommand. Per il metodo execute, implementa semplicemente la logica di cui hai bisogno. Nel mio esempio di seguito, la mia classe WindowCommand accetta un delegato che eseguirà ogni volta che viene invocato. Quando costruisco il nuovo WindowCommand per passare con la mia associazione, indico semplicemente nel mio inizializzatore il metodo che voglio che WindowCommand esegua.

Puoi usare questo schema per trovare le tue scorciatoie da tastiera rapide.

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

Crea una semplice classe WindowCommand che richiede a un delegato dell'esecuzione di attivare qualsiasi metodo impostato su di essa.

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

Ho avuto un problema simile e ho trovato la risposta di @ aliwa come la soluzione più utile ed elegante; tuttavia, avevo bisogno di una combinazione di tasti specifica, Ctrl+ 1. Purtroppo ho ricevuto il seguente errore:

'1' non può essere utilizzato come valore per 'Chiave'. I numeri non sono valori di enumerazione validi.

Con un po 'di ulteriore ricerca, ho modificato la risposta di @ aliwa a quanto segue:

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

Ho trovato che funzionava benissimo per qualsiasi combinazione di cui avevo bisogno.


Questo ha funzionato per me<UserControl.InputBindings> <KeyBinding Gesture="Enter" Command="{Binding someCommand}"/> </UserControl.InputBindings>
fs_tigre,

3

VB.NET:

Public Shared SaveCommand_AltS As New RoutedCommand

All'interno dell'evento caricato :

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

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

Non è necessario XAML.


1

Sebbene le risposte migliori siano corrette, personalmente mi piace lavorare con le proprietà associate per consentire l'applicazione della soluzione a qualsiasi UIElement, specialmente quando Windownon è a conoscenza dell'elemento che dovrebbe essere focalizzato. Nella mia esperienza vedo spesso una composizione di diversi modelli di vista e controlli utente, in cui la finestra spesso non è altro che il contenitore principale.

Frammento

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 questa proprietà collegata è possibile definire un collegamento di attivazione per qualsiasi UIElement. Registra automaticamente l'associazione di input nella finestra contenente l'elemento.

Utilizzo (XAML)

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

Codice sorgente

L'esempio completo che comprende l'implementazione FocusElementCommand è disponibile come gist: https://gist.github.com/shuebner20/c6a5191be23da549d5004ee56bcc352d

Dichiarazione di non responsabilità: è possibile utilizzare questo codice ovunque e gratuitamente. Si prega di tenere presente che questo è un campione che non è adatto per un uso intenso. Ad esempio, non esiste una garbage collection di elementi rimossi perché il comando conterrà un forte riferimento all'elemento.


-2

Come associare il comando a MenuItem:

<MenuItem Header="My command" Command="{x:Static local:MyWindow.MyCommand}"/>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.