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

129

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

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

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

Benjol
источник

Ответы:

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
2
Подобный пример wpf-tutorial.com/commands/implementing-custom-commands
Нароттам
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

Anvaka
источник
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>

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

Nik
источник
У меня это сработало<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 не требуется.

plaasmeisie
источник
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

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

Себастьян Хюбнер
источник
-2

Как связать команду с MenuItem:

<MenuItem Header="My command" Command="{x:Static local:MyWindow.MyCommand}"/>
Иржи Скала
источник