Tastaturkürzel in WPF

129

Ich weiß, wie man _statt verwendet &, aber ich sehe mir alle CtrlVerknüpfungen vom Typ + an.

Ctrl+ Zzum Rückgängigmachen, Ctrl+ Szum Speichern usw.

Gibt es eine Standardmethode, um diese in WPF-Anwendungen zu implementieren? Oder ist es ein Fall, dass Sie Ihre eigenen rollen und sie mit jedem Befehl / jeder Kontrolle verbinden?

Benjol
quelle

Antworten:

170

Eine Möglichkeit besteht darin, die Tastenkombinationen zu den Befehlen selbst hinzuzufügen InputGestures. Befehle werden implementiert als RoutedCommands.

Auf diese Weise können die Tastenkombinationen auch dann funktionieren, wenn sie nicht mit Steuerelementen verbunden sind. Und da Menüelemente Tastaturgesten verstehen, wird Ihre Tastenkombination automatisch im Text der Menüelemente angezeigt, wenn Sie diesen Befehl mit Ihrem Menüelement verknüpfen.

  1. Erstellen Sie ein statisches Attribut, um einen Befehl zu speichern (vorzugsweise als Eigenschaft in einer statischen Klasse, die Sie für Befehle erstellen - aber für ein einfaches Beispiel verwenden Sie einfach ein statisches Attribut in window.cs):

     public static RoutedCommand MyCommand = new RoutedCommand();
  2. Fügen Sie die Tastenkombination (en) hinzu, die die Methode aufrufen sollen:

     MyCommand.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control));
  3. Erstellen Sie eine Befehlsbindung, die auf Ihre Methode verweist, die beim Ausführen aufgerufen werden soll. Fügen Sie diese in die Befehlsbindungen für das UI-Element ein, unter dem es funktionieren soll (z. B. das Fenster) und die Methode:

     <Window.CommandBindings>
         <CommandBinding Command="{x:Static local:MyWindow.MyCommand}" Executed="MyCommandExecuted"/>
     </Window.CommandBindings>
    
     private void MyCommandExecuted(object sender, ExecutedRoutedEventArgs e) { ... }
Abby Fichtner
quelle
4
Wie ordne ich den Befehl einem Menüpunkt zu? Das wäre sicherlich die wichtigste Information, die in diese Antwort aufgenommen werden muss, aber sie fehlt.
Timwi
8
@ Timwi: Ich habe den obigen Code auf diese Weise verwendet, um einem vorhandenen Ereignis eine Tastenkombination hinzuzufügen: RoutedCommand cmndSettings = new RoutedCommand (); cmndSettings.InputGestures.Add (neue KeyGesture (Key.S, ModifierKeys.Control)); CommandBindings.Add (neues CommandBinding (cmndSettings, mnuSettings_Click));
itsho
1
Der Kommentar von itsho hat diese Arbeit für mich gemacht, konnte den obigen XML-Code nicht funktionieren lassen.
Gosr
1
Leider landet bei diesem Ansatz der ExecutedCode für den Befehl im Code-Behind (des Fensters oder der Benutzersteuerung) und nicht im Ansichtsmodell, im Gegensatz zur Verwendung eines üblichen Befehls (benutzerdefinierte ICommandImplementierung).
ODER Mapper
2
Ähnliches Beispiel wpf-tutorial.com/commands/implementing-custom-commands
Narottam Goyal
97

Ich fand, dass dies genau das ist, wonach ich im Zusammenhang mit der Schlüsselbindung in WPF gesucht habe:

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

Siehe Blogbeitrag MVVM CommandReference und KeyBinding

oliwa
quelle
Sehr schön und einfach!
Fusion
1
Würde es Ihnen etwas ausmachen, näher darauf einzugehen, was "CreateCustomerCommand" ist und wie es implementiert werden soll?
Vinz
Dies ist immer noch eine Antwort nur für Links, da das Snippet zum Kopieren und Einfügen von Codes im verknüpften Blog-Beitrag mit "Das Ergebnis ist eine Ausnahme" beschrieben wird. : P
Martin Schneider
Werke staunen hier. Ich habe zuerst versucht, "_" vor dem Schlüssel des Schaltflächeninhalts hinzuzufügen, wie beim OP, aber es hat nicht funktioniert. Am schlimmsten ist, dass es aktiviert wurde, wenn ich die Taste selbst drückte, als ich mich nicht auf ein beschreibbares Objekt der Benutzeroberfläche konzentrierte. Wie "s" zum Speichern anstelle von Strg-S.
Jay
14

Versuchen Sie diesen Code ...

Erstellen Sie zunächst ein RoutedComand-Objekt

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

Es hängt davon ab, wo Sie diese verwenden möchten.

TextBoxBase-derivierte Steuerelemente implementieren diese Verknüpfungen bereits. Wenn Sie benutzerdefinierte Tastaturkürzel verwenden möchten, sollten Sie sich Befehle und Eingabegesten ansehen. Hier ist ein kleines Tutorial von Code einschalten : WPF-Tutorial - Befehlsbindungen und benutzerdefinierte Befehle

Anvaka
quelle
8
Was für ein Mist-Tutorial - erklärt nicht das absolut Wichtigste von allem, nämlich wie man einen Befehl verwendet, der nicht zufällig einer der vordefinierten 20 "allgemeinen" Befehle ist.
Timwi
6

Dokumentieren Sie diese Antwort für andere, da es einen viel einfacheren Weg gibt, auf den selten verwiesen wird und für den das Berühren der XAML überhaupt nicht erforderlich ist.

Um eine Tastenkombination zu verknüpfen, fügen Sie im Fensterkonstruktor einfach eine neue Tastenkombination zur InputBindings-Auflistung hinzu. Übergeben Sie als Befehl Ihre beliebige Befehlsklasse, die ICommand implementiert. Implementieren Sie für die Methode execute einfach die gewünschte Logik. In meinem Beispiel unten nimmt meine WindowCommand-Klasse einen Delegaten, den sie bei jedem Aufruf ausführt. Wenn ich den neuen WindowCommand so konstruiere, dass er mit meiner Bindung übergeben wird, gebe ich in meinem Initialisierer einfach die Methode an, die der WindowCommand ausführen soll.

Mit diesem Muster können Sie Ihre eigenen schnellen Tastaturkürzel erstellen.

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

Erstellen Sie eine einfache WindowCommand-Klasse, die einen Ausführungsdelegierten benötigt, um eine darauf festgelegte Methode auszulösen.

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 ich
quelle
5

Ich hatte ein ähnliches Problem und fand die Antwort von @ aliwa die hilfreichste und eleganteste Lösung. Ich brauchte jedoch eine bestimmte Tastenkombination, Ctrl+ 1. Leider habe ich folgenden Fehler bekommen:

'1' kann nicht als Wert für 'Schlüssel' verwendet werden. Zahlen sind keine gültigen Aufzählungswerte.

Mit ein wenig weiterer Suche habe ich die Antwort von @ aliwa auf Folgendes geändert:

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

Ich fand, dass dies für jede Kombination, die ich brauchte, ziemlich gut funktioniert.

Nik
quelle
Das hat bei mir <UserControl.InputBindings> <KeyBinding Gesture="Enter" Command="{Binding someCommand}"/> </UserControl.InputBindings>
funktioniert
3

VB.NET:

Public Shared SaveCommand_AltS As New RoutedCommand

Innerhalb des geladenen Ereignisses:

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

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

Es wird keine XAML benötigt.

Plaasmeisie
quelle
1

Obwohl die Top-Antworten korrekt sind, arbeite ich persönlich gerne mit angehängten Eigenschaften, damit die Lösung auf alle angewendet werden kann UIElement, insbesondere wenn der Windownicht weiß, auf welches Element fokussiert werden soll. Nach meiner Erfahrung sehe ich oft eine Komposition aus mehreren Ansichtsmodellen und Benutzersteuerelementen, wobei das Fenster oft nichts anderes als der Stammcontainer ist.

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

Mit dieser angehängten Eigenschaft können Sie eine Fokusverknüpfung für jedes UIElement definieren. Die Eingabebindung wird automatisch in dem Fenster registriert, das das Element enthält.

Verwendung (XAML)

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

Quellcode

Das vollständige Beispiel einschließlich der Implementierung von FocusElementCommand ist als Inhalt verfügbar: https://gist.github.com/shuebner20/c6a5191be23da549d5004ee56bcc352d

Haftungsausschluss: Sie können diesen Code überall und kostenlos verwenden. Bitte beachten Sie, dass dies ein Muster ist, das nicht für starke Beanspruchung geeignet ist. Beispielsweise gibt es keine Speicherbereinigung für entfernte Elemente, da der Befehl einen starken Verweis auf das Element enthält.

Sebastian Hübner
quelle
-2

So ordnen Sie den Befehl einem zu MenuItem:

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