แป้นพิมพ์ลัดใน 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. สร้างการผูกคำสั่งที่ชี้ไปยังเมธอดของคุณเพื่อเรียกใช้งาน ใส่สิ่งเหล่านี้ในการผูกคำสั่งสำหรับองค์ประกอบ UI ที่ควรใช้ (เช่นหน้าต่าง) และวิธีการ:

     <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
ความคิดเห็นของ itho ทำให้สิ่งนี้ใช้งานได้สำหรับฉันไม่สามารถทำให้โค้ด xml ด้านบนใช้งานได้
gosr

1
น่าเสียดายที่ด้วยวิธีนี้Executedรหัสสำหรับคำสั่งจะสิ้นสุดในโค้ดหลัง (ของหน้าต่างหรือการควบคุมผู้ใช้) แทนที่จะเป็นมุมมองโมเดลซึ่งต่างจากการใช้คำสั่งปกติ ( ICommandการใช้งานแบบกำหนดเอง)
หรือผู้ทำแผนที่

2
ตัวอย่างที่คล้ายกันwpf-tutorial.com/commands/implementing-custom-commands
Narottam Goyal

98

ฉันพบว่านี่เป็นสิ่งที่ฉันกำลังมองหาที่เกี่ยวข้องกับการผูกคีย์ใน WPF:

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

ดูบล็อกโพสต์MVVM CommandReference และ KeyBinding


ดีและง่ายมาก!
ควบรวมกิจการ

1
คุณช่วยอธิบายได้ไหมว่า "CreateCustomerCommand" คืออะไรและจะนำไปใช้อย่างไร
Vinz

นี่ยังคงเป็นเพียงคำตอบเท่านั้นเนื่องจากมีการอธิบายข้อมูลโค้ดที่คัดลอกและวางไว้ด้วย "ผลลัพธ์จะเป็นข้อยกเว้น" ในบล็อกโพสต์ที่ลิงก์ : P
Martin Schneider

ทำงานได้อย่างน่าอัศจรรย์ที่นี่ ฉันลองเพิ่ม "_" ก่อนคีย์ของเนื้อหาปุ่มเช่น OP แต่ไม่ได้ผล ที่แย่ที่สุดก็คือมันเปิดใช้งานเมื่อฉันกดปุ่มเองเมื่อฉันไม่ได้โฟกัสไปที่วัตถุที่เขียนได้ของอินเทอร์เฟซเช่น "s" สำหรับบันทึกแทนที่จะเป็น ctrl-s
Jay

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 และ Custom Commands


8
บทช่วยสอนเรื่องอึไม่ได้อธิบายถึงสิ่งที่สำคัญที่สุดของทั้งหมดนั่นคือวิธีใช้คำสั่งที่ไม่ได้เป็นหนึ่งในชุดคำสั่ง "ทั่วไป" 20 ชุดที่กำหนดไว้ล่วงหน้า
Timwi

6

บันทึกคำตอบนี้สำหรับผู้อื่นเนื่องจากมีวิธีที่ง่ายกว่ามากในการดำเนินการซึ่งแทบไม่มีการอ้างอิงและไม่จำเป็นต้องแตะ XAML เลย

หากต้องการเชื่อมโยงแป้นพิมพ์ลัดในตัวสร้างหน้าต่างเพียงแค่เพิ่ม KeyBinding ใหม่ในคอลเล็กชัน InputBindings ในฐานะคำสั่งให้ส่งผ่านคลาสคำสั่งของคุณที่ใช้ ICommand สำหรับวิธีการดำเนินการเพียงใช้ตรรกะที่คุณต้องการ ในตัวอย่างของฉันด้านล่างคลาส 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' เป็นค่าสำหรับ 'Key' ตัวเลขไม่ใช่ค่าการแจงนับที่ถูกต้อง

ด้วยการค้นหาเพิ่มเติมเล็กน้อยฉันได้แก้ไขคำตอบของ @ 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

ข้อจำกัดความรับผิดชอบ: คุณสามารถใช้รหัสนี้ได้ทุกที่และไม่มีค่าใช้จ่าย โปรดทราบว่านี่เป็นตัวอย่างที่ไม่เหมาะสำหรับการใช้งานหนัก ตัวอย่างเช่นไม่มีการรวบรวมขยะขององค์ประกอบที่ถูกลบเนื่องจากคำสั่งจะมีการอ้างอิงถึงองค์ประกอบที่ชัดเจน


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.