Risposte:
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.
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();
Aggiungi i tasti di scelta rapida che dovrebbero invocare il metodo:
MyCommand.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control));
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) { ... }
Executed
codice per il comando finirà nel code-behind (della finestra o del controllo utente) anziché nel modello di visualizzazione, anziché utilizzare un normale comando ( ICommand
implementazione personalizzata ).
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
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));
Dipende da dove vuoi usarli.
TextBoxBase
controlli 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
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();
}
}
}
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.
<UserControl.InputBindings> <KeyBinding Gesture="Enter" Command="{Binding someCommand}"/> </UserControl.InputBindings>
Public Shared SaveCommand_AltS As New RoutedCommand
SaveCommand_AltS.InputGestures.Add(New KeyGesture(Key.S, ModifierKeys.Control))
Me.CommandBindings.Add(New CommandBinding(SaveCommand_AltS, AddressOf Me.save))
Non è necessario XAML.
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 Window
non è 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.
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.
<TextBox x:Name="SearchTextBox"
Text={Binding Path=SearchText}
local:AttachedProperties.FocusShortcutKey="Ctrl+Q"/>
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.
Come associare il comando a MenuItem
:
<MenuItem Header="My command" Command="{x:Static local:MyWindow.MyCommand}"/>