Pintasan keyboard di WPF

129

Saya tahu tentang menggunakan _bukan &, tapi saya melihat semua Ctrlcara pintas tipe +.

Ctrl+ Zuntuk membatalkan, Ctrl+ Suntuk menyimpan, dll.

Apakah ada cara 'standar' untuk mengimplementasikan ini dalam aplikasi WPF? Atau itu adalah kasus roll Anda sendiri dan kawat mereka ke perintah / kontrol apa pun?

Benjol
sumber

Jawaban:

170

Salah satu caranya adalah dengan menambahkan tombol pintas Anda ke perintah itu sendiri sebagai InputGestures. Perintah diimplementasikan sebagai RoutedCommands.

Ini memungkinkan tombol jalan pintas bekerja meskipun tidak terhubung ke kontrol apa pun. Dan karena item menu memahami gerakan keyboard, mereka akan secara otomatis menampilkan tombol pintas Anda dalam teks item menu, jika Anda menghubungkan perintah itu ke item menu Anda.

  1. Buat atribut statis untuk memegang perintah (lebih disukai sebagai properti di kelas statis yang Anda buat untuk perintah - tetapi untuk contoh sederhana, cukup gunakan atribut statis di window.cs):

     public static RoutedCommand MyCommand = new RoutedCommand();
  2. Tambahkan kunci pintas yang harus memanggil metode:

     MyCommand.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control));
  3. Buat perintah yang mengikat yang menunjuk ke metode Anda untuk memanggil eksekusi. Letakkan ini di binding perintah untuk elemen UI di mana ia harus bekerja untuk (misalnya, jendela) dan metode:

     <Window.CommandBindings>
         <CommandBinding Command="{x:Static local:MyWindow.MyCommand}" Executed="MyCommandExecuted"/>
     </Window.CommandBindings>
    
     private void MyCommandExecuted(object sender, ExecutedRoutedEventArgs e) { ... }
Abby Fichtner
sumber
4
Bagaimana cara mengaitkan perintah dengan item menu? Tentunya itu akan menjadi informasi paling penting untuk dimasukkan dalam jawaban ini, tetapi itu hilang.
Timwi
8
@Timwi: Saya telah menggunakan kode di atas dengan cara ini untuk menambahkan pintasan keyboard ke acara yang ada: RoutedCommand cmndSettings = new RoutedCommand (); cmndSettings.InputGestures.Add (KeyGesture baru (Key.S, ModifierKeys.Control)); CommandBindings.Add (CommandBinding baru (cmndSettings, mnuSettings_Click));
itsho
1
komentar itsho membuat ini berfungsi untuk saya, tidak dapat membuat kode xml di atas berfungsi.
gosr
1
Sayangnya, dengan pendekatan ini, Executedkode untuk perintah akan berakhir di kode-belakang (jendela atau kontrol-pengguna) daripada model tampilan, yang bertentangan dengan menggunakan perintah biasa ( ICommandimplementasi kustom ).
ATAU Mapper
98

Saya menemukan ini persis apa yang saya cari terkait dengan pengikatan kunci di WPF:

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

Lihat posting blog MVVM CommandReference dan KeyBinding

oliwa
sumber
Sangat bagus dan mudah!
merger
1
Maukah Anda menguraikan apa "CreateCustomerCommand" itu dan bagaimana penerapannya?
Vinz
Ini masih merupakan satu-satunya jawaban tautan karena cuplikan & kode yang ditempelkan dijelaskan dengan "Hasilnya akan menjadi Pengecualian" pada posting blog yang ditautkan. : P
Martin Schneider
Bekerja dengan kagum di sini. Saya pertama kali mencoba menambahkan "_" sebelum kunci konten tombol, seperti OP, tetapi tidak berhasil. Terburuk, itu diaktifkan ketika saya menekan tombol itu sendiri ketika saya tidak fokus ke objek antarmuka yang bisa ditulis .. seperti "s" untuk save, bukan ctrl-s.
Jay
14

Coba kode ini ...

Pertama buat objek RoutedComand

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

Itu tergantung di mana Anda ingin menggunakannya.

TextBoxBaseKontrol yang diterima telah menerapkan pintasan tersebut. Jika Anda ingin menggunakan pintasan keyboard khusus, Anda harus melihat pada gerakan Perintah dan Input. Ini adalah tutorial kecil dari Switch on the Code : WPF Tutorial - Command Bindings dan Custom Commands

Anvaka
sumber
8
Apa itu tutorial omong kosong - tidak menjelaskan hal yang paling penting dari semuanya, yaitu bagaimana menggunakan perintah yang tidak menjadi salah satu dari 20 perintah "umum" yang telah ditentukan sebelumnya.
Timwi
6

Mendokumentasikan jawaban ini untuk orang lain, karena ada cara yang lebih sederhana untuk melakukan ini yang jarang dirujuk, dan tidak perlu menyentuh XAML sama sekali.

Untuk menautkan pintasan keyboard, di konstruktor Window cukup tambahkan KeyBinding baru ke koleksi InputBindings. Sebagai perintah, berikan kelas perintah sewenang-wenang Anda yang mengimplementasikan ICommand. Untuk metode eksekusi, cukup terapkan logika apa pun yang Anda butuhkan. Dalam contoh saya di bawah ini, kelas WindowCommand saya mengambil delegasi yang akan dieksekusi setiap kali dipanggil. Ketika saya membangun WindowCommand baru untuk lulus dengan pengikatan saya, saya hanya menunjukkan di inisialisasi saya, metode yang saya ingin menjalankan WindowCommand.

Anda dapat menggunakan pola ini untuk menghasilkan pintasan keyboard cepat Anda sendiri.

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

Buat kelas WindowCommand sederhana yang membutuhkan delegasi eksekusi untuk mematikan metode apa pun yang ditetapkan di atasnya.

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 saya
sumber
5

Saya memiliki masalah yang sama dan menemukan jawaban aliwa @ menjadi solusi yang paling bermanfaat dan paling elegan; Namun, saya memerlukan kombinasi tombol tertentu, Ctrl+ 1. Sayangnya saya mendapat kesalahan berikut:

'1' tidak dapat digunakan sebagai nilai untuk 'Kunci'. Angka bukan nilai enumerasi yang valid.

Dengan sedikit pencarian lebih lanjut, saya mengubah jawaban aliwa @ sebagai berikut:

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

Saya menemukan ini bekerja sangat baik untuk kombinasi yang saya butuhkan.

Nik
sumber
Ini bekerja untuk saya<UserControl.InputBindings> <KeyBinding Gesture="Enter" Command="{Binding someCommand}"/> </UserControl.InputBindings>
fs_tigre
3

VB.NET:

Public Shared SaveCommand_AltS As New RoutedCommand

Di dalam acara yang dimuat :

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

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

Tidak diperlukan XAML.

plaasmeisie
sumber
1

Meskipun jawaban teratas sudah benar, saya pribadi suka bekerja dengan properti terlampir untuk memungkinkan solusi diterapkan ke apa pun UIElement, terutama ketika Windowtidak mengetahui elemen yang harus difokuskan. Dalam pengalaman saya, saya sering melihat komposisi beberapa model tampilan dan kontrol pengguna, di mana jendela sering tidak lebih dari wadah root.

Potongan

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

Dengan properti terlampir ini, Anda dapat menentukan pintasan fokus untuk UIElement apa pun. Ini akan secara otomatis mendaftarkan pengikatan input di jendela yang mengandung elemen.

Penggunaan (XAML)

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

Kode sumber

Sampel lengkap termasuk implementasi FocusElementCommand tersedia sebagai inti: https://gist.github.com/shuebner20/c6a5191be23da549d5004ee56bcc352d

Penafian: Anda dapat menggunakan kode ini di mana saja dan gratis. Harap diingat, bahwa ini adalah sampel yang tidak cocok untuk penggunaan berat. Misalnya, tidak ada pengumpulan sampah dari elemen yang dihapus karena Perintah akan memegang referensi kuat ke elemen.

Sebastian Hübner
sumber
-2

Cara mengaitkan perintah dengan MenuItem:

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