Skróty klawiaturowe w WPF

131

Wiem o używaniu _zamiast &, ale patrzę na wszystkie Ctrlskróty typu +.

Ctrl+ Zdo cofania, Ctrl+ Sdo zapisywania itp.

Czy istnieje „standardowy” sposób ich implementacji w aplikacjach WPF? A może jest to przypadek skręcenia własnego i połączenia go z jakimkolwiek dowództwem / kontrolą?

Benjol
źródło

Odpowiedzi:

173

Jednym ze sposobów jest dodanie klawiszy skrótów do samych poleceń jako InputGestures. Polecenia są implementowane jako RoutedCommands.

Dzięki temu klawisze skrótów działają, nawet jeśli nie są połączone z żadnymi kontrolkami. A ponieważ elementy menu rozumieją gesty klawiaturowe, automatycznie wyświetlą twój klawisz skrótu w tekście pozycji menu, jeśli podłączysz to polecenie do elementu menu.

  1. Utwórz atrybut statyczny do przechowywania polecenia (najlepiej jako właściwość w klasie statycznej tworzonej dla poleceń - ale dla prostego przykładu wystarczy użyć atrybutu statycznego w window.cs):

     public static RoutedCommand MyCommand = new RoutedCommand();
    
  2. Dodaj klawisze skrótu, które powinny wywoływać metodę:

     MyCommand.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control));
    
  3. Utwórz powiązanie polecenia, które wskazuje na metodę do wywołania podczas wykonywania. Umieść je w powiązaniach poleceń dla elementu UI, w którym ma działać (np. Okno) i metody:

     <Window.CommandBindings>
         <CommandBinding Command="{x:Static local:MyWindow.MyCommand}" Executed="MyCommandExecuted"/>
     </Window.CommandBindings>
    
     private void MyCommandExecuted(object sender, ExecutedRoutedEventArgs e) { ... }
    
Abby Fichtner
źródło
4
Jak skojarzyć polecenie z pozycją menu? Z pewnością byłaby to najważniejsza informacja, jaką należy zawrzeć w tej odpowiedzi, ale jej brakuje.
Timwi,
8
@Timwi: Użyłem powyższego kodu w ten sposób, aby dodać skrót klawiaturowy do istniejącego zdarzenia: RoutedCommand cmndSettings = new RoutedCommand (); cmndSettings.InputGestures.Add (new KeyGesture (Key.S, ModifierKeys.Control)); CommandBindings.Add (nowe CommandBinding (cmndSettings, mnuSettings_Click));
itsho
1
Komentarz itsho sprawił, że ten działał dla mnie, nie mógł sprawić, by powyższy kod XML działał.
gosr
1
Niestety, przy takim podejściu Executedkod polecenia skończy się w kodzie (okna lub kontroli użytkownika), a nie w modelu widoku, w przeciwieństwie do zwykłego polecenia ( ICommandimplementacja niestandardowa ).
LUB Mapper
2
Podobny przykład wpf-tutorial.com/commands/implementing-custom-commands
Narottam Goyal
99

Okazało się, że jest to dokładnie to, czego szukałem w związku z powiązaniem klucza w WPF:

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

Zobacz wpis w blogu MVVM CommandReference i KeyBinding

oliwa
źródło
Bardzo przyjemnie i łatwo!
połączenie
1
Czy mógłbyś wyjaśnić, czym jest „CreateCustomerCommand” i jak ma zostać wdrożone?
Vinz
To nadal jest odpowiedź zawierająca tylko łącze, ponieważ fragment kodu do kopiowania i wklejania jest opisany słowem „Wynikiem będzie wyjątek” w poście, do którego prowadzi link. : P
Martin Schneider
Działa tu cudownie. Najpierw próbowałem dodać „_” przed kluczem zawartości przycisku, tak jak OP, ale to nie zadziałało. Co najgorsze, aktywowało się, gdy wcisnąłem klawisz, gdy nie byłem skupiony na zapisywalnym obiekcie interfejsu… jak "s" do zapisu, zamiast ctrl-s.
Jay
W przypadku wielu modyfikatorów użyj +np Modifiers="Control+Alt". Zobacz stackoverflow.com/q/4050066
Pang
14

Spróbuj tego.

Najpierw utwórz obiekt RoutedCommand:

RoutedCommand newCmd = new RoutedCommand();
newCmd.InputGestures.Add(new KeyGesture(Key.N, ModifierKeys.Control));
CommandBindings.Add(new CommandBinding(newCmd, btnNew_Click));
Shahid Neermunda
źródło
9

To zależy od tego, gdzie chcesz ich użyć.

TextBoxBase- kontrolki pochodne już implementują te skróty. Jeśli chcesz używać niestandardowych skrótów klawiaturowych, zapoznaj się z poleceniami i gestami wprowadzania. Oto mały samouczek z Switch on the Code : Samouczek WPF - Powiązania poleceń i polecenia niestandardowe

Anvaka
źródło
8
Co za bzdury samouczek - nie wyjaśnia absolutnie najważniejszej rzeczy, jaką jest użycie polecenia, które nie jest jednym z predefiniowanych 20 „typowych” poleceń.
Timwi,
6

Udokumentowanie tej odpowiedzi dla innych, ponieważ istnieje znacznie prostszy sposób na zrobienie tego, który jest rzadko przywoływany i w ogóle nie wymaga dotykania XAML.

Aby połączyć skrót klawiaturowy, w konstruktorze Window po prostu dodaj nowy KeyBinding do kolekcji InputBindings. Jako polecenie przekaż dowolną klasę poleceń, która implementuje ICommand. W przypadku metody wykonywania po prostu zaimplementuj dowolną logikę, której potrzebujesz. W poniższym przykładzie moja klasa WindowCommand przyjmuje delegata, który będzie wykonywany po każdym wywołaniu. Kiedy konstruuję nowe WindowCommand do przekazania z moim powiązaniem, po prostu wskazuję w moim inicjatorze, metodę, którą chcę, aby WindowCommand wykonał.

Możesz użyć tego wzorca, aby wymyślić własne szybkie skróty klawiaturowe.

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

Utwórz prostą klasę WindowCommand, która pobiera delegata wykonania, aby odpalić dowolną ustawioną metodę.

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
źródło
5

Miałem podobny problem i uznałem, że odpowiedź @ aliwa jest najbardziej pomocnym i najbardziej eleganckim rozwiązaniem; potrzebowałem jednak określonej kombinacji klawiszy Ctrl+ 1. Niestety otrzymałem następujący błąd:

„1” nie może być użyte jako wartość dla „klucza”. Liczby nie są prawidłowymi wartościami wyliczenia.

Przy odrobinie dalszych poszukiwań zmodyfikowałem odpowiedź @ aliwa na następujące:

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

Okazało się, że to działa świetnie w przypadku dowolnej kombinacji, której potrzebowałem.

Nik
źródło
To zadziałało dla mnie<UserControl.InputBindings> <KeyBinding Gesture="Enter" Command="{Binding someCommand}"/> </UserControl.InputBindings>
fs_tigre
3

VB.NET:

Public Shared SaveCommand_AltS As New RoutedCommand

Wewnątrz załadowanego zdarzenia:

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

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

Nie jest potrzebny kod XAML.

plaasmeisie
źródło
1

Chociaż najlepsze odpowiedzi są poprawne, osobiście lubię pracować z dołączonymi właściwościami, aby umożliwić zastosowanie rozwiązania do dowolnego UIElement, zwłaszcza gdy Windownie jest świadomy elementu, na którym należy się skupić. Z mojego doświadczenia wynika, że ​​często widzę kompozycję kilku modeli widoku i kontrolek użytkownika, w których okno jest często niczym więcej niż kontenerem głównym.

Skrawek

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

Dzięki tej dołączonej właściwości możesz zdefiniować skrót fokusu dla dowolnego elementu UIElement. Automatycznie zarejestruje powiązanie wejściowe w oknie zawierającym element.

Użycie (XAML)

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

Kod źródłowy

Pełny przykład, w tym implementacja FocusElementCommand, jest dostępny jako gist: https://gist.github.com/shuebner20/c6a5191be23da549d5004ee56bcc352d

Zastrzeżenie: Możesz używać tego kodu wszędzie i bezpłatnie. Należy pamiętać, że jest to próbka, która nie nadaje się do intensywnego użytkowania. Na przykład nie ma wyrzucania elementów bezużytecznych, ponieważ Polecenie będzie przechowywać silne odwołanie do elementu.

Sebastian Hübner
źródło
0

Specjalny przypadek: Twój skrót nie jest uruchamiany, jeśli fokus znajduje się na elemencie, który „nie jest natywny”. Na przykład w moim przypadku skupienie się na WpfCurrencyTextbox nie spowoduje wywołania skrótów zdefiniowanych w Twoim XAML (zdefiniowanym jak w odpowiedzi oliwy).

Rozwiązałem ten problem, ustawiając mój skrót jako globalny za pomocą pakietu NHotkey.

Krótko mówiąc, w przypadku XAML wszystko, co musisz zrobić, to wymienić

<KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}" />

przez

<KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}"
            HotkeyManager.RegisterGlobalHotkey="True" />

Odpowiedź została również wysłana do: Jak mogę zarejestrować globalny skrót klawiszowy, aby powiedzieć CTRL + SHIFT + (LETTER) przy użyciu WPF i .NET 3.5?

Anthony Nguyen
źródło
-2

Jak skojarzyć polecenie z MenuItem:

<MenuItem Header="My command" Command="{x:Static local:MyWindow.MyCommand}"/>
Jiří Skála
źródło