Сочетания клавиш в WPF


129

Я знаю об использовании _вместо &, но я смотрю на все Ctrlсочетания клавиш типа +.

Ctrl+ Zдля отмены, Ctrl+ Sдля сохранения и т. д.

Есть ли «стандартный» способ их реализации в приложениях WPF? Или это случай, когда нужно свернуть свои собственные и подключить их к какой-либо команде / контролю?

Ответы:


170

Один из способов - добавить сочетания клавиш к самим командам, как InputGestures. Команды реализованы как RoutedCommands.

Это позволяет сочетаниям клавиш работать, даже если они не подключены к каким-либо элементам управления. А поскольку элементы меню понимают жесты клавиатуры, они автоматически отображают вашу комбинацию клавиш в тексте элементов меню, если вы подключите эту команду к своему элементу меню.

  1. Создайте статический атрибут для хранения команды (желательно как свойство в статическом классе, который вы создаете для команд, но для простого примера, просто используя статический атрибут в window.cs):

     public static RoutedCommand MyCommand = new RoutedCommand();
  2. Добавьте горячие клавиши, которые должны вызывать метод:

     MyCommand.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control));
  3. Создайте привязку команды, которая указывает на ваш метод для вызова при выполнении. Поместите их в привязки команд для элемента пользовательского интерфейса, для которого он должен работать (например, окна) и метода:

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

4
Как связать команду с пунктом меню? Конечно, это самая важная информация для включения в этот ответ, но она отсутствует.
Timwi

8
@Timwi: я использовал приведенный выше код таким образом, чтобы добавить сочетание клавиш к существующему событию: RoutedCommand cmndSettings = new RoutedCommand (); cmndSettings.InputGestures.Add (новый KeyGesture (Key.S, ModifierKeys.Control)); CommandBindings.Add (новый CommandBinding (cmndSettings, mnuSettings_Click));
itsho

1
Комментарий itsho сделал эту работу для меня, не смог заставить работать xml-код выше.
gosr 03

1
К сожалению, при таком подходе Executedкод для команды будет находиться в коде программной части (окна или пользовательского элемента управления), а не в модели представления, в отличие от использования обычной команды (пользовательская ICommandреализация).
OR Mapper


98

Я обнаружил, что это именно то, что я искал, связанный с привязкой клавиш в WPF:

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

См. Сообщение в блоге MVVM CommandReference и KeyBinding


Очень красиво и просто!
слияние

1
Не могли бы вы подробнее рассказать, что такое CreateCustomerCommand и как его реализовать?
Vinz

Это все еще ответ только по ссылке, поскольку фрагмент кода, который скопирован и вставлен, описан как «Результатом будет исключение» в связанной публикации блога. : P
Мартин Шнайдер

Работает здесь чудо. Сначала я попытался добавить "_" перед ключом содержимого кнопки, например OP, но это не сработало. Хуже того, он активировался, когда я нажимал клавишу, когда я не был в фокусе на доступном для записи объекте интерфейса ... например, "s" для сохранения вместо ctrl-s.
Джей,

14

Попробуйте этот код ...

Сначала создайте объект RoutedComand

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

9

Это зависит от того, где вы хотите их использовать.

TextBoxBaseпроизводные элементы управления уже реализуют эти ярлыки. Если вы хотите использовать собственные сочетания клавиш, вам следует взглянуть на «Команды» и «Жесты ввода». Вот небольшой учебник от Switch on the Code : WPF Tutorial - Command Bindings and Custom Commands


8
Какое дерьмовое руководство - не объясняет абсолютно самого важного из всех, а именно, как использовать команду, которая не входит в их предопределенный набор из 20 «общих» команд.
Timwi

6

Документирование этого ответа для других, так как есть гораздо более простой способ сделать это, на который редко ссылаются и не требует вообще касаться XAML.

Чтобы связать сочетание клавиш, в конструкторе Window просто добавьте новый KeyBinding в коллекцию InputBindings. В качестве команды передайте произвольный класс команд, реализующий ICommand. Для метода execute просто реализуйте любую нужную логику. В моем примере ниже мой класс WindowCommand принимает делегат, который будет выполняться при каждом вызове. Когда я создаю новую WindowCommand для передачи с моей привязкой, я просто указываю в моем инициализаторе метод, который я хочу, чтобы WindowCommand выполнялась.

Вы можете использовать этот шаблон для создания собственных быстрых сочетаний клавиш.

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

Создайте простой класс WindowCommand, который принимает делегат выполнения для запуска любого установленного для него метода.

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

У меня была аналогичная проблема, и я нашел ответ @aliwa самым полезным и элегантным решением; однако мне нужна была особая комбинация клавиш Ctrl+ 1. К сожалению, я получил следующую ошибку:

«1» не может использоваться в качестве значения для «Ключ». Числа не являются допустимыми значениями перечисления.

Путем дальнейшего поиска я изменил ответ @aliwa на следующее:

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

Я обнаружил, что это отлично подходит для любой комбинации, которая мне нужна.


У меня это сработало<UserControl.InputBindings> <KeyBinding Gesture="Enter" Command="{Binding someCommand}"/> </UserControl.InputBindings>
fs_tigre

3

VB.NET:

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

XAML не требуется.


1

Хотя основные ответы верны, мне лично нравится работать с прикрепленными свойствами, чтобы решение можно было применить к любому UIElement, особенно когдаWindow элемент не знает, какой элемент должен быть сфокусирован. По своему опыту я часто вижу композицию из нескольких моделей представления и пользовательских элементов управления, где окно часто является не чем иным, как корневым контейнером.

отрывок

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

С помощью этого присоединенного свойства вы можете определить ярлык фокуса для любого UIElement. Он автоматически зарегистрирует входную привязку в окне, содержащем элемент.

Использование (XAML)

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

Исходный код

Полный пример, включая реализацию FocusElementCommand, доступен как суть: https://gist.github.com/shuebner20/c6a5191be23da549d5004ee56bcc352d

Отказ от ответственности: вы можете использовать этот код везде и бесплатно. Имейте в виду, что это образец, который не подходит для интенсивного использования. Например, отсутствует сборка мусора для удаленных элементов, потому что команда будет содержать сильную ссылку на элемент.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.